@zervonex/baileys 3.0.9 → 4.0.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 CHANGED
@@ -3189,6 +3189,7 @@ import {
3189
3189
  useMultiFileAuthState,
3190
3190
  useKeyvAuthState,
3191
3191
  hasKeyvAuth,
3192
+ clearAdapterCache,
3192
3193
  makeCacheableSignalKeyStore,
3193
3194
  Browsers,
3194
3195
  DisconnectReason,
@@ -208,6 +208,10 @@ export const INITIAL_PREKEY_COUNT = 95;
208
208
  export const UPLOAD_TIMEOUT = 30000;
209
209
  export const MIN_UPLOAD_INTERVAL = 5000;
210
210
  export const HISTORY_SYNC_PAUSED_TIMEOUT_MS = 120000;
211
+ export const DEFAULT_PREKEY_RETENTION = 150
212
+ export const DEFAULT_CLEANUP_THRESHOLD = 50
213
+ export const CLEANUP_INTERVAL_MS = 10 * 60 * 1000
214
+ export const DEFAULT_COLLECTION_NAME = 'zervonex'
211
215
  export const DEFAULT_CACHE_TTLS = {
212
216
  SIGNAL_STORE: 5 * 60,
213
217
  MSG_RETRY: 60 * 60,
@@ -6,8 +6,9 @@ import PQueue from 'p-queue'
6
6
  import { v4 } from 'uuid'
7
7
  import { DEFAULT_CACHE_TTLS } from '../Defaults/index.js'
8
8
  import { Curve, signedKeyPair } from './crypto.js'
9
- import { delay, generateRegistrationId } from './generics.js'
9
+ import { delay, generateRegistrationId, BufferJSON } from './generics.js'
10
10
  import { PreKeyManager } from './pre-key-manager.js'
11
+ import { proto } from '../../WAProto/index.js'
11
12
 
12
13
  export function makeCacheableSignalKeyStore(store, logger, _cache) {
13
14
  const cache = _cache || new NodeCache({ stdTTL: DEFAULT_CACHE_TTLS.SIGNAL_STORE, useClones: false, deleteOnExpire: true })
@@ -139,4 +140,20 @@ export const initAuthCreds = () => {
139
140
  routingInfo: undefined,
140
141
  additionalData: undefined
141
142
  }
142
- }
143
+ }
144
+
145
+
146
+ export const bootstrapCreds = raw => raw ?? initAuthCreds()
147
+ export const patchAppStateKey = (type, v) => type === 'app-state-sync-key' && v ? proto.Message.AppStateSyncKeyData.fromObject(v) : v
148
+ export const ser = v => v == null ? null : JSON.parse(JSON.stringify(v, BufferJSON.replacer))
149
+ export const deser = raw => {
150
+ if (raw == null) return null
151
+ let p = typeof raw === 'string'
152
+ ? (() => { try { return JSON.parse(raw) } catch { return raw } })()
153
+ : raw
154
+ let depth = 0
155
+ while (depth++ < 3 && p && typeof p === 'object' && !Array.isArray(p) && Object.keys(p).length === 1 && 'value' in p) p = p.value
156
+ if (p == null) return null
157
+ if (typeof p === 'object' && '__checksum' in p && 'data' in p) p = p.data
158
+ return JSON.parse(JSON.stringify(p), BufferJSON.reviver)
159
+ }
@@ -14,6 +14,7 @@ export * from './lt-hash.js'
14
14
  export * from './auth-utils.js'
15
15
  export * from './baileys-event-stream.js'
16
16
  export * from './use-multi-file-auth-state.js'
17
+ export * from './use-keyv-auth-state.js'
17
18
  export * from './link-preview.js'
18
19
  export * from './event-buffer.js'
19
20
  export * from './process-message.js'
@@ -0,0 +1,169 @@
1
+ import { Keyv, detectKeyvStorage } from 'keyv'
2
+ import { DEFAULT_PREKEY_RETENTION, DEFAULT_CLEANUP_THRESHOLD, CLEANUP_INTERVAL_MS, DEFAULT_COLLECTION_NAME } from '../Defaults/index.js'
3
+ import { bootstrapCreds, patchAppStateKey, ser, deser } from './auth-utils.js'
4
+ import { PROTOCOL_ADAPTERS } from '../WABinary/index.js'
5
+ import logger from './logger.js'
6
+
7
+ // ── Adapter resolution ────────────────────────────────────────────────────────
8
+
9
+ // Only these adapters accept a named collection/table — redis, valkey, etcd etc do not
10
+ const COLLECTION_KEY = { '@keyv/mongo': 'collection', '@keyv/postgres': 'table', '@keyv/mysql': 'table', '@keyv/sqlite': 'table' }
11
+
12
+ // Cached per conn::col::namespace — each session gets its own adapter instance so
13
+ // clear() never bleeds across sessions. Underlying drivers manage their own connection
14
+ // pools so multiple adapters on the same URI still share one real connection.
15
+ const _adapterCache = new Map()
16
+ export const clearAdapterCache = () => _adapterCache.clear()
17
+
18
+ const resolveAdapter = async (conn, col, namespace) => {
19
+ const ck = `${conn}::${col ?? ''}::${namespace ?? ''}`
20
+ if (_adapterCache.has(ck)) return _adapterCache.get(ck)
21
+ let protocol
22
+ try { protocol = new URL(conn).protocol } catch { throw new Error(`[Auth] Invalid connection string: "${conn}"`) }
23
+ const pkg = PROTOCOL_ADAPTERS[protocol]
24
+ if (!pkg) throw new Error(`[Auth] Unsupported protocol "${protocol}" — supported: ${Object.keys(PROTOCOL_ADAPTERS).map(p => p.replace(':', '')).join(', ')}`)
25
+ let mod
26
+ try { mod = await import(pkg) } catch (e) {
27
+ if (e.code === 'ERR_MODULE_NOT_FOUND') throw new Error(`[Auth] "${pkg}" required — npm i ${pkg}`)
28
+ throw e
29
+ }
30
+ const Adapter = mod.default ?? mod[Object.keys(mod)[0]]
31
+ const optKey = COLLECTION_KEY[pkg]
32
+ const adapter = col && optKey ? new Adapter({ url: conn, uri: conn, [optKey]: col }) : new Adapter(conn)
33
+ adapter.setMaxListeners?.(0)
34
+ if (namespace) adapter.namespace = namespace
35
+ _adapterCache.set(ck, adapter)
36
+ return adapter
37
+ }
38
+
39
+ const resolveStore = async (backend, col, namespace) => {
40
+ if (backend == null) return null
41
+ if (typeof backend === 'string') return resolveAdapter(backend, col, namespace)
42
+ if (backend instanceof Keyv) return backend.opts?.store ?? backend.store ?? null
43
+ const { compatible } = detectKeyvStorage(backend)
44
+ if (!compatible) throw new Error('[Auth] backend must be a connection string, Keyv instance, or KeyvStoreAdapter')
45
+ // scope namespace via prototype so we don't mutate the original store instance
46
+ const scoped = Object.create(backend)
47
+ if (namespace) scoped.namespace = namespace
48
+ return scoped
49
+ }
50
+
51
+ const makeKeyv = (store, namespace) => {
52
+ const keyv = store ? new Keyv({ store, namespace }) : new Keyv({ namespace })
53
+ keyv.setMaxListeners?.(0)
54
+ return keyv
55
+ }
56
+
57
+ // ── Multi-backend support ─────────────────────────────────────────────────────
58
+
59
+ // Multi-backend map: { 'creds': pgBackend, 'pre-key': mongoBackend, ... }
60
+ // Anything else (string, Keyv, raw store adapter) is treated as single backend
61
+ const isMultiBackend = backend =>
62
+ backend !== null
63
+ && typeof backend === 'object'
64
+ && !Array.isArray(backend)
65
+ && !(backend instanceof Keyv)
66
+ && !detectKeyvStorage(backend).compatible
67
+
68
+ // Returns { get(type) → Keyv, all → Keyv[] }
69
+ // get(type) falls back to first entry if type not in map
70
+ // all is deduplicated — shared backends only clear once
71
+ const resolveBackendMap = async (backend, sessionId, collectionName) => {
72
+ if (!isMultiBackend(backend)) {
73
+ const store = await resolveStore(backend, collectionName, sessionId)
74
+ const keyv = makeKeyv(store, sessionId)
75
+ return { get: () => keyv, all: [keyv] }
76
+ }
77
+ const entries = Object.entries(backend)
78
+ if (!entries.length) throw new Error('[Auth] backend map must have at least one entry')
79
+ const resolved = new Map()
80
+ for (const [, b] of entries) {
81
+ if (!resolved.has(b)) {
82
+ const store = await resolveStore(b, collectionName, sessionId)
83
+ resolved.set(b, makeKeyv(store, sessionId))
84
+ }
85
+ }
86
+ const typeMap = Object.fromEntries(entries.map(([type, b]) => [type, resolved.get(b)]))
87
+ const defaultKeyv = resolved.get(entries[0][1])
88
+ return { get: type => typeMap[type] ?? defaultKeyv, all: [...new Set(resolved.values())] }
89
+ }
90
+
91
+ // ── Keyv-backed auth state ────────────────────────────────────────────────────
92
+
93
+ export const useKeyvAuthState = async (sessionId, backend, opts = {}) => {
94
+ const { preKeyRetention = DEFAULT_PREKEY_RETENTION, cleanupThreshold = DEFAULT_CLEANUP_THRESHOLD, collectionName = DEFAULT_COLLECTION_NAME } = opts
95
+ const bmap = await resolveBackendMap(backend, sessionId, collectionName)
96
+ const credsKeyv = bmap.get('creds')
97
+ credsKeyv.on('error', err => logger.warn({ err: err?.message, sessionId }, '[Auth] store error'))
98
+
99
+ const kn = (type, id) => `${type}-${id}`
100
+ const credsKey = 'creds'
101
+ let creds = bootstrapCreds(deser(await credsKeyv.get(credsKey)))
102
+ let cleanupRunning = false, lastCleanupAt = 0, lastCleanedId = creds.nextPreKeyId
103
+
104
+ const cleanOldPreKeys = async () => {
105
+ if (cleanupRunning || Date.now() - lastCleanupAt < CLEANUP_INTERVAL_MS) return
106
+ cleanupRunning = true
107
+ try {
108
+ const minId = creds.nextPreKeyId - preKeyRetention
109
+ if (minId <= 0) return
110
+ const ids = []
111
+ for (let id = Math.max(0, lastCleanedId - preKeyRetention); id < minId; id++) ids.push(kn('pre-key', id))
112
+ if (ids.length) await bmap.get('pre-key').deleteMany(ids)
113
+ lastCleanupAt = Date.now(); lastCleanedId = creds.nextPreKeyId
114
+ } catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] prekey cleanup failed') }
115
+ finally { cleanupRunning = false }
116
+ }
117
+ cleanOldPreKeys().catch(() => { })
118
+
119
+ const keys = {
120
+ get: async (type, ids) => {
121
+ if (!ids.length) return {}
122
+ const keyv = bmap.get(type)
123
+ const raws = await keyv.getMany(ids.map(id => kn(type, id)))
124
+ return Object.fromEntries(ids.map((id, i) => [id, patchAppStateKey(type, deser(raws[i]))]))
125
+ },
126
+ set: async data => {
127
+ // group by Keyv instance to batch writes per backend
128
+ const byKeyv = new Map()
129
+ for (const type in data) {
130
+ const keyv = bmap.get(type)
131
+ if (!byKeyv.has(keyv)) byKeyv.set(keyv, { sets: [], dels: [] })
132
+ const bucket = byKeyv.get(keyv)
133
+ for (const id in data[type]) {
134
+ const v = data[type][id], k = kn(type, id)
135
+ if (v != null) bucket.sets.push({ key: k, value: ser(v) })
136
+ else bucket.dels.push(k)
137
+ }
138
+ }
139
+ await Promise.all([...byKeyv.entries()].map(([keyv, { sets, dels }]) =>
140
+ Promise.all([sets.length ? keyv.setMany(sets) : null, dels.length ? keyv.deleteMany(dels) : null])
141
+ ))
142
+ }
143
+ }
144
+
145
+ const saveCreds = async () => {
146
+ if (creds.nextPreKeyId - lastCleanedId >= cleanupThreshold) cleanOldPreKeys().catch(() => { })
147
+ await credsKeyv.set(credsKey, ser(creds))
148
+ }
149
+
150
+ const clearSession = async () => {
151
+ try { await Promise.all(bmap.all.map(keyv => keyv.clear())) }
152
+ catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] clearSession failed') }
153
+ bmap.all.forEach(keyv => keyv.removeAllListeners?.())
154
+ }
155
+
156
+ return { state: { creds, keys }, saveCreds, clearSession }
157
+ }
158
+
159
+ // Lightweight check — reads only the creds key without full state init
160
+ export const hasKeyvAuth = async (sessionId, backend, opts = {}) => {
161
+ try {
162
+ const b = isMultiBackend(backend) ? Object.values(backend)[0] : backend
163
+ const bmap = await resolveBackendMap(b, sessionId, opts.collectionName ?? DEFAULT_COLLECTION_NAME)
164
+ const parsed = deser(await bmap.get('creds').get('creds'))
165
+ return !!(parsed?.noiseKey && parsed?.signedIdentityKey)
166
+ } catch { return false }
167
+ }
168
+
169
+ export { Keyv }
@@ -1,121 +1,10 @@
1
1
  import { mkdir, readFile, rename, stat, unlink, writeFile, readdir } from 'fs/promises'
