@vibersmoon/nexalink 0.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/CHANGELOG.md +72 -0
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/SECURITY.md +59 -0
- package/dist/index.cjs +159 -0
- package/dist/index.d.cts +67 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +129 -0
- package/package.json +74 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to @vibersmoon/nexalink are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.3.0] — 2026-09-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Media Engine:** MIME type detection via magic bytes, file validation, temporary file cleanup with automatic removal.
|
|
10
|
+
- **Plugin System:** `PluginRegistry` for extensible plugin lifecycle (setup, teardown, error isolation).
|
|
11
|
+
- **Directory Services:** `GroupService`, `ContactService`, `ChatService`, `PresenceService` for local metadata storage.
|
|
12
|
+
- **Comprehensive Documentation:** Updated README with full API reference, examples, and project structure.
|
|
13
|
+
- **Security Policy:** Detailed SECURITY.md with legal disclaimers, threat model, and vulnerability reporting.
|
|
14
|
+
- **Type-safe Event Listeners:** All event subscriptions use strict TypeScript inference.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Client core (`NexaClientCore`) now implements `on`, `once`, `off` methods directly.
|
|
19
|
+
- Connection manager emits detailed transition metadata (state, previousState, reason).
|
|
20
|
+
- All public APIs consolidated in root `src/index.ts` (single import point).
|
|
21
|
+
- Logger outputs structured JSON with timestamp, level, and context.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- TypeScript import resolution in `directory/index.ts` (corrected relative paths).
|
|
26
|
+
- ESLint unused import warnings in test files.
|
|
27
|
+
- Build pipeline now generates both ESM and CommonJS with correct type declarations.
|
|
28
|
+
|
|
29
|
+
### Verified
|
|
30
|
+
|
|
31
|
+
- ✅ 82 unit tests passing (14 test files).
|
|
32
|
+
- ✅ ESLint clean (no errors).
|
|
33
|
+
- ✅ TypeScript `noEmit` clean (no errors).
|
|
34
|
+
- ✅ Build output: ESM, CJS, `.d.ts` declarations all present.
|
|
35
|
+
- ✅ npm audit: 0 production vulnerabilities.
|
|
36
|
+
|
|
37
|
+
## [0.2.0] — 2026-09-21
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **Messaging Foundation:** `MessageService` with normalization, validation, ID generation.
|
|
42
|
+
- **Event Model:** Type-safe `NexaEventBus` with `on`, `once`, `off` subscriptions.
|
|
43
|
+
- **Connection State Machine:** 8-state FSM (idle, connecting, authenticating, connected, disconnecting, disconnected, error, awaiting-login) with guards.
|
|
44
|
+
- **Session Store:** File-based `FileSessionStore` with optional AES-256-CBC encryption.
|
|
45
|
+
- **Client Phases:** Phase 2 (stub for testing), Phase 3 (full implementation with connection manager).
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- Configuration validation now uses Zod for strict schema enforcement.
|
|
50
|
+
- Connection manager uses exponential backoff with configurable retry limits.
|
|
51
|
+
|
|
52
|
+
### Tests
|
|
53
|
+
|
|
54
|
+
- 38 unit tests covering errors, logger, config, events, client, connection, messaging.
|
|
55
|
+
|
|
56
|
+
## [0.1.0] — 2026-09-21
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- **Initial Release:** Foundation library with stable module structure.
|
|
61
|
+
- **Error System:** Typed error classes (ConnectionError, AuthenticationError, TimeoutError, ValidationError, UnsupportedFeatureError, ProtocolError, MediaError, PermissionError, RetryableError, ConfigurationError).
|
|
62
|
+
- **Logger:** Structured logging with secret redaction (passwords, tokens, keys, pairing codes).
|
|
63
|
+
- **Configuration:** Zod-backed `NexaClientConfig` with validation and normalization.
|
|
64
|
+
- **Connection Manager:** Exponential backoff retry logic, connection state tracking.
|
|
65
|
+
- **Event Bus:** Async event system with error isolation.
|
|
66
|
+
- **Base Client Types:** `NexaClient` interface, factory function stubs.
|
|
67
|
+
|
|
68
|
+
### Status
|
|
69
|
+
|
|
70
|
+
- ✅ 21 unit tests passing.
|
|
71
|
+
- ✅ Build pipeline (ESM, CJS, TypeScript declarations).
|
|
72
|
+
- ✅ ESLint and Prettier configured.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Akram
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# NexaLink
|
|
2
|
+
|
|
3
|
+
Modular, type-safe WhatsApp automation library for Node.js, built from the ground up with security, correctness, and developer experience in mind.
|
|
4
|
+
|
|
5
|
+
**⚠️ Important:** This is an independent library. It is **not** affiliated with WhatsApp or Meta. Using it may violate WhatsApp Terms of Service. See [SECURITY.md](./SECURITY.md) for legal and protocol constraints.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
| Area | Status |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Package, errors, logger, config, events | ✅ Stable |
|
|
12
|
+
| Connection / authentication | ✅ PHASE 3 complete |
|
|
13
|
+
| Messaging / media | ✅ PHASE 5 complete |
|
|
14
|
+
| Plugins | ✅ PHASE 6 complete |
|
|
15
|
+
| Directory (groups, contacts, chats) | ✅ PHASE 7 complete |
|
|
16
|
+
| Full protocol implementation | ⏳ In progress |
|
|
17
|
+
|
|
18
|
+
**Version:** 0.3.0 — Media, plugins, directory services.
|
|
19
|
+
|
|
20
|
+
## Requirements
|
|
21
|
+
|
|
22
|
+
- Node.js 20+ (LTS preferred)
|
|
23
|
+
- Linux, Windows, or macOS — no OS-specific dependencies
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @vibersmoon/nexalink
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
(Not yet published to npm. For development, clone and build locally.)
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { createClient } from '@vibersmoon/nexalink';
|
|
37
|
+
|
|
38
|
+
const client = await createClient({
|
|
39
|
+
sessionName: 'my-bot',
|
|
40
|
+
authDir: './auth',
|
|
41
|
+
logLevel: 'info',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Listen for messages
|
|
45
|
+
client.on('message', async (msg) => {
|
|
46
|
+
console.log(`From ${msg.from}: ${msg.text}`);
|
|
47
|
+
await client.sendMessage(msg.from, { text: 'Got it!' });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Listen for connection updates
|
|
51
|
+
client.on('connection.update', (update) => {
|
|
52
|
+
console.log(`State: ${update.state}`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await client.connect();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
| Field | Default | Notes |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| `sessionName` | required | 1–64 characters, unique identifier |
|
|
63
|
+
| `authDir` | `~/.nexalink/auth` | Directory to store encrypted sessions |
|
|
64
|
+
| `logLevel` | `info` | `silent`, `error`, `warn`, `info`, `debug` |
|
|
65
|
+
| `connectionTimeoutMs` | `30000` | WebSocket handshake timeout (ms) |
|
|
66
|
+
| `reconnect` | `true` | Auto-reconnect on disconnect |
|
|
67
|
+
| `maxReconnectAttempts` | `5` | Maximum reconnection retries |
|
|
68
|
+
| `retryBaseDelayMs` | `1000` | Initial exponential backoff delay (ms) |
|
|
69
|
+
| `encryption` | disabled | Set `encryption: true` to enable AES-256-CBC session encryption |
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
### `createClient(config: NexaClientConfig): Promise<NexaClient>`
|
|
74
|
+
|
|
75
|
+
Factory function to create a new client instance.
|
|
76
|
+
|
|
77
|
+
### `NexaClient` Interface
|
|
78
|
+
|
|
79
|
+
#### Methods
|
|
80
|
+
|
|
81
|
+
- `connect(): Promise<void>` — Establish WebSocket and authenticate
|
|
82
|
+
- `disconnect(): Promise<void>` — Close connection gracefully
|
|
83
|
+
- `logout(): Promise<void>` — Disconnect and destroy session
|
|
84
|
+
- `sendMessage(jid: string, options: SendMessageOptions): Promise<{ id: string }>` — Send message
|
|
85
|
+
- `on(event: string, listener): () => void` — Register event listener
|
|
86
|
+
- `once(event: string, listener): () => void` — One-time listener
|
|
87
|
+
- `off(event: string, listener): void` — Unsubscribe listener
|
|
88
|
+
|
|
89
|
+
#### Properties
|
|
90
|
+
|
|
91
|
+
- `state: ConnectionState` — Current connection state
|
|
92
|
+
- `config: NexaClientConfig` — Active configuration
|
|
93
|
+
|
|
94
|
+
### Events
|
|
95
|
+
|
|
96
|
+
#### `connection.update`
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
client.on('connection.update', (update) => {
|
|
100
|
+
console.log(update.state); // 'connecting', 'connected', 'disconnected', etc.
|
|
101
|
+
console.log(update.previousState);
|
|
102
|
+
console.log(update.reason); // 'client.connect', 'websocket.error', etc.
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `auth.update`
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
client.on('auth.update', (update) => {
|
|
110
|
+
console.log(update.sessionLoaded); // boolean
|
|
111
|
+
console.log(update.pairingCode); // string | undefined
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### `message`
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
client.on('message', (msg) => {
|
|
119
|
+
console.log(msg.id); // Message ID
|
|
120
|
+
console.log(msg.from); // Sender JID (e.g., '628123456789@s.whatsapp.net')
|
|
121
|
+
console.log(msg.text); // Message text
|
|
122
|
+
console.log(msg.timestamp); // Unix timestamp (ms)
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Error Handling
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import {
|
|
130
|
+
NexaError,
|
|
131
|
+
ConnectionError,
|
|
132
|
+
AuthenticationError,
|
|
133
|
+
TimeoutError,
|
|
134
|
+
ValidationError,
|
|
135
|
+
UnsupportedFeatureError,
|
|
136
|
+
} from '@vibersmoon/nexalink';
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await client.connect();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (error instanceof AuthenticationError) {
|
|
142
|
+
console.error('Auth failed:', error.message);
|
|
143
|
+
} else if (error instanceof ConnectionError) {
|
|
144
|
+
console.error('Connection failed:', error.message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Media Support
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { MediaService } from '@vibersmoon/nexalink';
|
|
153
|
+
|
|
154
|
+
const media = new MediaService({
|
|
155
|
+
mediaDirectory: './media',
|
|
156
|
+
maxBytes: 16 * 1024 * 1024,
|
|
157
|
+
logger: client.logger,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const prepared = await media.prepare({
|
|
161
|
+
buffer: imageBuffer,
|
|
162
|
+
filename: 'photo.jpg',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
console.log(prepared.kind); // 'image', 'video', 'audio', 'document'
|
|
166
|
+
console.log(prepared.mimeType); // 'image/jpeg'
|
|
167
|
+
console.log(prepared.size); // file size in bytes
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Plugins
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { definePlugin, PluginRegistry } from '@vibersmoon/nexalink';
|
|
174
|
+
|
|
175
|
+
const plugin = definePlugin({
|
|
176
|
+
name: 'auto-reply',
|
|
177
|
+
version: '1.0.0',
|
|
178
|
+
setup(host) {
|
|
179
|
+
host.on('message', async (msg) => {
|
|
180
|
+
console.log('Plugin received:', msg.text);
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
teardown() {
|
|
184
|
+
console.log('Cleaning up');
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const registry = new PluginRegistry(client.logger);
|
|
189
|
+
await registry.use(plugin, client);
|
|
190
|
+
|
|
191
|
+
// When done
|
|
192
|
+
await registry.teardownAll();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Directory Services
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import {
|
|
199
|
+
GroupService,
|
|
200
|
+
ContactService,
|
|
201
|
+
ChatService,
|
|
202
|
+
PresenceService,
|
|
203
|
+
} from '@vibersmoon/nexalink';
|
|
204
|
+
|
|
205
|
+
const groups = new GroupService(client.logger);
|
|
206
|
+
groups.upsert({
|
|
207
|
+
id: '123-456@g.us',
|
|
208
|
+
subject: 'Team Chat',
|
|
209
|
+
participants: ['628123456789@s.whatsapp.net'],
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const contacts = new ContactService(client.logger);
|
|
213
|
+
contacts.upsert({
|
|
214
|
+
id: '628123456789@s.whatsapp.net',
|
|
215
|
+
name: 'John Doe',
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const chats = new ChatService(client.logger);
|
|
219
|
+
chats.upsert({
|
|
220
|
+
id: '628123456789@s.whatsapp.net',
|
|
221
|
+
unreadCount: 5,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const presence = new PresenceService(client.logger);
|
|
225
|
+
// Presence updates require active protocol session (PHASE 9+)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
### Setup
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
git clone https://github.com/akram-fanz/nexalink.git
|
|
234
|
+
cd nexalink
|
|
235
|
+
npm install
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Scripts
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
npm test # Run full test suite
|
|
242
|
+
npm run test:watch # Watch mode
|
|
243
|
+
npm run test:coverage # Coverage report
|
|
244
|
+
npm run build # Compile ESM, CJS, TypeScript declarations
|
|
245
|
+
npm run lint # ESLint
|
|
246
|
+
npm run format # Prettier
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Project Structure
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
nexalink/
|
|
253
|
+
├── src/
|
|
254
|
+
│ ├── client/ # Client factory and phases
|
|
255
|
+
│ ├── connection/ # Connection manager, state machine, transport
|
|
256
|
+
│ ├── auth/ # Session store, auth provider
|
|
257
|
+
│ ├── messaging/ # Message model, service, validation
|
|
258
|
+
│ ├── media/ # MIME detection, file validation
|
|
259
|
+
│ ├── plugins/ # Plugin registry and API
|
|
260
|
+
│ ├── groups/ # Group service
|
|
261
|
+
│ ├── contacts/ # Contact service
|
|
262
|
+
│ ├── chats/ # Chat service
|
|
263
|
+
│ ├── presence/ # Presence service
|
|
264
|
+
│ ├── events/ # Event bus
|
|
265
|
+
│ ├── errors/ # Error classes
|
|
266
|
+
│ ├── logger.ts # Logger with secret redaction
|
|
267
|
+
│ ├── config.ts # Configuration validation (Zod)
|
|
268
|
+
│ ├── constants/ # Library constants
|
|
269
|
+
│ ├── types/ # Type definitions
|
|
270
|
+
│ └── index.ts # Public API exports
|
|
271
|
+
├── tests/
|
|
272
|
+
│ └── unit/ # Vitest unit tests (82 tests)
|
|
273
|
+
├── package.json # v0.3.0
|
|
274
|
+
├── tsconfig.json # TypeScript configuration
|
|
275
|
+
├── vitest.config.ts # Vitest configuration
|
|
276
|
+
├── eslint.config.js # ESLint configuration
|
|
277
|
+
├── prettier.config.js # Prettier configuration
|
|
278
|
+
├── README.md # This file
|
|
279
|
+
├── SECURITY.md # Security policy
|
|
280
|
+
├── CHANGELOG.md # Version history
|
|
281
|
+
└── LICENSE # MIT
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Phase Timeline
|
|
286
|
+
|
|
287
|
+
- ✅ **PHASE 1 (Research & Planning):** Feasibility, protocol analysis, architecture planning
|
|
288
|
+
- ✅ **PHASE 2 (Foundation):** Package, errors, logger, config, events, client stub
|
|
289
|
+
- ✅ **PHASE 3 (Connection & Auth):** Connection manager, state machine, session store, WebSocket transport
|
|
290
|
+
- ✅ **PHASE 4 (Messaging):** Message model, validation, service, sendMessage API
|
|
291
|
+
- ✅ **PHASE 5 (Media):** MIME detection, file validation, temporary file cleanup
|
|
292
|
+
- ✅ **PHASE 6 (Plugins):** Plugin registry, lifecycle hooks, extensibility
|
|
293
|
+
- ✅ **PHASE 7 (Directory):** Groups, contacts, chats, presence services (local metadata only)
|
|
294
|
+
- 🔄 **PHASE 8 (Documentation):** README, API docs, security policy, contribution guide
|
|
295
|
+
- ⏳ **PHASE 9 (Release):** GitHub publish, npm publish, CI/CD setup
|
|
296
|
+
|
|
297
|
+
## License
|
|
298
|
+
|
|
299
|
+
MIT. See [LICENSE](./LICENSE).
|
|
300
|
+
|
|
301
|
+
## Contributing
|
|
302
|
+
|
|
303
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) (coming soon).
|
|
304
|
+
|
|
305
|
+
## Support
|
|
306
|
+
|
|
307
|
+
- **Issues:** GitHub Issues: https://github.com/akram-fanz/nexalink/issues
|
|
308
|
+
- **Security:** See [SECURITY.md](./SECURITY.md)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Important Disclaimers
|
|
4
|
+
|
|
5
|
+
### Legal Status
|
|
6
|
+
|
|
7
|
+
- **Not an Official SDK:** @vibersmoon/nexalink is **not** affiliated with WhatsApp or Meta Platforms.
|
|
8
|
+
- **Terms of Service:** Using this library to automate WhatsApp Web may violate WhatsApp's Terms of Service. Users assume all legal risks.
|
|
9
|
+
- **No Warranty:** This library is provided "as-is" without warranty of any kind. The maintainers are not responsible for account bans, data loss, or legal consequences.
|
|
10
|
+
|
|
11
|
+
### Protocol Reliability
|
|
12
|
+
|
|
13
|
+
- **Reverse Engineering:** WhatsApp's internal protocol is **not officially documented**. This library reverse-engineers behavior from WhatsApp Web.
|
|
14
|
+
- **Breaking Changes:** WhatsApp updates can break protocol compatibility. Expect occasional maintenance releases.
|
|
15
|
+
- **Detection Risk:** Automated messaging may trigger WhatsApp's abuse detection and result in account restrictions or bans.
|
|
16
|
+
|
|
17
|
+
## Security Practices
|
|
18
|
+
|
|
19
|
+
### Data Protection
|
|
20
|
+
|
|
21
|
+
- Session files are equivalent to account credentials. Treat them like passwords.
|
|
22
|
+
- Enable `encryption.enabled` with a strong passphrase to encrypt sessions (AES-256-CBC).
|
|
23
|
+
- Never log private keys, pairing codes, passphrases, or session tokens.
|
|
24
|
+
- Restrict filesystem permissions on `authDirectory` (mode 0o700).
|
|
25
|
+
- Rotate a session by calling `logout()` and pairing again if you suspect theft.
|
|
26
|
+
|
|
27
|
+
### File Safety
|
|
28
|
+
|
|
29
|
+
- All file paths are validated to prevent directory traversal attacks.
|
|
30
|
+
- Media file access is restricted to the configured `mediaDirectory`.
|
|
31
|
+
- Temporary files are cleaned up automatically after processing.
|
|
32
|
+
|
|
33
|
+
## Reporting a Vulnerability
|
|
34
|
+
|
|
35
|
+
Do not open a public GitHub issue for credential leaks, remote code execution, or protocol bypasses.
|
|
36
|
+
|
|
37
|
+
Email: `akram-fanz@protonmail.com` with:
|
|
38
|
+
|
|
39
|
+
- Affected version
|
|
40
|
+
- Reproduction steps (without live credentials)
|
|
41
|
+
- Impact assessment
|
|
42
|
+
|
|
43
|
+
We will acknowledge and work toward a fix within 48 hours.
|
|
44
|
+
|
|
45
|
+
## What We Will Never Ship
|
|
46
|
+
|
|
47
|
+
- Credential harvesting
|
|
48
|
+
- Unauthorized account access helpers
|
|
49
|
+
- Spam / bulk-send tooling
|
|
50
|
+
- CAPTCHA or platform-security bypasses
|
|
51
|
+
- Hidden persistence or telemetry
|
|
52
|
+
|
|
53
|
+
## Supported Versions
|
|
54
|
+
|
|
55
|
+
| Version | Status | Security |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| 0.3.x | Current | Actively maintained |
|
|
58
|
+
| 0.2.x | Previous | Security patches only |
|
|
59
|
+
| 0.1.x | Legacy | End of life |
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ChatService: () => ChatService,
|
|
24
|
+
ContactService: () => ContactService,
|
|
25
|
+
GroupService: () => GroupService,
|
|
26
|
+
PresenceService: () => PresenceService
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/errors/base.ts
|
|
31
|
+
var NexaError = class extends Error {
|
|
32
|
+
code;
|
|
33
|
+
metadata;
|
|
34
|
+
retryable;
|
|
35
|
+
retryAfterMs;
|
|
36
|
+
constructor(code, message, options = {}) {
|
|
37
|
+
super(message, { cause: options.cause });
|
|
38
|
+
this.name = new.target.name;
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.metadata = Object.freeze({ ...options.metadata ?? {} });
|
|
41
|
+
this.retryable = options.retryable ?? false;
|
|
42
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
43
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
44
|
+
}
|
|
45
|
+
/** Returns a safe representation suitable for logging and user-facing errors. */
|
|
46
|
+
toJSON() {
|
|
47
|
+
return {
|
|
48
|
+
name: this.name,
|
|
49
|
+
code: this.code,
|
|
50
|
+
message: this.message,
|
|
51
|
+
retryable: this.retryable,
|
|
52
|
+
retryAfterMs: this.retryAfterMs,
|
|
53
|
+
metadata: this.metadata
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/errors/index.ts
|
|
59
|
+
var ValidationError = class extends NexaError {
|
|
60
|
+
constructor(message, options = {}) {
|
|
61
|
+
super("VALIDATION_ERROR" /* VALIDATION_ERROR */, message, options);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var UnsupportedFeatureError = class extends NexaError {
|
|
65
|
+
constructor(message, options = {}) {
|
|
66
|
+
super("UNSUPPORTED_FEATURE" /* UNSUPPORTED_FEATURE */, message, options);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/groups/service.ts
|
|
71
|
+
var GroupService = class {
|
|
72
|
+
groups = /* @__PURE__ */ new Map();
|
|
73
|
+
logger;
|
|
74
|
+
constructor(logger) {
|
|
75
|
+
this.logger = logger;
|
|
76
|
+
}
|
|
77
|
+
upsert(group) {
|
|
78
|
+
if (!group.id || !group.id.endsWith("@g.us")) {
|
|
79
|
+
throw new ValidationError("Invalid group ID format", { metadata: { id: group.id } });
|
|
80
|
+
}
|
|
81
|
+
this.groups.set(group.id, group);
|
|
82
|
+
this.logger.debug("Group upserted", { id: group.id });
|
|
83
|
+
return group;
|
|
84
|
+
}
|
|
85
|
+
get(id) {
|
|
86
|
+
return this.groups.get(id);
|
|
87
|
+
}
|
|
88
|
+
list() {
|
|
89
|
+
return [...this.groups.values()];
|
|
90
|
+
}
|
|
91
|
+
async create(_options) {
|
|
92
|
+
throw new UnsupportedFeatureError("Group creation requires active WebSocket protocol session");
|
|
93
|
+
}
|
|
94
|
+
async addParticipant(_groupId, _participantId) {
|
|
95
|
+
throw new UnsupportedFeatureError("Group mutation requires active WebSocket protocol session");
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/contacts/service.ts
|
|
100
|
+
var ContactService = class {
|
|
101
|
+
contacts = /* @__PURE__ */ new Map();
|
|
102
|
+
logger;
|
|
103
|
+
constructor(logger) {
|
|
104
|
+
this.logger = logger;
|
|
105
|
+
}
|
|
106
|
+
upsert(contact) {
|
|
107
|
+
if (!contact.id || !contact.id.endsWith("@s.whatsapp.net")) {
|
|
108
|
+
throw new ValidationError("Invalid contact ID format", { metadata: { id: contact.id } });
|
|
109
|
+
}
|
|
110
|
+
this.contacts.set(contact.id, contact);
|
|
111
|
+
this.logger.debug("Contact upserted", { id: contact.id });
|
|
112
|
+
return contact;
|
|
113
|
+
}
|
|
114
|
+
get(id) {
|
|
115
|
+
return this.contacts.get(id);
|
|
116
|
+
}
|
|
117
|
+
list() {
|
|
118
|
+
return [...this.contacts.values()];
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/chats/service.ts
|
|
123
|
+
var ChatService = class {
|
|
124
|
+
chats = /* @__PURE__ */ new Map();
|
|
125
|
+
logger;
|
|
126
|
+
constructor(logger) {
|
|
127
|
+
this.logger = logger;
|
|
128
|
+
}
|
|
129
|
+
upsert(chat) {
|
|
130
|
+
this.chats.set(chat.id, chat);
|
|
131
|
+
this.logger.debug("Chat upserted", { id: chat.id });
|
|
132
|
+
return chat;
|
|
133
|
+
}
|
|
134
|
+
get(id) {
|
|
135
|
+
return this.chats.get(id);
|
|
136
|
+
}
|
|
137
|
+
list() {
|
|
138
|
+
return [...this.chats.values()];
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/presence/service.ts
|
|
143
|
+
var PresenceService = class {
|
|
144
|
+
logger;
|
|
145
|
+
constructor(logger) {
|
|
146
|
+
this.logger = logger;
|
|
147
|
+
}
|
|
148
|
+
async sendTyping(_jid) {
|
|
149
|
+
this.logger.debug("Presence simulation requested");
|
|
150
|
+
throw new UnsupportedFeatureError("Presence updates require active WebSocket protocol session");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
154
|
+
0 && (module.exports = {
|
|
155
|
+
ChatService,
|
|
156
|
+
ContactService,
|
|
157
|
+
GroupService,
|
|
158
|
+
PresenceService
|
|
159
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
interface NexaLogger {
|
|
2
|
+
readonly debug: (message: string, data?: unknown) => void;
|
|
3
|
+
readonly info: (message: string, data?: unknown) => void;
|
|
4
|
+
readonly warn: (message: string, data?: unknown) => void;
|
|
5
|
+
readonly error: (message: string, data?: unknown) => void;
|
|
6
|
+
readonly fatal: (message: string, data?: unknown) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface NexaGroup {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly subject: string;
|
|
12
|
+
readonly participants: ReadonlyArray<string>;
|
|
13
|
+
readonly owner?: string;
|
|
14
|
+
readonly createdAt?: number;
|
|
15
|
+
}
|
|
16
|
+
declare class GroupService {
|
|
17
|
+
private readonly groups;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
constructor(logger: NexaLogger);
|
|
20
|
+
upsert(group: NexaGroup): NexaGroup;
|
|
21
|
+
get(id: string): NexaGroup | undefined;
|
|
22
|
+
list(): ReadonlyArray<NexaGroup>;
|
|
23
|
+
create(_options: {
|
|
24
|
+
readonly subject: string;
|
|
25
|
+
readonly participants: ReadonlyArray<string>;
|
|
26
|
+
}): Promise<NexaGroup>;
|
|
27
|
+
addParticipant(_groupId: string, _participantId: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface NexaContact {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly name?: string;
|
|
33
|
+
readonly status?: string;
|
|
34
|
+
readonly profilePictureUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
declare class ContactService {
|
|
37
|
+
private readonly contacts;
|
|
38
|
+
private readonly logger;
|
|
39
|
+
constructor(logger: NexaLogger);
|
|
40
|
+
upsert(contact: NexaContact): NexaContact;
|
|
41
|
+
get(id: string): NexaContact | undefined;
|
|
42
|
+
list(): ReadonlyArray<NexaContact>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface NexaChat {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly unreadCount?: number;
|
|
48
|
+
readonly lastMessageTimestamp?: number;
|
|
49
|
+
readonly archived?: boolean;
|
|
50
|
+
readonly pinned?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare class ChatService {
|
|
53
|
+
private readonly chats;
|
|
54
|
+
private readonly logger;
|
|
55
|
+
constructor(logger: NexaLogger);
|
|
56
|
+
upsert(chat: NexaChat): NexaChat;
|
|
57
|
+
get(id: string): NexaChat | undefined;
|
|
58
|
+
list(): ReadonlyArray<NexaChat>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare class PresenceService {
|
|
62
|
+
private readonly logger;
|
|
63
|
+
constructor(logger: NexaLogger);
|
|
64
|
+
sendTyping(_jid: string): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { ChatService, ContactService, GroupService, PresenceService };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
interface NexaLogger {
|
|
2
|
+
readonly debug: (message: string, data?: unknown) => void;
|
|
3
|
+
readonly info: (message: string, data?: unknown) => void;
|
|
4
|
+
readonly warn: (message: string, data?: unknown) => void;
|
|
5
|
+
readonly error: (message: string, data?: unknown) => void;
|
|
6
|
+
readonly fatal: (message: string, data?: unknown) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface NexaGroup {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly subject: string;
|
|
12
|
+
readonly participants: ReadonlyArray<string>;
|
|
13
|
+
readonly owner?: string;
|
|
14
|
+
readonly createdAt?: number;
|
|
15
|
+
}
|
|
16
|
+
declare class GroupService {
|
|
17
|
+
private readonly groups;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
constructor(logger: NexaLogger);
|
|
20
|
+
upsert(group: NexaGroup): NexaGroup;
|
|
21
|
+
get(id: string): NexaGroup | undefined;
|
|
22
|
+
list(): ReadonlyArray<NexaGroup>;
|
|
23
|
+
create(_options: {
|
|
24
|
+
readonly subject: string;
|
|
25
|
+
readonly participants: ReadonlyArray<string>;
|
|
26
|
+
}): Promise<NexaGroup>;
|
|
27
|
+
addParticipant(_groupId: string, _participantId: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface NexaContact {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly name?: string;
|
|
33
|
+
readonly status?: string;
|
|
34
|
+
readonly profilePictureUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
declare class ContactService {
|
|
37
|
+
private readonly contacts;
|
|
38
|
+
private readonly logger;
|
|
39
|
+
constructor(logger: NexaLogger);
|
|
40
|
+
upsert(contact: NexaContact): NexaContact;
|
|
41
|
+
get(id: string): NexaContact | undefined;
|
|
42
|
+
list(): ReadonlyArray<NexaContact>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface NexaChat {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly unreadCount?: number;
|
|
48
|
+
readonly lastMessageTimestamp?: number;
|
|
49
|
+
readonly archived?: boolean;
|
|
50
|
+
readonly pinned?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare class ChatService {
|
|
53
|
+
private readonly chats;
|
|
54
|
+
private readonly logger;
|
|
55
|
+
constructor(logger: NexaLogger);
|
|
56
|
+
upsert(chat: NexaChat): NexaChat;
|
|
57
|
+
get(id: string): NexaChat | undefined;
|
|
58
|
+
list(): ReadonlyArray<NexaChat>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare class PresenceService {
|
|
62
|
+
private readonly logger;
|
|
63
|
+
constructor(logger: NexaLogger);
|
|
64
|
+
sendTyping(_jid: string): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { ChatService, ContactService, GroupService, PresenceService };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/errors/base.ts
|
|
2
|
+
var NexaError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
metadata;
|
|
5
|
+
retryable;
|
|
6
|
+
retryAfterMs;
|
|
7
|
+
constructor(code, message, options = {}) {
|
|
8
|
+
super(message, { cause: options.cause });
|
|
9
|
+
this.name = new.target.name;
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.metadata = Object.freeze({ ...options.metadata ?? {} });
|
|
12
|
+
this.retryable = options.retryable ?? false;
|
|
13
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
14
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
15
|
+
}
|
|
16
|
+
/** Returns a safe representation suitable for logging and user-facing errors. */
|
|
17
|
+
toJSON() {
|
|
18
|
+
return {
|
|
19
|
+
name: this.name,
|
|
20
|
+
code: this.code,
|
|
21
|
+
message: this.message,
|
|
22
|
+
retryable: this.retryable,
|
|
23
|
+
retryAfterMs: this.retryAfterMs,
|
|
24
|
+
metadata: this.metadata
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/errors/index.ts
|
|
30
|
+
var ValidationError = class extends NexaError {
|
|
31
|
+
constructor(message, options = {}) {
|
|
32
|
+
super("VALIDATION_ERROR" /* VALIDATION_ERROR */, message, options);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var UnsupportedFeatureError = class extends NexaError {
|
|
36
|
+
constructor(message, options = {}) {
|
|
37
|
+
super("UNSUPPORTED_FEATURE" /* UNSUPPORTED_FEATURE */, message, options);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/groups/service.ts
|
|
42
|
+
var GroupService = class {
|
|
43
|
+
groups = /* @__PURE__ */ new Map();
|
|
44
|
+
logger;
|
|
45
|
+
constructor(logger) {
|
|
46
|
+
this.logger = logger;
|
|
47
|
+
}
|
|
48
|
+
upsert(group) {
|
|
49
|
+
if (!group.id || !group.id.endsWith("@g.us")) {
|
|
50
|
+
throw new ValidationError("Invalid group ID format", { metadata: { id: group.id } });
|
|
51
|
+
}
|
|
52
|
+
this.groups.set(group.id, group);
|
|
53
|
+
this.logger.debug("Group upserted", { id: group.id });
|
|
54
|
+
return group;
|
|
55
|
+
}
|
|
56
|
+
get(id) {
|
|
57
|
+
return this.groups.get(id);
|
|
58
|
+
}
|
|
59
|
+
list() {
|
|
60
|
+
return [...this.groups.values()];
|
|
61
|
+
}
|
|
62
|
+
async create(_options) {
|
|
63
|
+
throw new UnsupportedFeatureError("Group creation requires active WebSocket protocol session");
|
|
64
|
+
}
|
|
65
|
+
async addParticipant(_groupId, _participantId) {
|
|
66
|
+
throw new UnsupportedFeatureError("Group mutation requires active WebSocket protocol session");
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/contacts/service.ts
|
|
71
|
+
var ContactService = class {
|
|
72
|
+
contacts = /* @__PURE__ */ new Map();
|
|
73
|
+
logger;
|
|
74
|
+
constructor(logger) {
|
|
75
|
+
this.logger = logger;
|
|
76
|
+
}
|
|
77
|
+
upsert(contact) {
|
|
78
|
+
if (!contact.id || !contact.id.endsWith("@s.whatsapp.net")) {
|
|
79
|
+
throw new ValidationError("Invalid contact ID format", { metadata: { id: contact.id } });
|
|
80
|
+
}
|
|
81
|
+
this.contacts.set(contact.id, contact);
|
|
82
|
+
this.logger.debug("Contact upserted", { id: contact.id });
|
|
83
|
+
return contact;
|
|
84
|
+
}
|
|
85
|
+
get(id) {
|
|
86
|
+
return this.contacts.get(id);
|
|
87
|
+
}
|
|
88
|
+
list() {
|
|
89
|
+
return [...this.contacts.values()];
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/chats/service.ts
|
|
94
|
+
var ChatService = class {
|
|
95
|
+
chats = /* @__PURE__ */ new Map();
|
|
96
|
+
logger;
|
|
97
|
+
constructor(logger) {
|
|
98
|
+
this.logger = logger;
|
|
99
|
+
}
|
|
100
|
+
upsert(chat) {
|
|
101
|
+
this.chats.set(chat.id, chat);
|
|
102
|
+
this.logger.debug("Chat upserted", { id: chat.id });
|
|
103
|
+
return chat;
|
|
104
|
+
}
|
|
105
|
+
get(id) {
|
|
106
|
+
return this.chats.get(id);
|
|
107
|
+
}
|
|
108
|
+
list() {
|
|
109
|
+
return [...this.chats.values()];
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/presence/service.ts
|
|
114
|
+
var PresenceService = class {
|
|
115
|
+
logger;
|
|
116
|
+
constructor(logger) {
|
|
117
|
+
this.logger = logger;
|
|
118
|
+
}
|
|
119
|
+
async sendTyping(_jid) {
|
|
120
|
+
this.logger.debug("Presence simulation requested");
|
|
121
|
+
throw new UnsupportedFeatureError("Presence updates require active WebSocket protocol session");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
export {
|
|
125
|
+
ChatService,
|
|
126
|
+
ContactService,
|
|
127
|
+
GroupService,
|
|
128
|
+
PresenceService
|
|
129
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vibersmoon/nexalink",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Unofficial WhatsApp Web automation library for Node.js. Not affiliated with Meta or WhatsApp.",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"SECURITY.md"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "tsx watch src/index.ts",
|
|
30
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"test:coverage": "vitest run --coverage",
|
|
34
|
+
"lint": "eslint src tests",
|
|
35
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"clean": "rm -rf dist"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"whatsapp",
|
|
41
|
+
"automation",
|
|
42
|
+
"bot",
|
|
43
|
+
"unofficial",
|
|
44
|
+
"web-api"
|
|
45
|
+
],
|
|
46
|
+
"author": "Akram",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"pino": "^9.6.0",
|
|
50
|
+
"qrcode-terminal": "^0.12.0",
|
|
51
|
+
"ws": "^8.18.1",
|
|
52
|
+
"zod": "^3.23.8"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.10.5",
|
|
56
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
57
|
+
"@types/ws": "^8.5.10",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
|
59
|
+
"@typescript-eslint/parser": "^8.15.0",
|
|
60
|
+
"eslint": "^9.15.0",
|
|
61
|
+
"eslint-config-prettier": "^9.1.0",
|
|
62
|
+
"prettier": "^3.3.3",
|
|
63
|
+
"tsup": "^8.4.0",
|
|
64
|
+
"tsx": "^4.19.2",
|
|
65
|
+
"typescript": "^5.6.3",
|
|
66
|
+
"vitest": "^4.1.11"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=20.0.0"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public"
|
|
73
|
+
}
|
|
74
|
+
}
|