@zhuxe-js/fake-server 0.0.0-stage → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +322 -2
- package/package.json +62 -5
package/README.md
CHANGED
|
@@ -1,3 +1,323 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zhuxe-js/fake-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
In-process fake WhatsApp server that drives the real `zhuxe-assistance` `WaClient` end-to-end - full Noise XX/IK handshake, QR pairing, Signal Protocol (X3DH + Double Ratchet), SenderKey for groups, media upload/download over self-signed HTTPS, app-state sync - all without touching WhatsApp servers.
|
|
4
|
+
|
|
5
|
+
It serves both transports: companions over WebSocket, and phones over the
|
|
6
|
+
mobile TCP transport, including the companion-hosting side of a primary
|
|
7
|
+
(pair-device, key-index list, pairing-code handshake).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -D @zhuxe-js/fake-server zhuxe-assistance
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`zhuxe-assistance` is a peer dependency - the fake server reuses the core's binary codec, crypto, and protos. Everything below works from any project; nothing requires this monorepo.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { FakeWaServer } from '@zhuxe-js/fake-server'
|
|
21
|
+
import { createStore, WaClient } from 'zhuxe-assistance'
|
|
22
|
+
|
|
23
|
+
const server = await FakeWaServer.start()
|
|
24
|
+
const client = new WaClient({
|
|
25
|
+
store: createStore({
|
|
26
|
+
providers: { auth: 'memory', signal: 'memory', senderKey: 'memory', appState: 'memory' }
|
|
27
|
+
}),
|
|
28
|
+
chatSocketUrls: [server.url],
|
|
29
|
+
testHooks: { noiseRootCa: server.noiseRootCa },
|
|
30
|
+
proxy: { mediaUpload: server.mediaProxyAgent, mediaDownload: server.mediaProxyAgent }
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
await client.connect()
|
|
34
|
+
const pipeline = await server.waitForAuthenticatedPipeline()
|
|
35
|
+
// ... pair, create peers, send messages
|
|
36
|
+
await server.stop()
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
src/
|
|
43
|
+
├── api/ # Public-facing API
|
|
44
|
+
│ ├── FakeWaServer.ts # Main facade – WS/TCP servers, IQ router, registries, lifecycle
|
|
45
|
+
│ ├── FakePeer.ts # Simulated WhatsApp peer – Signal crypto, send/recv, groups
|
|
46
|
+
│ ├── FakePairingDriver.ts # QR pairing flow orchestrator (server plays the primary)
|
|
47
|
+
│ ├── FakeMobilePrimary.ts # Registered mobile-primary credentials for a phone client
|
|
48
|
+
│ └── Scenario.ts # Declarative test scenario DSL
|
|
49
|
+
├── protocol/ # Protocol-layer builders, parsers, crypto
|
|
50
|
+
│ ├── auth/ # Pairing, ADV identity, cert chain, client payload
|
|
51
|
+
│ ├── iq/ # IQ stanza handlers (abprops, privacy, groups, profile, ...)
|
|
52
|
+
│ ├── push/ # Inbound stanza builders (message, notification, receipt, ...)
|
|
53
|
+
│ ├── signal/ # Signal Protocol impl (Double Ratchet, SenderKey, prekeys)
|
|
54
|
+
│ └── stream/ # Stream error builders
|
|
55
|
+
├── infra/ # Transport infrastructure
|
|
56
|
+
│ ├── WaFakeWsServer.ts # Raw WebSocket server (ws) – companion transport
|
|
57
|
+
│ ├── WaFakeTcpServer.ts # Raw TCP server – mobile transport
|
|
58
|
+
│ ├── socket-adapters.ts # Per-carrier socket adapters (WebSocket, TCP)
|
|
59
|
+
│ ├── WaFakeConnection.ts # Per-connection, carrier-agnostic wrapper
|
|
60
|
+
│ ├── WaFakeConnectionPipeline.ts # Noise handshake + authenticated frame transport
|
|
61
|
+
│ ├── WaFakeFrameSocket.ts # Length-prefixed framing layer
|
|
62
|
+
│ ├── WaFakeTransport.ts # AES-GCM noise transport (post-handshake encrypt/decrypt)
|
|
63
|
+
│ └── WaFakeMediaHttpsServer.ts # Self-signed HTTPS media up/download
|
|
64
|
+
├── state/ # State stores
|
|
65
|
+
│ ├── fake-media-store.ts # In-memory media blob store
|
|
66
|
+
│ └── fake-app-state-collection.ts # App-state patch/snapshot provider
|
|
67
|
+
├── transport/ # Re-exports from zhuxe-assistance (codec, crypto, protos)
|
|
68
|
+
└── __tests__/ # Cross-check test suite
|
|
69
|
+
└── helpers/ # Shared test utilities (zhuxe-client factory)
|
|
70
|
+
|
|
71
|
+
bench/
|
|
72
|
+
├── messaging.bench.ts # 4-scenario messaging profiler (send/recv × 1:1/group)
|
|
73
|
+
├── server-process.ts # Child-process entry for --separate-process mode
|
|
74
|
+
└── server-rpc.ts # IPC client for the child-process server
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Core concepts
|
|
78
|
+
|
|
79
|
+
### FakeWaServer
|
|
80
|
+
|
|
81
|
+
Central facade. Manages the WS listener, noise handshake, IQ router, and all state registries.
|
|
82
|
+
|
|
83
|
+
Constructor options (`FakeWaServer.start({...})` / `new FakeWaServer({...})`):
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const server = await FakeWaServer.start({
|
|
87
|
+
host: '127.0.0.1', // default
|
|
88
|
+
port: 5222, // default: random free port
|
|
89
|
+
path: '/ws/chat', // default
|
|
90
|
+
successNodeAttributes: {
|
|
91
|
+
// stamped on every post-handshake <success/>
|
|
92
|
+
lid: '5511999@lid',
|
|
93
|
+
displayName: 'Fake Display'
|
|
94
|
+
},
|
|
95
|
+
defaultIqHandlers: false // start with an empty IQ router (default: true)
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- **Peer registry** (`peerRegistry`): maps device JIDs → `FakePeer` instances. The global `usync` + `prekey-fetch` IQ handlers consult this.
|
|
100
|
+
- **Sessions**: state is grouped per session (see below). A single-client server uses one default session, and the `server.registries` / `server.expectIq(...)` / `server.preKeyDispenser` surface delegates straight to it.
|
|
101
|
+
- **Group registry** (`groupRegistry`): maps group JIDs → group metadata + participants. The `w:g2` handler serves it.
|
|
102
|
+
- **IQ router** (`WaFakeIqRouter`): first-match-wins stanza dispatcher with `{ xmlns, type, childTag }` matchers. ~27 global handlers registered in the constructor cover every IQ the lib emits during normal operation (disable them with `defaultIqHandlers: false`). `registerIqHandler(matcher, respond)` registers at high priority and shadows the defaults; a responder that returns `null` falls through to the next matching handler, so you can observe an IQ (capture, assert) and still let the default answer it.
|
|
103
|
+
- **Prekey dispenser**: hands out unique one-time prekeys from the lib's upload to FakePeers. Resets on each forced refill (`triggerPreKeyUpload({ force: true })`).
|
|
104
|
+
- **Listener fan-outs**: `onOutboundGroupOp`, `onOutboundPrivacySet`, `onOutboundBlocklistChange`, `onOutboundProfilePictureSet`, `onOutboundStatusSet`, `onLogout`, `onOutboundPrivacyTokenIssue`, `onOutboundDirtyBitsClear`.
|
|
105
|
+
|
|
106
|
+
### Sessions (multiple clients, one server)
|
|
107
|
+
|
|
108
|
+
By default every connection shares one session, so a single `WaClient` sees the whole `FakeWaServer` surface. To run **several clients against one server without them sharing any state**, pass a `sessionKey` resolver: each authenticated connection is bound to an isolated session keyed by the returned id. Sessions never share peers, groups, prekeys, app-state, or captured stanzas.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const server = await FakeWaServer.start({
|
|
112
|
+
// Key each connection by its login username; pre-login (registration)
|
|
113
|
+
// connections fall under one transient bucket.
|
|
114
|
+
sessionKey: ({ clientPayload }) =>
|
|
115
|
+
clientPayload.kind === 'login' ? String(clientPayload.username) : 'pending'
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// After a client authenticates, reach its isolated state through the pipeline:
|
|
119
|
+
const session = server.sessionFor(pipeline) // FakeServerSession
|
|
120
|
+
await session.expectIq({ xmlns: 'passive', type: 'set' })
|
|
121
|
+
session.registries.peerRegistry // this client's peers only
|
|
122
|
+
session.preKeyDispenser.capturedPreKeyBundleSnapshot() // this client's prekeys
|
|
123
|
+
|
|
124
|
+
// Or address a session by id directly:
|
|
125
|
+
const alice = server.session('5511111111111')
|
|
126
|
+
|
|
127
|
+
// Peer creation routes to the connection's session automatically:
|
|
128
|
+
const peer = await server.createFakePeer({ jid: peerJid }, pipeline)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Notes:
|
|
132
|
+
|
|
133
|
+
- The resolver runs once per connection, right after authentication (so `clientPayload` is available). Connections that resolve to the same id reuse one session.
|
|
134
|
+
- `server.registerIqHandler(...)` applies to every session (present and future); `session.registerIqHandler(...)` scopes to one session.
|
|
135
|
+
- The media HTTPS store is process-global and shared across sessions; isolation covers peers/groups/prekeys/app-state/captures, not uploaded media blobs.
|
|
136
|
+
- QR pairing writes to the connection's session too, so pair clients **sequentially** when keying registration connections into a shared bucket.
|
|
137
|
+
|
|
138
|
+
### FakePeer
|
|
139
|
+
|
|
140
|
+
Simulated WhatsApp peer with real Signal Protocol crypto:
|
|
141
|
+
|
|
142
|
+
- **1:1 messaging**: `peer.sendConversation(text)` encrypts via Double Ratchet and pushes a `<message><enc type="pkmsg|msg"/></message>` stanza.
|
|
143
|
+
- **Group messaging**: `peer.sendGroupConversation(groupJid, text)` bootstraps a SenderKey chain, encrypts the SKDM via 1:1 session, and sends `<enc type="skmsg"/>`.
|
|
144
|
+
- **Receive**: `peer.expectMessage()` / `peer.expectGroupMessage()` capture and decrypt the lib's outbound stanzas.
|
|
145
|
+
- **X3DH**: each peer generates its own identity keypair, signed prekey, and one-time prekeys. The prekey dispenser ensures unique consumption.
|
|
146
|
+
|
|
147
|
+
### Pairing
|
|
148
|
+
|
|
149
|
+
`server.runPairing(pipeline, { deviceJid }, materialFn)` drives the full QR-pairing flow:
|
|
150
|
+
|
|
151
|
+
1. Sends `pair-device` IQ with random refs
|
|
152
|
+
2. Awaits the `advSecretKey` + `identityPublicKey` from the client's `auth_qr` event
|
|
153
|
+
3. Builds an `ADVSignedDeviceIdentityHMAC` with a fresh fake primary keypair
|
|
154
|
+
4. Pushes `pair-success` IQ
|
|
155
|
+
|
|
156
|
+
After pairing, the lib reconnects with IK handshake. Use `waitForNextAuthenticatedPipeline()` to capture the post-pair pipeline.
|
|
157
|
+
|
|
158
|
+
### Mobile transport (phone clients)
|
|
159
|
+
|
|
160
|
+
Start the server with `{ tcp: true }` to also listen for the WhatsApp Mobile
|
|
161
|
+
transport, which dials `tcp://host:port` instead of upgrading to a WebSocket.
|
|
162
|
+
Both listeners share one server identity, session model, and IQ router.
|
|
163
|
+
|
|
164
|
+
Registration happens out of band (against WhatsApp's HTTP endpoints), so a
|
|
165
|
+
phone session starts from credentials that already exist – seed them with
|
|
166
|
+
`seedFakeMobilePrimary`:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
const server = await FakeWaServer.start({ tcp: true })
|
|
170
|
+
const store = createStore({})
|
|
171
|
+
const primary = await seedFakeMobilePrimary(store, 'phone-session', {
|
|
172
|
+
phoneNumber: '5511999999999'
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const client = new WaClient({
|
|
176
|
+
store,
|
|
177
|
+
sessionId: 'phone-session',
|
|
178
|
+
mobileTransport: { deviceInfo: primary.deviceInfo, tcpUrl: server.tcpUrl },
|
|
179
|
+
testHooks: { noiseRootCa: server.noiseRootCa }
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Companion hosting (a phone links a companion)
|
|
184
|
+
|
|
185
|
+
The inverse of `runPairing`: the identity is signed by a real mobile-primary
|
|
186
|
+
client rather than by the server, which relays that signed exchange and does the
|
|
187
|
+
account bookkeeping around it. `offerCompanionPairing(pipeline)` pushes the refs
|
|
188
|
+
a companion turns into its QR; when the primary uploads `pair-device` for one of
|
|
189
|
+
them, the server mints the device jid, hands the signed identity to the
|
|
190
|
+
companion as `pair-success`, and tracks the link.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
await server.offerCompanionPairing(companionPipeline)
|
|
194
|
+
const linked = await primary.mobile.linkCompanion(qrFromCompanion)
|
|
195
|
+
server.companionHost.linkedCompanions() // [{ deviceJid, keyIndex, ... }]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The pairing-code flow works the same way end to end: the server mints the ref on
|
|
199
|
+
`companion_hello` and relays `primary_hello` / `companion_finish` between the two
|
|
200
|
+
clients, so `client.auth.requestPairingCode()` on one side and
|
|
201
|
+
`client.mobile.linkCompanionByCode()` on the other complete a real handshake.
|
|
202
|
+
|
|
203
|
+
`pushAccountSyncDevices(pipeline)` pushes the account's device set as an
|
|
204
|
+
`account_sync` notification – pass a shorter list to tell a primary that a
|
|
205
|
+
device disappeared while it was offline.
|
|
206
|
+
|
|
207
|
+
## IQ coverage
|
|
208
|
+
|
|
209
|
+
Every outbound IQ the lib sends during normal operation is handled:
|
|
210
|
+
|
|
211
|
+
| IQ | Handler | State mutation |
|
|
212
|
+
| -------------------------------------------------------------------------------- | ----------------------------- | ----------------------------------------- |
|
|
213
|
+
| `abt` get | `abprops` | Seedable via `setAbProps()` |
|
|
214
|
+
| `w:p` / `urn:xmpp:ping` get | `whatsapp-ping` / `xmpp-ping` | Ack |
|
|
215
|
+
| `encrypt` set | `prekey-upload` | Captures bundle, resets dispenser |
|
|
216
|
+
| `encrypt` get `<digest>` | `signal-digest` | Returns 404 → forces upload |
|
|
217
|
+
| `encrypt` get `<count>` | `prekey-count` | Serves remaining dispenser prekey count |
|
|
218
|
+
| `encrypt` set `<rotate>` | `signed-prekey-rotate` | Ack |
|
|
219
|
+
| `encrypt` get `<key>` | `prekey-fetch` | Serves peer bundles from registry |
|
|
220
|
+
| `passive` set | `passive-mode` | Ack |
|
|
221
|
+
| `usync` get | `usync` | Resolves device IDs from registry |
|
|
222
|
+
| `w:m` set `<media_conn>` | `media-conn` | Points lib at fake HTTPS server |
|
|
223
|
+
| `w:sync:app:state` set | `app-state-sync` | Serves patches/snapshots from providers |
|
|
224
|
+
| `w:g2` get `<query>` | `group-metadata` | Serves from group registry |
|
|
225
|
+
| `w:g2` set `<create\|add\|remove\|promote\|demote\|subject\|description\|leave>` | `group-*` | Mutates group registry |
|
|
226
|
+
| `privacy` get | `privacy-get` | Serves settings + disallowed lists |
|
|
227
|
+
| `privacy` set `<privacy>` | `privacy-set` | Mutates privacy state |
|
|
228
|
+
| `privacy` set `<tokens>` | `privacy-token-issue` | Captures issued tokens |
|
|
229
|
+
| `blocklist` get/set | `blocklist-*` | Mutates blocklist |
|
|
230
|
+
| `w:profile:picture` get/set | `profile-picture-*` | Mutates profile picture registry |
|
|
231
|
+
| `status` set | `status-set` | Captures latest status |
|
|
232
|
+
| `w:biz` get/set | `business-profile-*` | Serves/captures business profiles |
|
|
233
|
+
| `md` set `<remove-companion-device>` | `remove-companion-device` | Unlinks a hosted device, else logs out |
|
|
234
|
+
| `md` set `<pair-device>` | `companion-pair-device` | Mints the device jid, relays pair-success |
|
|
235
|
+
| `md` set `<key-index-list>` | `companion-key-index-list` | Records the published list |
|
|
236
|
+
| `md` set `<link_code_companion_reg>` | `link-code-companion-reg` | Relays the pairing-code handshake |
|
|
237
|
+
| `newsletter` get `<my_addons>` | `newsletter-my-addons` | Ack |
|
|
238
|
+
| `urn:xmpp:whatsapp:dirty` set | `dirty-bits-clear` | Captures cleared bits |
|
|
239
|
+
|
|
240
|
+
## Using with non-zhuxe-js clients
|
|
241
|
+
|
|
242
|
+
The fake server speaks the real wire protocol, so other WhatsApp Web libraries (Baileys forks, whatsmeow, ...) can connect to it too. The recipe:
|
|
243
|
+
|
|
244
|
+
1. **Point the client's WebSocket URL at `server.url`** (the upgrade path is `/ws/chat`, same as production).
|
|
245
|
+
2. **Trust the fake root CA.** The server signs its Noise cert chain with a per-instance random root; clients that verify the cert signature against WhatsApp's pinned production key must be told to trust `server.noiseRootCa.publicKey` instead (exposed via the CLI's `--json` as `noiseRootCa.publicKeyHex`). The chain carries a valid `notBefore`/`notAfter` window, so clients that enforce validity work out of the box.
|
|
246
|
+
3. **Post-connect IQs are answered by default**: the `passive` set IQ and the `encrypt` get `<count>` prekey-count query, which Baileys-family and whatsmeow clients block on before reporting the connection as open.
|
|
247
|
+
4. **Offline drain bulletin**: after every login the server sends `<ib><offline count="0"/></ib>`, which Baileys-family clients require before flushing their buffered events.
|
|
248
|
+
5. **QR pairing**: in-process, use `server.runPairing(...)` with the client's `advSecretKey` + identity public key; from the standalone CLI, use `--pair <device-jid>` and paste the QR payload the client displays when prompted.
|
|
249
|
+
|
|
250
|
+
## Benchmarking
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Default: 1000 contacts × 2 devices, 4 groups × 500 members, 1000 msgs/scenario
|
|
254
|
+
npm --workspace=@zhuxe-js/fake-server run bench:messaging
|
|
255
|
+
|
|
256
|
+
# With CPU + heap profiling (separate process for clean lib-only profiles)
|
|
257
|
+
node --expose-gc --import tsx packages/fake-server/bench/messaging.bench.ts \
|
|
258
|
+
--separate-process --cpu --per-scenario --heap --out-dir=./profiles
|
|
259
|
+
|
|
260
|
+
# Focused on a single scenario
|
|
261
|
+
ZHUXE_BENCH_SCENARIOS=send_group ZHUXE_BENCH_MESSAGES=2000 \
|
|
262
|
+
node --expose-gc --import tsx packages/fake-server/bench/messaging.bench.ts --cpu
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Profiling flags
|
|
266
|
+
|
|
267
|
+
| Flag | Output |
|
|
268
|
+
| ------------------------- | ---------------------------------------------------------------- |
|
|
269
|
+
| `--cpu` | `cpu-<ts>.cpuprofile` (whole run) |
|
|
270
|
+
| `--heap` | `heap-<ts>.heaptimeline` (allocation tracking) |
|
|
271
|
+
| `--snapshot` | `snapshot-{start,end}-<ts>.heapsnapshot` |
|
|
272
|
+
| `--per-scenario` | Per-scenario CPU profiles |
|
|
273
|
+
| `--snapshot-per-scenario` | Per-scenario heap snapshots |
|
|
274
|
+
| `--separate-process` | Forks fake server into child process for clean lib CPU profiling |
|
|
275
|
+
| `--out-dir=<path>` | Output directory (default: cwd) |
|
|
276
|
+
|
|
277
|
+
### Env vars
|
|
278
|
+
|
|
279
|
+
| Var | Default | Description |
|
|
280
|
+
| ---------------------------- | ------- | ------------------------------------------------------------ |
|
|
281
|
+
| `ZHUXE_BENCH_CONTACTS` | 1000 | Number of contacts |
|
|
282
|
+
| `ZHUXE_BENCH_CONTACT_DEVICES` | 2 | Devices per contact |
|
|
283
|
+
| `ZHUXE_BENCH_GROUPS` | 4 | Number of groups |
|
|
284
|
+
| `ZHUXE_BENCH_GROUP_MEMBERS` | 500 | Members per group |
|
|
285
|
+
| `ZHUXE_BENCH_MESSAGES` | 1000 | Messages per scenario |
|
|
286
|
+
| `ZHUXE_BENCH_SCENARIOS` | all | CSV of: `send_1to1`, `recv_1to1`, `send_group`, `recv_group` |
|
|
287
|
+
| `ZHUXE_BENCH_JSON` | 0 | Set to `1` to print JSON results |
|
|
288
|
+
| `ZHUXE_BENCH_VERBOSE` | 0 | Set to `1` to forward lib warns/errors |
|
|
289
|
+
|
|
290
|
+
## CLI
|
|
291
|
+
|
|
292
|
+
The package ships a `fake-wa-server` bin, so the standalone server works from any project (or no project at all):
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
# One-off via npx
|
|
296
|
+
npx @zhuxe-js/fake-server --port 5222 --peer 5511888@s.whatsapp.net --log
|
|
297
|
+
|
|
298
|
+
# Installed as a dev dependency
|
|
299
|
+
npx fake-wa-server --port 5222 --peer 5511888@s.whatsapp.net --log
|
|
300
|
+
|
|
301
|
+
# First-time QR pairing: prompts on stdin for the QR payload the client displays
|
|
302
|
+
npx fake-wa-server --port 5222 --pair 5511999:1@s.whatsapp.net
|
|
303
|
+
|
|
304
|
+
# Print connection info (url + noise root CA hex) as JSON, for scripting
|
|
305
|
+
npx fake-wa-server --json
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
From inside this monorepo (runs the source, no build needed):
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
npm --workspace=@zhuxe-js/fake-server run cli -- --port 5222 --peer 5511888@s.whatsapp.net --log
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Run with `--help` for the full flag list.
|
|
315
|
+
|
|
316
|
+
## Test suite
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
npm --workspace=@zhuxe-js/fake-server test
|
|
320
|
+
# 164 tests, --test-concurrency=1
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Cross-check tests drive a real `WaClient` against the fake server and assert on both sides (lib emits correct events, peer decrypts correctly). Unit tests validate protocol builders/parsers in isolation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@zhuxe-js/fake-server",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Fake WhatsApp Web server used to test zhuxe-assistance",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "vinikjkkj <contact@vinicius.email> (https://github.com/vinikjkkj)",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/abay62096-dotcom/zhuxe.git",
|
|
10
|
+
"directory": "packages/fake-server"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"module": "dist/esm/index.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"bin": {
|
|
16
|
+
"fake-wa-server": "./dist/bin.js"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"require": "./dist/index.js",
|
|
22
|
+
"import": "./dist/esm/index.js",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.cjs.json && tsc -p tsconfig.build.esm.json && node ../../scripts/finalize-esm-build.cjs",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "node --import tsx --test --test-concurrency=1 \"src/**/__tests__/**/*.test.ts\"",
|
|
36
|
+
"cli": "node --import tsx src/bin.ts",
|
|
37
|
+
"bench:messaging": "node --expose-gc --import tsx bench/messaging.bench.ts",
|
|
38
|
+
"bench:connect": "node --expose-gc --import tsx bench/connect-lifecycle.bench.ts",
|
|
39
|
+
"bench:history": "node --expose-gc --import tsx bench/history-sync.bench.ts",
|
|
40
|
+
"bench:usync": "node --expose-gc --import tsx bench/bulk-usync.bench.ts",
|
|
41
|
+
"bench:group": "node --expose-gc --import tsx bench/group-provision.bench.ts",
|
|
42
|
+
"bench:media": "node --expose-gc --import tsx bench/media-upload.bench.ts",
|
|
43
|
+
"bench:media:messaging": "node --expose-gc --import tsx bench/messaging-media.bench.ts",
|
|
44
|
+
"bench:retry": "node --expose-gc --import tsx bench/retry.bench.ts",
|
|
45
|
+
"bench:receipts": "node --expose-gc --import tsx bench/receipts-flood.bench.ts",
|
|
46
|
+
"bench:reconnect": "node --expose-gc --import tsx bench/reconnect-resume.bench.ts",
|
|
47
|
+
"bench:appstate": "node --expose-gc --import tsx bench/appstate.bench.ts",
|
|
48
|
+
"bench:all-stores": "node bench/run-all-stores.cjs"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"zhuxe-assistance": "^1.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"ws": "^8.18.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/ws": "^8.18.1",
|
|
58
|
+
"zhuxe-assistance": "file:../.."
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20.9.0"
|
|
62
|
+
}
|
|
63
|
+
}
|