@oxidezap/baileyrs 0.0.7 → 0.0.8
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/README.md +119 -5
- package/lib/Bridge/__tests__/adapt.test.d.ts +2 -0
- package/lib/Bridge/__tests__/adapt.test.d.ts.map +1 -0
- package/lib/Bridge/__tests__/adapt.test.js +355 -0
- package/lib/Bridge/__tests__/adapt.test.js.map +1 -0
- package/lib/Bridge/__tests__/dts-drift.test.d.ts +13 -0
- package/lib/Bridge/__tests__/dts-drift.test.d.ts.map +1 -0
- package/lib/Bridge/__tests__/dts-drift.test.js +92 -0
- package/lib/Bridge/__tests__/dts-drift.test.js.map +1 -0
- package/lib/Bridge/__tests__/fixtures.d.ts +192 -0
- package/lib/Bridge/__tests__/fixtures.d.ts.map +1 -0
- package/lib/Bridge/__tests__/fixtures.js +99 -0
- package/lib/Bridge/__tests__/fixtures.js.map +1 -0
- package/lib/Bridge/adapt.d.ts +28 -0
- package/lib/Bridge/adapt.d.ts.map +1 -0
- package/lib/Bridge/adapt.js +504 -0
- package/lib/Bridge/adapt.js.map +1 -0
- package/lib/Bridge/index.d.ts +12 -0
- package/lib/Bridge/index.d.ts.map +1 -0
- package/lib/Bridge/index.js +12 -0
- package/lib/Bridge/index.js.map +1 -0
- package/lib/Bridge/primitives.d.ts +56 -0
- package/lib/Bridge/primitives.d.ts.map +1 -0
- package/lib/Bridge/primitives.js +58 -0
- package/lib/Bridge/primitives.js.map +1 -0
- package/lib/Bridge/types.d.ts +273 -0
- package/lib/Bridge/types.d.ts.map +1 -0
- package/lib/Bridge/types.js +17 -0
- package/lib/Bridge/types.js.map +1 -0
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +2 -2
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Socket/blocking.d.ts.map +1 -1
- package/lib/Socket/chat-actions.d.ts.map +1 -1
- package/lib/Socket/contacts.d.ts +9 -4
- package/lib/Socket/contacts.d.ts.map +1 -1
- package/lib/Socket/contacts.js +11 -8
- package/lib/Socket/contacts.js.map +1 -1
- package/lib/Socket/events.d.ts +7 -1
- package/lib/Socket/events.d.ts.map +1 -1
- package/lib/Socket/events.js +212 -243
- package/lib/Socket/events.js.map +1 -1
- package/lib/Socket/group-notifications.d.ts +25 -0
- package/lib/Socket/group-notifications.d.ts.map +1 -0
- package/lib/Socket/group-notifications.js +162 -0
- package/lib/Socket/group-notifications.js.map +1 -0
- package/lib/Socket/groups.d.ts +12 -1
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +44 -1
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +47 -13
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/index.js +158 -24
- package/lib/Socket/index.js.map +1 -1
- package/lib/Socket/messages.d.ts +5 -2
- package/lib/Socket/messages.d.ts.map +1 -1
- package/lib/Socket/messages.js +42 -21
- package/lib/Socket/messages.js.map +1 -1
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/presence.d.ts.map +1 -1
- package/lib/Socket/profile.d.ts +7 -1
- package/lib/Socket/profile.d.ts.map +1 -1
- package/lib/Socket/profile.js +20 -5
- package/lib/Socket/profile.js.map +1 -1
- package/lib/Socket/transport.d.ts.map +1 -1
- package/lib/Socket/transport.js +14 -13
- package/lib/Socket/transport.js.map +1 -1
- package/lib/Socket/types.d.ts.map +1 -1
- package/lib/Types/Auth.d.ts +1 -1
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Bussines.d.ts +1 -1
- package/lib/Types/Bussines.d.ts.map +1 -1
- package/lib/Types/Chat.d.ts +1 -1
- package/lib/Types/Chat.d.ts.map +1 -1
- package/lib/Types/Events.d.ts +12 -3
- package/lib/Types/Events.d.ts.map +1 -1
- package/lib/Types/Events.js +1 -1
- package/lib/Types/Events.js.map +1 -1
- package/lib/Types/GroupMetadata.d.ts.map +1 -1
- package/lib/Types/Message.d.ts +16 -1
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js +1 -1
- package/lib/Types/Message.js.map +1 -1
- package/lib/Types/Socket.d.ts +42 -1
- package/lib/Types/Socket.d.ts.map +1 -1
- package/lib/Types/State.d.ts +1 -1
- package/lib/Types/State.d.ts.map +1 -1
- package/lib/Types/index.d.ts +1 -1
- package/lib/Types/index.d.ts.map +1 -1
- package/lib/Types/index.js +17 -12
- package/lib/Types/index.js.map +1 -1
- package/lib/Utils/__tests__/wrap-legacy-store-lid-mapping.test.d.ts +23 -0
- package/lib/Utils/__tests__/wrap-legacy-store-lid-mapping.test.d.ts.map +1 -0
- package/lib/Utils/__tests__/wrap-legacy-store-lid-mapping.test.js +157 -0
- package/lib/Utils/__tests__/wrap-legacy-store-lid-mapping.test.js.map +1 -0
- package/lib/Utils/__tests__/wrap-legacy-store-sender-key.test.d.ts +31 -0
- package/lib/Utils/__tests__/wrap-legacy-store-sender-key.test.d.ts.map +1 -0
- package/lib/Utils/__tests__/wrap-legacy-store-sender-key.test.js +253 -0
- package/lib/Utils/__tests__/wrap-legacy-store-sender-key.test.js.map +1 -0
- package/lib/Utils/__tests__/wrap-legacy-store-session.test.d.ts +32 -0
- package/lib/Utils/__tests__/wrap-legacy-store-session.test.d.ts.map +1 -0
- package/lib/Utils/__tests__/wrap-legacy-store-session.test.js +488 -0
- package/lib/Utils/__tests__/wrap-legacy-store-session.test.js.map +1 -0
- package/lib/Utils/boom.d.ts +33 -0
- package/lib/Utils/boom.d.ts.map +1 -0
- package/lib/Utils/boom.js +63 -0
- package/lib/Utils/boom.js.map +1 -0
- package/lib/Utils/browser-utils.d.ts.map +1 -1
- package/lib/Utils/event-buffer.d.ts.map +1 -1
- package/lib/Utils/generics.d.ts +37 -1
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +60 -2
- package/lib/Utils/generics.js.map +1 -1
- package/lib/Utils/index.d.ts +1 -0
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +10 -9
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/lid-mapping-store.d.ts +81 -0
- package/lib/Utils/lid-mapping-store.d.ts.map +1 -0
- package/lib/Utils/lid-mapping-store.js +126 -0
- package/lib/Utils/lid-mapping-store.js.map +1 -0
- package/lib/Utils/logger.d.ts.map +1 -1
- package/lib/Utils/logger.js +25 -1
- package/lib/Utils/logger.js.map +1 -1
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +1 -1
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts +30 -1
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +115 -41
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/native-crypto-provider.d.ts +18 -0
- package/lib/Utils/native-crypto-provider.d.ts.map +1 -0
- package/lib/Utils/native-crypto-provider.js +67 -0
- package/lib/Utils/native-crypto-provider.js.map +1 -0
- package/lib/Utils/use-bridge-store.d.ts.map +1 -1
- package/lib/Utils/use-memory-store.d.ts.map +1 -1
- package/lib/Utils/use-memory-store.js +1 -1
- package/lib/Utils/use-memory-store.js.map +1 -1
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -1
- package/lib/Utils/use-multi-file-auth-state.js +1 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
- package/lib/Utils/wrap-legacy-store.d.ts.map +1 -1
- package/lib/Utils/wrap-legacy-store.js +621 -13
- package/lib/Utils/wrap-legacy-store.js.map +1 -1
- package/lib/WABinary/index.d.ts.map +1 -1
- package/lib/WABinary/index.js +1 -1
- package/lib/WABinary/index.js.map +1 -1
- package/lib/__tests__/expect.d.ts +30 -0
- package/lib/__tests__/expect.d.ts.map +1 -0
- package/lib/__tests__/expect.js +60 -0
- package/lib/__tests__/expect.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +9 -6
- package/lib/index.js.map +1 -1
- package/package.json +30 -25
- package/WAProto/index.d.ts +0 -13875
- package/WAProto/index.js +0 -175
package/README.md
CHANGED
|
@@ -25,20 +25,45 @@ so existing integrations can migrate with minimal changes. See
|
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
28
|
+
### New project
|
|
29
|
+
|
|
28
30
|
```sh
|
|
29
31
|
npm install @oxidezap/baileyrs
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
Then import:
|
|
33
34
|
```ts
|
|
34
35
|
import makeWASocket from '@oxidezap/baileyrs'
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
### Drop-in replacement for upstream Baileys
|
|
39
|
+
|
|
40
|
+
baileyrs is API-compatible with [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys).
|
|
41
|
+
Existing projects switch over by aliasing the package — **no source changes needed**:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npm install @whiskeysockets/baileys@npm:@oxidezap/baileyrs
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
That writes the alias to your `package.json`:
|
|
48
|
+
|
|
49
|
+
```jsonc
|
|
50
|
+
{
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@whiskeysockets/baileys": "npm:@oxidezap/baileyrs@^0.0.8"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Every `import { makeWASocket } from '@whiskeysockets/baileys'` in your codebase
|
|
58
|
+
now resolves to baileyrs. Auth state, event listeners, and message APIs work
|
|
59
|
+
unchanged — see [Migrating from Upstream Baileys](#migrating-from-upstream-baileys)
|
|
60
|
+
for what happens behind the scenes and the (very small) surface where behavior
|
|
61
|
+
differs.
|
|
62
|
+
|
|
37
63
|
## Quick Start
|
|
38
64
|
|
|
39
65
|
```ts
|
|
40
|
-
import makeWASocket, { DisconnectReason, useMultiFileAuthState } from '@oxidezap/baileyrs'
|
|
41
|
-
import { Boom } from '@hapi/boom'
|
|
66
|
+
import makeWASocket, { Boom, DisconnectReason, useMultiFileAuthState } from '@oxidezap/baileyrs'
|
|
42
67
|
|
|
43
68
|
const { state } = await useMultiFileAuthState('auth_info')
|
|
44
69
|
const sock = makeWASocket({ auth: state })
|
|
@@ -63,6 +88,31 @@ sock.ev.on('messages.upsert', ({ messages }) => {
|
|
|
63
88
|
})
|
|
64
89
|
```
|
|
65
90
|
|
|
91
|
+
## Error Handling
|
|
92
|
+
|
|
93
|
+
baileyrs ships its own `Boom` error class, API-compatible with
|
|
94
|
+
[`@hapi/boom`](https://github.com/hapijs/boom) — no extra dependency needed.
|
|
95
|
+
Methods that fail (including `sendMessage`, media uploads, and connection errors)
|
|
96
|
+
throw a `Boom` with a `statusCode` aligned to WhatsApp disconnect reasons.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { Boom } from '@oxidezap/baileyrs'
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await sock.sendMessage(jid, { text: 'hi' })
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (Boom.isBoom(err)) {
|
|
105
|
+
console.log(err.statusCode) // fast path
|
|
106
|
+
console.log(err.output.statusCode) // @hapi/boom-compatible
|
|
107
|
+
console.log(err.data) // optional user payload
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If you have existing code importing `Boom` from `@hapi/boom`, the
|
|
113
|
+
`(err as Boom)?.output?.statusCode` pattern continues to work via structural
|
|
114
|
+
typing — only the underlying dependency changed.
|
|
115
|
+
|
|
66
116
|
## Connecting
|
|
67
117
|
|
|
68
118
|
### QR Code
|
|
@@ -132,12 +182,76 @@ No creds management, no type routing — just `(store, key) → bytes`. Works wi
|
|
|
132
182
|
|
|
133
183
|
### Migrating from Upstream Baileys
|
|
134
184
|
|
|
135
|
-
|
|
185
|
+
baileyrs accepts the upstream `auth: { creds, keys }` shape directly — the
|
|
186
|
+
internal `wrapLegacyStore` adapter runs automatically when it sees that
|
|
187
|
+
shape, so the **`makeWASocket(...)` call site needs zero changes**:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
// makeWASocket() works as-is once the npm alias is in place.
|
|
191
|
+
// Auto-wrap kicks in when it sees {creds, keys}.
|
|
192
|
+
const sock = makeWASocket({ auth: state })
|
|
193
|
+
|
|
194
|
+
sock.ev.on('creds.update', saveCreds) // still fires — adapter re-emits
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The one switch you do make is **how you load `state`**. baileyrs's
|
|
198
|
+
`useMultiFileAuthState` is a *new-state-only* helper: it provisions a
|
|
199
|
+
binary `.bin` store for the Rust engine and ignores any pre-existing
|
|
200
|
+
upstream JSON. To carry an existing pairing across the migration, swap
|
|
201
|
+
the import to `useLegacyMultiFileAuthState` (one line):
|
|
202
|
+
|
|
203
|
+
```diff
|
|
204
|
+
- import makeWASocket, { useMultiFileAuthState } from '@whiskeysockets/baileys'
|
|
205
|
+
+ import makeWASocket, { useLegacyMultiFileAuthState as useMultiFileAuthState } from '@whiskeysockets/baileys'
|
|
206
|
+
|
|
207
|
+
const { state, saveCreds } = await useMultiFileAuthState('auth_info')
|
|
208
|
+
const sock = makeWASocket({ auth: state })
|
|
209
|
+
sock.ev.on('creds.update', saveCreds)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Same approach works for any custom upstream auth (`useMySQLAuthState`,
|
|
213
|
+
`useRedisAuthState`, mysql-baileys, etc.) — your loader keeps returning
|
|
214
|
+
`{creds, keys, saveCreds}`, baileyrs auto-wraps it on the way in.
|
|
215
|
+
|
|
216
|
+
#### Existing sessions migrate without re-pairing
|
|
217
|
+
|
|
218
|
+
`useLegacyMultiFileAuthState` reads the existing `creds.json` +
|
|
219
|
+
`pre-key-*.json` / `session-*.json` files from the same folder upstream
|
|
220
|
+
Baileys was using and hands them to the Rust engine. Pair-ID, identity,
|
|
221
|
+
signed pre-keys, and Signal sessions are all preserved. No QR re-scan,
|
|
222
|
+
no logged-out events.
|
|
223
|
+
|
|
224
|
+
> **Where the new state goes**: baileyrs continues to write the legacy
|
|
225
|
+
> JSON files (via `saveCreds`) so the folder stays compatible with both
|
|
226
|
+
> sides during a rollback window. The Rust engine treats the JSON as the
|
|
227
|
+
> source of truth as long as you keep using `useLegacyMultiFileAuthState`.
|
|
228
|
+
> If you later want to drop the JSON layer, switch to
|
|
229
|
+
> `useMultiFileAuthState` — but that path requires a fresh QR.
|
|
230
|
+
|
|
231
|
+
#### Gotchas
|
|
232
|
+
|
|
233
|
+
A few behaviors that differ from upstream — almost always to your advantage:
|
|
234
|
+
|
|
235
|
+
- **Auto-reconnect is built in.** Don't call `makeWASocket()` again from
|
|
236
|
+
`connection.update`'s `'close'` branch. The Rust engine retries with
|
|
237
|
+
fibonacci backoff; opening a second socket leaks the first one.
|
|
238
|
+
- **No `getMessage` / `cachedGroupMetadata` polyfill required.** The Rust
|
|
239
|
+
side caches group metadata and message keys natively. You can still pass
|
|
240
|
+
them — they're respected as overrides — but they're optional.
|
|
241
|
+
- **`Boom` ships in the box.** baileyrs exports its own
|
|
242
|
+
`@hapi/boom`-compatible `Boom` (see [Error Handling](#error-handling))
|
|
243
|
+
so the existing `(err as Boom).output.statusCode` pattern works
|
|
244
|
+
unchanged. If your `package.json` was pulling `@hapi/boom` only for
|
|
245
|
+
baileys, you can drop the dependency.
|
|
246
|
+
|
|
247
|
+
#### Manual `wrapLegacyStore` (advanced)
|
|
248
|
+
|
|
249
|
+
If you want explicit control over when the adapter runs (testing, custom
|
|
250
|
+
shapes, mixed storage), call it yourself:
|
|
136
251
|
|
|
137
252
|
```ts
|
|
138
253
|
import makeWASocket, { useLegacyMultiFileAuthState, wrapLegacyStore } from '@oxidezap/baileyrs'
|
|
139
254
|
|
|
140
|
-
// Works with any upstream auth state: useMultiFileAuthState, useMySQLAuthState, etc.
|
|
141
255
|
const { state, saveCreds } = await useLegacyMultiFileAuthState('/path/to/baileys_auth_info')
|
|
142
256
|
const store = await wrapLegacyStore(state, saveCreds)
|
|
143
257
|
const sock = makeWASocket({ auth: { store } })
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapt.test.d.ts","sourceRoot":"","sources":["../../../src/Bridge/__tests__/adapt.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import { adaptBridgeEvent } from "../adapt.js";
|
|
3
|
+
import { expect } from "../../__tests__/expect.js";
|
|
4
|
+
import { groupAnnouncementWireFixture, groupDemotePascalFixture, groupDemoteWireFixture, groupNotAnnouncementWireFixture, groupPromotePascalFixture, groupRevokeWireFixture } from "./fixtures.js";
|
|
5
|
+
describe('adaptBridgeEvent — anti-corruption layer', () => {
|
|
6
|
+
describe('connection lifecycle', () => {
|
|
7
|
+
it('connected → canonical "connected"', () => {
|
|
8
|
+
expect(adaptBridgeEvent({ type: 'connected' })).toEqual({ type: 'connected' });
|
|
9
|
+
});
|
|
10
|
+
it('disconnected → canonical "disconnected"', () => {
|
|
11
|
+
expect(adaptBridgeEvent({ type: 'disconnected' })).toEqual({ type: 'disconnected' });
|
|
12
|
+
});
|
|
13
|
+
it('qr → canonical "qr" with code', () => {
|
|
14
|
+
expect(adaptBridgeEvent({ type: 'qr', data: { code: 'ABC', timeout: 60 } })).toEqual({
|
|
15
|
+
type: 'qr',
|
|
16
|
+
code: 'ABC'
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('pairing_code collapses to canonical "qr"', () => {
|
|
20
|
+
expect(adaptBridgeEvent({ type: 'pairing_code', data: { code: 'XYZ', timeout: 60 } })).toEqual({
|
|
21
|
+
type: 'qr',
|
|
22
|
+
code: 'XYZ'
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
it('pair_success normalizes snake_case to camelCase fields', () => {
|
|
26
|
+
const result = adaptBridgeEvent({
|
|
27
|
+
type: 'pair_success',
|
|
28
|
+
data: { id: '5511999990000@s.whatsapp.net', lid: '100@lid', business_name: 'Acme', platform: 'android' }
|
|
29
|
+
});
|
|
30
|
+
expect(result).toEqual({
|
|
31
|
+
type: 'pairSuccess',
|
|
32
|
+
id: '5511999990000@s.whatsapp.net',
|
|
33
|
+
lid: '100@lid',
|
|
34
|
+
businessName: 'Acme',
|
|
35
|
+
platform: 'android'
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('group_update — the PascalCase regression', () => {
|
|
40
|
+
it('captured Demote payload normalizes to canonical "demote"', () => {
|
|
41
|
+
const result = adaptBridgeEvent(groupDemotePascalFixture);
|
|
42
|
+
expect(result?.type).toBe('groupUpdate');
|
|
43
|
+
if (result?.type !== 'groupUpdate')
|
|
44
|
+
throw new Error('narrowing');
|
|
45
|
+
expect(result.action.type).toBe('demote');
|
|
46
|
+
expect(result.groupJid).toBe('120363040237990503@g.us');
|
|
47
|
+
expect(result.author).toBe('236395184570386@lid');
|
|
48
|
+
expect(result.authorPn).toBe('559984726662@s.whatsapp.net');
|
|
49
|
+
expect(result.isLidAddressingMode).toBe(true);
|
|
50
|
+
// ISO timestamp coerced to unix seconds
|
|
51
|
+
expect(result.timestamp).toBe(Math.floor(Date.parse('2026-04-18T05:45:46Z') / 1000));
|
|
52
|
+
if (result.action.type !== 'demote')
|
|
53
|
+
throw new Error('narrowing');
|
|
54
|
+
expect(result.action.participants).toEqual([
|
|
55
|
+
{ jid: '65546133500078@lid', phoneNumber: '559984246891@s.whatsapp.net' }
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
it('captured Promote payload normalizes to canonical "promote"', () => {
|
|
59
|
+
const result = adaptBridgeEvent(groupPromotePascalFixture);
|
|
60
|
+
if (result?.type !== 'groupUpdate' || result.action.type !== 'promote') {
|
|
61
|
+
throw new Error('expected canonical promote');
|
|
62
|
+
}
|
|
63
|
+
expect(result.action.participants[0].jid).toBe('65546133500078@lid');
|
|
64
|
+
});
|
|
65
|
+
it('snake_case action.type also passes through (forward-compat with rust fix)', () => {
|
|
66
|
+
const fixture = {
|
|
67
|
+
...groupDemotePascalFixture,
|
|
68
|
+
data: {
|
|
69
|
+
...groupDemotePascalFixture.data,
|
|
70
|
+
action: { ...groupDemotePascalFixture.data.action, type: 'demote' }
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const result = adaptBridgeEvent(fixture);
|
|
74
|
+
expect(result?.type === 'groupUpdate' && result.action.type).toBe('demote');
|
|
75
|
+
});
|
|
76
|
+
it('unknown action.type falls back to {type: "unknown", rawType}', () => {
|
|
77
|
+
const fixture = {
|
|
78
|
+
...groupDemotePascalFixture,
|
|
79
|
+
data: { ...groupDemotePascalFixture.data, action: { type: 'BrandNewAction', participants: [] } }
|
|
80
|
+
};
|
|
81
|
+
const result = adaptBridgeEvent(fixture);
|
|
82
|
+
if (result?.type !== 'groupUpdate' || result.action.type !== 'unknown')
|
|
83
|
+
throw new Error('narrowing');
|
|
84
|
+
expect(result.action.rawType).toBe('BrandNewAction');
|
|
85
|
+
});
|
|
86
|
+
it('subject action carries through with new subject', () => {
|
|
87
|
+
const fixture = {
|
|
88
|
+
type: 'group_update',
|
|
89
|
+
data: {
|
|
90
|
+
group_jid: { user: '123', server: 'g.us' },
|
|
91
|
+
participant: { user: '999', server: 'lid' },
|
|
92
|
+
timestamp: 1734000000,
|
|
93
|
+
is_lid_addressing_mode: false,
|
|
94
|
+
action: {
|
|
95
|
+
type: 'Subject',
|
|
96
|
+
subject: 'New name',
|
|
97
|
+
subject_owner: { user: '999', server: 'lid' },
|
|
98
|
+
subject_time: 1734000000
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const result = adaptBridgeEvent(fixture);
|
|
103
|
+
if (result?.type !== 'groupUpdate' || result.action.type !== 'subject')
|
|
104
|
+
throw new Error('narrowing');
|
|
105
|
+
expect(result.action.subject).toBe('New name');
|
|
106
|
+
expect(result.action.subjectOwner).toBe('999@lid');
|
|
107
|
+
expect(result.action.subjectTime).toBe(1734000000);
|
|
108
|
+
});
|
|
109
|
+
it('Locked / Unlocked / Announce / NotAnnounce normalize to camelCase', () => {
|
|
110
|
+
const cases = [
|
|
111
|
+
['Locked', 'locked'],
|
|
112
|
+
['Unlocked', 'unlocked'],
|
|
113
|
+
['Announce', 'announce'],
|
|
114
|
+
['NotAnnounce', 'notAnnounce']
|
|
115
|
+
];
|
|
116
|
+
for (const [raw, canonical] of cases) {
|
|
117
|
+
const fixture = {
|
|
118
|
+
type: 'group_update',
|
|
119
|
+
data: {
|
|
120
|
+
group_jid: { user: '123', server: 'g.us' },
|
|
121
|
+
timestamp: 0,
|
|
122
|
+
is_lid_addressing_mode: false,
|
|
123
|
+
action: { type: raw }
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const result = adaptBridgeEvent(fixture);
|
|
127
|
+
if (result?.type !== 'groupUpdate')
|
|
128
|
+
throw new Error(`expected groupUpdate, got ${result?.type}`);
|
|
129
|
+
expect(result.action.type).toBe(canonical);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it('MembershipApprovalMode preserves enabled flag', () => {
|
|
133
|
+
const fixture = {
|
|
134
|
+
type: 'group_update',
|
|
135
|
+
data: {
|
|
136
|
+
group_jid: { user: '123', server: 'g.us' },
|
|
137
|
+
timestamp: 0,
|
|
138
|
+
is_lid_addressing_mode: false,
|
|
139
|
+
action: { type: 'MembershipApprovalMode', enabled: true }
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const result = adaptBridgeEvent(fixture);
|
|
143
|
+
if (result?.type !== 'groupUpdate' || result.action.type !== 'membershipApprovalMode')
|
|
144
|
+
throw new Error('narrowing');
|
|
145
|
+
expect(result.action.enabled).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
it('rejects malformed payloads (missing group_jid)', () => {
|
|
148
|
+
expect(adaptBridgeEvent({ type: 'group_update', data: { action: { type: 'demote' } } })).toBe(null);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
// Wire-tag fixtures — what the bridge emits today (post whatsapp-rust PR
|
|
152
|
+
// #567). These are the canonical reality the adapter must agree with.
|
|
153
|
+
describe('group_update — wire-tag canonical (post-WireEnum)', () => {
|
|
154
|
+
it('lowercase "demote" payload normalizes to canonical "demote"', () => {
|
|
155
|
+
const result = adaptBridgeEvent(groupDemoteWireFixture);
|
|
156
|
+
if (result?.type !== 'groupUpdate' || result.action.type !== 'demote')
|
|
157
|
+
throw new Error('narrowing');
|
|
158
|
+
expect(result.action.participants[0].jid).toBe('65546133500078@lid');
|
|
159
|
+
expect(result.timestamp).toBe(1776490677);
|
|
160
|
+
});
|
|
161
|
+
it('XML wire tag "announcement" maps to canonical "announce"', () => {
|
|
162
|
+
const result = adaptBridgeEvent(groupAnnouncementWireFixture);
|
|
163
|
+
if (result?.type !== 'groupUpdate')
|
|
164
|
+
throw new Error('narrowing');
|
|
165
|
+
expect(result.action.type).toBe('announce');
|
|
166
|
+
});
|
|
167
|
+
it('XML wire tag "not_announcement" maps to canonical "notAnnounce"', () => {
|
|
168
|
+
const result = adaptBridgeEvent(groupNotAnnouncementWireFixture);
|
|
169
|
+
if (result?.type !== 'groupUpdate')
|
|
170
|
+
throw new Error('narrowing');
|
|
171
|
+
expect(result.action.type).toBe('notAnnounce');
|
|
172
|
+
});
|
|
173
|
+
it('XML wire tag "revoke" (was "revoke_invite" pre-#567) maps to canonical "revokeInvite"', () => {
|
|
174
|
+
const result = adaptBridgeEvent(groupRevokeWireFixture);
|
|
175
|
+
if (result?.type !== 'groupUpdate')
|
|
176
|
+
throw new Error('narrowing');
|
|
177
|
+
expect(result.action.type).toBe('revokeInvite');
|
|
178
|
+
});
|
|
179
|
+
it('new variants from PR #567 surface as `unknown` with the wire-tag preserved', () => {
|
|
180
|
+
for (const tag of ['membership_approval_request', 'created_membership_requests', 'revoked_membership_requests']) {
|
|
181
|
+
const result = adaptBridgeEvent({
|
|
182
|
+
type: 'group_update',
|
|
183
|
+
data: {
|
|
184
|
+
group_jid: { user: '120', server: 'g.us' },
|
|
185
|
+
timestamp: 0,
|
|
186
|
+
is_lid_addressing_mode: false,
|
|
187
|
+
action: { type: tag }
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
if (result?.type !== 'groupUpdate' || result.action.type !== 'unknown')
|
|
191
|
+
throw new Error('narrowing');
|
|
192
|
+
expect(result.action.rawType).toBe(tag);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('messages & receipts', () => {
|
|
197
|
+
it('coerces ISO MessageInfo.timestamp to unix seconds', () => {
|
|
198
|
+
const result = adaptBridgeEvent({
|
|
199
|
+
type: 'message',
|
|
200
|
+
data: {
|
|
201
|
+
message: { conversation: 'hi' },
|
|
202
|
+
info: {
|
|
203
|
+
id: 'MSG1',
|
|
204
|
+
timestamp: '2026-04-18T05:00:00Z',
|
|
205
|
+
push_name: 'alice',
|
|
206
|
+
source: {
|
|
207
|
+
chat: { user: '5511', server: 's.whatsapp.net' },
|
|
208
|
+
sender: { user: '5511', server: 's.whatsapp.net' },
|
|
209
|
+
is_from_me: false,
|
|
210
|
+
is_group: false
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
if (result?.type !== 'message')
|
|
216
|
+
throw new Error('narrowing');
|
|
217
|
+
expect(result.timestamp).toBe(Math.floor(Date.parse('2026-04-18T05:00:00Z') / 1000));
|
|
218
|
+
expect(result.id).toBe('MSG1');
|
|
219
|
+
expect(result.chatJid).toBe('5511@s.whatsapp.net');
|
|
220
|
+
expect(result.isGroup).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
it('group message extracts senderJid + participantAlt', () => {
|
|
223
|
+
const result = adaptBridgeEvent({
|
|
224
|
+
type: 'message',
|
|
225
|
+
data: {
|
|
226
|
+
message: { conversation: 'hi' },
|
|
227
|
+
info: {
|
|
228
|
+
id: 'MSG2',
|
|
229
|
+
timestamp: 1734000000,
|
|
230
|
+
source: {
|
|
231
|
+
chat: { user: '120', server: 'g.us' },
|
|
232
|
+
sender: { user: '5511', server: 's.whatsapp.net' },
|
|
233
|
+
sender_alt: { user: '999', server: 'lid' },
|
|
234
|
+
is_from_me: false,
|
|
235
|
+
is_group: true
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
if (result?.type !== 'message')
|
|
241
|
+
throw new Error('narrowing');
|
|
242
|
+
expect(result.senderJid).toBe('5511@s.whatsapp.net');
|
|
243
|
+
expect(result.participantAlt).toBe('999@lid');
|
|
244
|
+
});
|
|
245
|
+
it('receipt collects message_ids and timestamp', () => {
|
|
246
|
+
const result = adaptBridgeEvent({
|
|
247
|
+
type: 'receipt',
|
|
248
|
+
data: {
|
|
249
|
+
message_ids: ['M1', 'M2'],
|
|
250
|
+
timestamp: '2026-04-18T05:00:00Z',
|
|
251
|
+
source: {
|
|
252
|
+
chat: { user: '120', server: 'g.us' },
|
|
253
|
+
sender: { user: '5511', server: 's.whatsapp.net' },
|
|
254
|
+
is_from_me: false,
|
|
255
|
+
is_group: true
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
if (result?.type !== 'receipt')
|
|
260
|
+
throw new Error('narrowing');
|
|
261
|
+
expect(result.messageIds).toEqual(['M1', 'M2']);
|
|
262
|
+
expect(result.chatJid).toBe('120@g.us');
|
|
263
|
+
expect(result.senderJid).toBe('5511@s.whatsapp.net');
|
|
264
|
+
expect(result.isGroup).toBe(true);
|
|
265
|
+
expect(result.timestamp).toBe(Math.floor(Date.parse('2026-04-18T05:00:00Z') / 1000));
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
describe('chat state', () => {
|
|
269
|
+
it('archive_update maps to canonical archiveUpdate', () => {
|
|
270
|
+
const result = adaptBridgeEvent({
|
|
271
|
+
type: 'archive_update',
|
|
272
|
+
data: { jid: { user: '5511', server: 's.whatsapp.net' } }
|
|
273
|
+
});
|
|
274
|
+
expect(result).toEqual({ type: 'archiveUpdate', jid: '5511@s.whatsapp.net' });
|
|
275
|
+
});
|
|
276
|
+
it('star_update reads action.starred even when wrapped', () => {
|
|
277
|
+
const result = adaptBridgeEvent({
|
|
278
|
+
type: 'star_update',
|
|
279
|
+
data: {
|
|
280
|
+
chat_jid: { user: '5511', server: 's.whatsapp.net' },
|
|
281
|
+
message_id: 'MID1',
|
|
282
|
+
from_me: true,
|
|
283
|
+
action: { starred: true }
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
expect(result).toEqual({
|
|
287
|
+
type: 'starUpdate',
|
|
288
|
+
chatJid: '5511@s.whatsapp.net',
|
|
289
|
+
messageId: 'MID1',
|
|
290
|
+
fromMe: true,
|
|
291
|
+
participantJid: undefined,
|
|
292
|
+
starred: true
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
describe('calls', () => {
|
|
297
|
+
it('pre_accept normalizes to camelCase preAccept', () => {
|
|
298
|
+
const result = adaptBridgeEvent({
|
|
299
|
+
type: 'incoming_call',
|
|
300
|
+
data: {
|
|
301
|
+
from: { user: '5511', server: 's.whatsapp.net' },
|
|
302
|
+
stanza_id: 'STZ',
|
|
303
|
+
timestamp: 1734000000,
|
|
304
|
+
offline: false,
|
|
305
|
+
action: { type: 'pre_accept', call_id: 'CID', call_creator: { user: '5511', server: 's.whatsapp.net' } }
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
if (result?.type !== 'incomingCall')
|
|
309
|
+
throw new Error('narrowing');
|
|
310
|
+
expect(result.action.type).toBe('preAccept');
|
|
311
|
+
expect(result.action.callId).toBe('CID');
|
|
312
|
+
});
|
|
313
|
+
it('offer carries callerPn + isVideo', () => {
|
|
314
|
+
const result = adaptBridgeEvent({
|
|
315
|
+
type: 'incoming_call',
|
|
316
|
+
data: {
|
|
317
|
+
from: { user: '5511', server: 's.whatsapp.net' },
|
|
318
|
+
stanza_id: 'STZ',
|
|
319
|
+
timestamp: 1734000000,
|
|
320
|
+
offline: false,
|
|
321
|
+
action: {
|
|
322
|
+
type: 'offer',
|
|
323
|
+
call_id: 'CID2',
|
|
324
|
+
call_creator: { user: '5511', server: 's.whatsapp.net' },
|
|
325
|
+
caller_pn: { user: '5522', server: 's.whatsapp.net' },
|
|
326
|
+
is_video: true,
|
|
327
|
+
joinable: true,
|
|
328
|
+
audio: []
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
if (result?.type !== 'incomingCall' || result.action.type !== 'offer')
|
|
333
|
+
throw new Error('narrowing');
|
|
334
|
+
expect(result.action.callerPn).toBe('5522@s.whatsapp.net');
|
|
335
|
+
expect(result.action.isVideo).toBe(true);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
describe('noop / passthrough', () => {
|
|
339
|
+
it('history_sync collapses to noop', () => {
|
|
340
|
+
expect(adaptBridgeEvent({ type: 'history_sync', data: {} })).toEqual({
|
|
341
|
+
type: 'noop',
|
|
342
|
+
bridgeType: 'history_sync'
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
it('raw_node passes through with tag/attrs', () => {
|
|
346
|
+
const node = { tag: 'iq', attrs: { id: 'abc' }, content: undefined };
|
|
347
|
+
const result = adaptBridgeEvent({ type: 'raw_node', data: node });
|
|
348
|
+
expect(result?.type).toBe('rawNode');
|
|
349
|
+
});
|
|
350
|
+
it('truly unknown event types return null (caller drops)', () => {
|
|
351
|
+
expect(adaptBridgeEvent({ type: 'invented_2099', data: {} })).toBe(null);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
//# sourceMappingURL=adapt.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapt.test.js","sourceRoot":"","sources":["../../../src/Bridge/__tests__/adapt.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift detection between the bridge's `WhatsAppEvent` union (the runtime
|
|
3
|
+
* contract) and our adapter's `KNOWN_BRIDGE_EVENT_TYPES` (the set of types
|
|
4
|
+
* we explicitly handle).
|
|
5
|
+
*
|
|
6
|
+
* When the bridge ships a new event variant or removes an existing one, this
|
|
7
|
+
* test fails with a precise diff — long before the missing handler shows up
|
|
8
|
+
* as a silently-dropped event in production. Pair with
|
|
9
|
+
* `bun run revalidate` so any bridge bump that changes the event surface is
|
|
10
|
+
* caught before merge.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=dts-drift.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dts-drift.test.d.ts","sourceRoot":"","sources":["../../../src/Bridge/__tests__/dts-drift.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift detection between the bridge's `WhatsAppEvent` union (the runtime
|
|
3
|
+
* contract) and our adapter's `KNOWN_BRIDGE_EVENT_TYPES` (the set of types
|
|
4
|
+
* we explicitly handle).
|
|
5
|
+
*
|
|
6
|
+
* When the bridge ships a new event variant or removes an existing one, this
|
|
7
|
+
* test fails with a precise diff — long before the missing handler shows up
|
|
8
|
+
* as a silently-dropped event in production. Pair with
|
|
9
|
+
* `bun run revalidate` so any bridge bump that changes the event surface is
|
|
10
|
+
* caught before merge.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { describe, it } from 'node:test';
|
|
16
|
+
import { KNOWN_BRIDGE_EVENT_TYPES } from "../adapt.js";
|
|
17
|
+
import { expect } from "../../__tests__/expect.js";
|
|
18
|
+
const `${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}` = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
/** Resolve `whatsapp_rust_bridge.d.ts` from the installed package. */
|
|
20
|
+
function loadBridgeDts() {
|
|
21
|
+
const candidates = [
|
|
22
|
+
path.resolve(`${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}`, '../../../node_modules/whatsapp-rust-bridge/pkg/whatsapp_rust_bridge.d.ts'),
|
|
23
|
+
path.resolve(`${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}`, '../../../../whatsapp-rust-bridge/pkg/whatsapp_rust_bridge.d.ts')
|
|
24
|
+
];
|
|
25
|
+
for (const candidate of candidates) {
|
|
26
|
+
try {
|
|
27
|
+
return readFileSync(candidate, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
/* try next */
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Could not find whatsapp_rust_bridge.d.ts. Tried:\n ${candidates.join('\n ')}`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract every discriminator string from the `WhatsAppEvent` union. The
|
|
37
|
+
* bridge declares it as:
|
|
38
|
+
*
|
|
39
|
+
* export type WhatsAppEvent =
|
|
40
|
+
* | { type: 'connected'; data: ... }
|
|
41
|
+
* | { type: 'disconnected'; data: ... }
|
|
42
|
+
* ...
|
|
43
|
+
* ;
|
|
44
|
+
*
|
|
45
|
+
* We slice from the `export type WhatsAppEvent =` line down to the
|
|
46
|
+
* terminating `;` and pull out every quoted `type:` literal.
|
|
47
|
+
*/
|
|
48
|
+
function extractWhatsAppEventTypes(dts) {
|
|
49
|
+
const start = dts.indexOf('export type WhatsAppEvent =');
|
|
50
|
+
if (start < 0)
|
|
51
|
+
throw new Error('WhatsAppEvent union not found in .d.ts');
|
|
52
|
+
const after = dts.slice(start);
|
|
53
|
+
const end = after.indexOf(';\n');
|
|
54
|
+
if (end < 0)
|
|
55
|
+
throw new Error('WhatsAppEvent union has no terminator in .d.ts');
|
|
56
|
+
const block = after.slice(0, end);
|
|
57
|
+
// Match `type: 'foo'` or `type: "foo"` (the bridge currently uses single quotes
|
|
58
|
+
// but stay forgiving).
|
|
59
|
+
const re = /type:\s*['"]([a-z_]+)['"]/g;
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const m of block.matchAll(re)) {
|
|
62
|
+
seen.add(m[1]);
|
|
63
|
+
}
|
|
64
|
+
if (seen.size === 0)
|
|
65
|
+
throw new Error('Failed to parse any variant from WhatsAppEvent union');
|
|
66
|
+
// `[...seen]` is already a fresh copy — sorting it in place is safe.
|
|
67
|
+
// eslint-disable-next-line unicorn/no-array-sort
|
|
68
|
+
return [...seen].sort();
|
|
69
|
+
}
|
|
70
|
+
describe('Bridge .d.ts ↔ adapter drift', () => {
|
|
71
|
+
const bridgeTypes = extractWhatsAppEventTypes(loadBridgeDts());
|
|
72
|
+
it('every bridge event type from .d.ts is handled by the adapter', () => {
|
|
73
|
+
const missing = bridgeTypes.filter(t => !KNOWN_BRIDGE_EVENT_TYPES.has(t));
|
|
74
|
+
// If this fails, the bridge added a new variant we don't handle yet.
|
|
75
|
+
// Add a `case` in `adaptBridgeEvent` and the matching string in
|
|
76
|
+
// `KNOWN_BRIDGE_EVENT_TYPES`.
|
|
77
|
+
expect(missing).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
it('KNOWN_BRIDGE_EVENT_TYPES has no entries the bridge no longer ships', () => {
|
|
80
|
+
const stale = [...KNOWN_BRIDGE_EVENT_TYPES].filter(t => !bridgeTypes.includes(t));
|
|
81
|
+
// If this fails, the bridge removed a variant we still claim to handle.
|
|
82
|
+
// Remove the corresponding `case` from `adaptBridgeEvent` and the entry
|
|
83
|
+
// from `KNOWN_BRIDGE_EVENT_TYPES`.
|
|
84
|
+
expect(stale).toEqual([]);
|
|
85
|
+
});
|
|
86
|
+
it('parser found a non-trivial number of variants (sanity check)', () => {
|
|
87
|
+
// If this drops to 0/few, the regex broke against a `.d.ts` reformat
|
|
88
|
+
// and the two assertions above would silently pass.
|
|
89
|
+
expect(bridgeTypes.length).toBeGreaterThanOrEqual(20);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=dts-drift.test.js.map
|