2
2
  import { join } from 'path'
3
- import { Keyv, detectKeyvStorage } from 'keyv'
4
- import { proto } from '../../WAProto/index.js'
5
- import { initAuthCreds } from './auth-utils.js'
3
+ import { DEFAULT_PREKEY_RETENTION, DEFAULT_CLEANUP_THRESHOLD, CLEANUP_INTERVAL_MS } from '../Defaults/index.js'
4
+ import { bootstrapCreds, patchAppStateKey } from './auth-utils.js'
6
5
  import { BufferJSON } from './generics.js'
7
- import { PROTOCOL_ADAPTERS } from '../WABinary/index.js'
8
6
  import logger from './logger.js'
9
7
 
10
- const DEFAULT_PREKEY_RETENTION = 150
11
- const DEFAULT_CLEANUP_THRESHOLD = 50
12
- const CLEANUP_INTERVAL_MS = 10 * 60 * 1000
13
- const DEFAULT_COLLECTION_NAME = 'zervonex'
14
-
15
- // ── Helpers ───────────────────────────────────────────────────────────────────
16
-
17
- const bootstrapCreds = raw => raw ?? initAuthCreds()
18
- const patchAppStateKey = (type, v) => type === 'app-state-sync-key' && v ? proto.Message.AppStateSyncKeyData.fromObject(v) : v
19
- const ser = v => v == null ? null : JSON.parse(JSON.stringify(v, BufferJSON.replacer))
20
- const deser = raw => {
21
- if (raw == null) return null
22
- let p = typeof raw === 'string'
23
- ? (() => { try { return JSON.parse(raw) } catch { return raw } })() // ← return raw string on parse fail, don't warn
24
- : raw
25
- // some adapters double-wrap in { value: { value: ... } } — unwrap up to 3 levels
26
- let depth = 0
27
- while (depth++ < 3 && p && typeof p === 'object' && !Array.isArray(p) && Object.keys(p).length === 1 && 'value' in p) p = p.value
28
- if (p == null) return null
29
- if (typeof p === 'object' && '__checksum' in p && 'data' in p) p = p.data
30
- return JSON.parse(JSON.stringify(p), BufferJSON.reviver)
31
- }
32
-
33
- // ── Adapter resolution ────────────────────────────────────────────────────────
34
-
35
- // Only these adapters accept a named collection/table — redis, valkey, etcd etc do not
36
- const COLLECTION_KEY = { '@keyv/mongo': 'collection', '@keyv/postgres': 'table', '@keyv/mysql': 'table', '@keyv/sqlite': 'table' }
37
-
38
- // Cached per conn::col::namespace — each session gets its own adapter instance so
39
- // clear() never bleeds across sessions. Underlying drivers manage their own connection
40
- // pools so multiple adapters on the same URI still share one real connection.
41
- const _adapterCache = new Map()
42
- export const clearAdapterCache = () => _adapterCache.clear()
43
-
44
- const resolveAdapter = async (conn, col, namespace) => {
45
- const ck = `${conn}::${col ?? ''}::${namespace ?? ''}`
46
- if (_adapterCache.has(ck)) return _adapterCache.get(ck)
47
- let protocol
48
- try { protocol = new URL(conn).protocol } catch { throw new Error(`[Auth] Invalid connection string: "${conn}"`) }
49
- const pkg = PROTOCOL_ADAPTERS[protocol]
50
- if (!pkg) throw new Error(`[Auth] Unsupported protocol "${protocol}" — supported: ${Object.keys(PROTOCOL_ADAPTERS).map(p => p.replace(':', '')).join(', ')}`)
51
- let mod
52
- try { mod = await import(pkg) } catch (e) {
53
- if (e.code === 'ERR_MODULE_NOT_FOUND') throw new Error(`[Auth] "${pkg}" required — npm i ${pkg}`)
54
- throw e
55
- }
56
- const Adapter = mod.default ?? mod[Object.keys(mod)[0]]
57
- const optKey = COLLECTION_KEY[pkg]
58
- const adapter = col && optKey ? new Adapter({ url: conn, uri: conn, [optKey]: col }) : new Adapter(conn)
59
- adapter.setMaxListeners?.(0)
60
- if (namespace) adapter.namespace = namespace
61
- _adapterCache.set(ck, adapter)
62
- return adapter
63
- }
64
-
65
- const resolveStore = async (backend, col, namespace) => {
66
- if (backend == null) return null
67
- if (typeof backend === 'string') return resolveAdapter(backend, col, namespace)
68
- if (backend instanceof Keyv) return backend.opts?.store ?? backend.store ?? null
69
- const { compatible } = detectKeyvStorage(backend)
70
- if (!compatible) throw new Error('[Auth] backend must be a connection string, Keyv instance, or KeyvStoreAdapter')
71
- // scope namespace via prototype so we don't mutate the original store instance
72
- const scoped = Object.create(backend)
73
- if (namespace) scoped.namespace = namespace
74
- return scoped
75
- }
76
-
77
- const makeKeyv = (store, namespace) => {
78
- const keyv = store ? new Keyv({ store, namespace }) : new Keyv({ namespace })
79
- keyv.setMaxListeners?.(0)
80
- return keyv
81
- }
82
-
83
- // ── Multi-backend support ─────────────────────────────────────────────────────
84
-
85
- // Multi-backend map: { 'creds': pgBackend, 'pre-key': mongoBackend, ... }
86
- // Anything else (string, Keyv, raw store adapter) is treated as single backend
87
- const isMultiBackend = backend =>
88
- backend !== null
89
- && typeof backend === 'object'
90
- && !Array.isArray(backend)
91
- && !(backend instanceof Keyv)
92
- && !detectKeyvStorage(backend).compatible
93
-
94
- // Returns { get(type) → Keyv, all → Keyv[] }
95
- // get(type) falls back to first entry if type not in map
96
- // all is deduplicated — shared backends only clear once
97
- const resolveBackendMap = async (backend, sessionId, collectionName) => {
98
- if (!isMultiBackend(backend)) {
99
- const store = await resolveStore(backend, collectionName, sessionId)
100
- const keyv = makeKeyv(store, sessionId)
101
- return { get: () => keyv, all: [keyv] }
102
- }
103
- const entries = Object.entries(backend)
104
- if (!entries.length) throw new Error('[Auth] backend map must have at least one entry')
105
- const resolved = new Map()
106
- for (const [, b] of entries) {
107
- if (!resolved.has(b)) {
108
- const store = await resolveStore(b, collectionName, sessionId)
109
- resolved.set(b, makeKeyv(store, sessionId))
110
- }
111
- }
112
- const typeMap = Object.fromEntries(entries.map(([type, b]) => [type, resolved.get(b)]))
113
- const defaultKeyv = resolved.get(entries[0][1])
114
- return { get: type => typeMap[type] ?? defaultKeyv, all: [...new Set(resolved.values())] }
115
- }
116
-
117
- // ── File-based auth state ─────────────────────────────────────────────────────
118
-
119
8
  export const useMultiFileAuthState = async (folder, opts = {}) => {
120
9
  const { preKeyRetention = DEFAULT_PREKEY_RETENTION, cleanupThreshold = DEFAULT_CLEANUP_THRESHOLD } = opts
121
10
  const fix = f => f?.replace(/\//g, '__')?.replace(/:/g, '-')
@@ -192,84 +81,4 @@ export const useMultiFileAuthState = async (folder, opts = {}) => {
192
81
  return { totalFiles: files.length, preKeyCount: files.filter(f => /^pre-key-\d+\.json$/.test(f)).length, nextPreKeyId: creds.nextPreKeyId, lastCleanupAt: lastCleanupAt ? new Date(lastCleanupAt).toISOString() : null }
193
82
  }
194
83
  }
195
- }
196
-
197
- // ── Keyv-backed auth state ────────────────────────────────────────────────────
198
-
199
- export const useKeyvAuthState = async (sessionId, backend, opts = {}) => {
200
- const { preKeyRetention = DEFAULT_PREKEY_RETENTION, cleanupThreshold = DEFAULT_CLEANUP_THRESHOLD, collectionName = DEFAULT_COLLECTION_NAME } = opts
201
- const bmap = await resolveBackendMap(backend, sessionId, collectionName)
202
- const credsKeyv = bmap.get('creds')
203
- credsKeyv.on('error', err => logger.warn({ err: err?.message, sessionId }, '[Auth] store error'))
204
-
205
- const kn = (type, id) => `${type}-${id}`
206
- const credsKey = 'creds'
207
- let creds = bootstrapCreds(deser(await credsKeyv.get(credsKey)))
208
- let cleanupRunning = false, lastCleanupAt = 0, lastCleanedId = creds.nextPreKeyId
209
-
210
- const cleanOldPreKeys = async () => {
211
- if (cleanupRunning || Date.now() - lastCleanupAt < CLEANUP_INTERVAL_MS) return
212
- cleanupRunning = true
213
- try {
214
- const minId = creds.nextPreKeyId - preKeyRetention
215
- if (minId <= 0) return
216
- const ids = []
217
- for (let id = Math.max(0, lastCleanedId - preKeyRetention); id < minId; id++) ids.push(kn('pre-key', id))
218
- if (ids.length) await bmap.get('pre-key').deleteMany(ids)
219
- lastCleanupAt = Date.now(); lastCleanedId = creds.nextPreKeyId
220
- } catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] prekey cleanup failed') }
221
- finally { cleanupRunning = false }
222
- }
223
- cleanOldPreKeys().catch(() => { })
224
-
225
- const keys = {
226
- get: async (type, ids) => {
227
- if (!ids.length) return {}
228
- const keyv = bmap.get(type)
229
- const raws = await keyv.getMany(ids.map(id => kn(type, id)))
230
- return Object.fromEntries(ids.map((id, i) => [id, patchAppStateKey(type, deser(raws[i]))]))
231
- },
232
- set: async data => {
233
- // group by Keyv instance to batch writes per backend
234
- const byKeyv = new Map()
235
- for (const type in data) {
236
- const keyv = bmap.get(type)
237
- if (!byKeyv.has(keyv)) byKeyv.set(keyv, { sets: [], dels: [] })
238
- const bucket = byKeyv.get(keyv)
239
- for (const id in data[type]) {
240
- const v = data[type][id], k = kn(type, id)
241
- if (v != null) bucket.sets.push({ key: k, value: ser(v) })
242
- else bucket.dels.push(k)
243
- }
244
- }
245
- await Promise.all([...byKeyv.entries()].map(([keyv, { sets, dels }]) =>
246
- Promise.all([sets.length ? keyv.setMany(sets) : null, dels.length ? keyv.deleteMany(dels) : null])
247
- ))
248
- }
249
- }
250
-
251
- const saveCreds = async () => {
252
- if (creds.nextPreKeyId - lastCleanedId >= cleanupThreshold) cleanOldPreKeys().catch(() => { })
253
- await credsKeyv.set(credsKey, ser(creds))
254
- }
255
-
256
- const clearSession = async () => {
257
- try { await Promise.all(bmap.all.map(keyv => keyv.clear())) }
258
- catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] clearSession failed') }
259
- bmap.all.forEach(keyv => keyv.removeAllListeners?.())
260
- }
261
-
262
- return { state: { creds, keys }, saveCreds, clearSession }
263
- }
264
-
265
- // Lightweight check — reads only the creds key without full state init
266
- export const hasKeyvAuth = async (sessionId, backend, opts = {}) => {
267
- try {
268
- const b = isMultiBackend(backend) ? Object.values(backend)[0] : backend
269
- const bmap = await resolveBackendMap(b, sessionId, opts.collectionName ?? DEFAULT_COLLECTION_NAME)
270
- const parsed = deser(await bmap.get('creds').get('creds'))
271
- return !!(parsed?.noiseKey && parsed?.signedIdentityKey)
272
- } catch { return false }
273
- }
274
-
275
- export { Keyv }
84
+ }
package/lib/index.js CHANGED
@@ -29,7 +29,7 @@ const info = [
29
29
  '╭──────────────────────────────────────────────────────────────╮',
30
30
  '│ │',
31
31
  '│ ◆ Package › @zervonex/baileys │',
32
- '│ ◆ Version › 3.0.9 │',
32
+ '│ ◆ Version › 4.0.0 │',
33
33
  '│ ◆ Status › Production Ready │',
34
34
  '│ │',
35
35
  '├──────────────────────────── ◇◆◇ ─────────────────────────────┤',
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@zervonex/baileys",
4
4
  "type": "module",
5
- "version": "3.0.9",
5
+ "version": "4.0.0",
6
6
  "description": "Advanced WhatsApp Web API built on Baileys — interactive messages, buttons, carousels, products, events, payments, polls, albums, sticker packs and more.",
7
7
  "keywords": [
8
8
  "baileys",