@vowifi-rs/vowifi 0.1.13 → 0.2.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
@@ -1,10 +1,10 @@
1
1
  # @vowifi-rs/vowifi
2
2
 
3
- Hardware-independent VoWiFi SMS sessions for Node.js.
3
+ Hardware-independent VoWiFi SMS and voice sessions for Node.js.
4
4
 
5
5
  `@vowifi-rs/vowifi` connects a SIM subscription to its operator's ePDG, establishes an
6
- IKEv2/EAP-AKA/IPsec tunnel, registers with IMS, and sends or receives SMS over Wi-Fi. The package
7
- exposes a small session API and delegates only SIM authentication to the host application.
6
+ IKEv2/EAP-AKA/IPsec tunnel, registers with IMS, and exchanges SMS or voice media over Wi-Fi. The
7
+ package exposes a small session API and delegates only SIM authentication to the host application.
8
8
 
9
9
  > **Project status:** experimental. Mobile-originated and mobile-terminated SMS have completed an
10
10
  > end-to-end commercial-network verification. Interoperability still depends on the operator,
@@ -49,10 +49,22 @@ exposes a small session API and delegates only SIM authentication to the host ap
49
49
  - Text and binary incoming SMS events.
50
50
  - International numbers, national numbers, and operator short codes.
51
51
 
52
+ **IMS voice and media**
53
+
54
+ - Single-call outgoing and incoming IMS SIP lifecycle, including reliable provisional responses,
55
+ PRACK, ACK, CANCEL, rejection, and BYE.
56
+ - Automatic SDP offer/answer negotiation for octet-aligned AMR-NB and AMR-WB.
57
+ - Userspace RTP endpoint with bounded reordering, packet-loss concealment, 20 ms pacing, and
58
+ AMR/PCM/Opus conversion.
59
+ - Periodic RTCP SR/RR + SDES, RTCP BYE cleanup, and call-scoped media statistics.
60
+ - Call-scoped mono Opus `Duplex` stream at 8 kHz or 16 kHz with one 20 ms packet per chunk.
61
+
52
62
  **Node.js API**
53
63
 
54
64
  - Hardware-independent async AKA callback; the addon does not own a modem or serial port.
55
- - Pull-based event delivery backed by a bounded queue.
65
+ - EventEmitter-based control delivery through a bounded native queue; no application polling loop.
66
+ - Bounded call-audio `Duplex` with a one-write-per-Opus-packet contract and aggregated drop events.
67
+ - On-demand `call.audioStats()` snapshots without a Node.js telemetry loop.
56
68
  - Structured session states and stable JavaScript error codes.
57
69
  - Built-in carrier-profile inspection through `resolveCarrierProfile()`.
58
70
  - Optional Rust `tracing` subscriber through `initLogging()`.
@@ -68,6 +80,7 @@ exposes a small session API and delegates only SIM authentication to the host ap
68
80
  SMS at the SIP layer and returns the requested service response, but this does not cover every
69
81
  operator's RP behavior.
70
82
  - IPv6-only ePDG, Child SA, and IMS deployments. Current built-in profiles use IPv4.
83
+ - Real-operator outgoing and incoming voice, early media, and bidirectional AMR-NB/AMR-WB audio.
71
84
 
72
85
  ### Not implemented
73
86
 
@@ -84,17 +97,18 @@ exposes a small session API and delegates only SIM authentication to the host ap
84
97
  authentication, or a user interface.
85
98
  - Remote carrier-profile updates or a comprehensive carrier configuration database.
86
99
  - Native binaries outside the platforms listed below.
100
+ - EVS, SRTP toward applications, conferencing, transfer, and multiple simultaneous calls.
87
101
 
88
102
  ### Planned or possible future work
89
103
 
90
- - MMTel voice calling, SIP call control, RTP/RTCP media, and host-provided audio-device bridging.
104
+ - Real-network voice interoperability and aggregated RTCP/media diagnostics.
91
105
  - Additional carrier profiles and real-network interoperability coverage.
92
106
  - EAP-AKA', certificate-authenticated ePDG deployments, and broader IPsec transform support as
93
107
  required by verified carrier configurations.
94
108
  - Additional native platforms and higher-level integrations.
95
109
 
96
- Voice is not part of the current API, but it is a potential extension of the existing ePDG, IPsec,
97
- IMS registration, and session-lifecycle foundation rather than a permanently excluded feature.
110
+ Mobile audio capture/playback, WebSocket transport, AEC, AGC, noise suppression, and system call UI
111
+ remain responsibilities of the host application and mobile client.
98
112
 
99
113
  ## Interoperability
100
114
 
@@ -165,6 +179,12 @@ const session = await VowifiSession.connect({
165
179
  try {
166
180
  console.log('online using carrier profile:', session.carrierProfileId)
167
181
 
182
+ session.on('sms', message => {
183
+ if (message.content.kind === 'text') {
184
+ console.log(`SMS from ${message.sender}: ${message.content.text}`)
185
+ }
186
+ })
187
+
168
188
  const result = await session.sendSms({
169
189
  recipient: '+1555...',
170
190
  text: 'Hello over VoWiFi',
@@ -172,18 +192,6 @@ try {
172
192
  })
173
193
 
174
194
  console.log('send result:', result)
175
-
176
- for (;;) {
177
- const event = await session.nextEvent()
178
- if (!event) break
179
-
180
- if (event.kind === 'incomingSms') {
181
- const { sender, content } = event.message
182
- if (content.kind === 'text') {
183
- console.log(`SMS from ${sender}: ${content.text}`)
184
- }
185
- }
186
- }
187
195
  } finally {
188
196
  await session.shutdown()
189
197
  }
@@ -318,9 +326,65 @@ egress: {
318
326
 
319
327
  The proxy must support the SOCKS5 UDP association needed by IKE/IPsec traffic.
320
328
 
329
+ ## Voice Calls and Opus Audio
330
+
331
+ Carrier codec preference and bounded application-audio buffering are selected at connect time:
332
+
333
+ ```js
334
+ const session = await VowifiSession.connect({
335
+ subscription,
336
+ aka,
337
+ voice: {
338
+ preferredCodecs: ['AMR-WB', 'AMR'],
339
+ uplinkBufferMs: 200,
340
+ downlinkBufferMs: 200,
341
+ },
342
+ })
343
+
344
+ function attachCall(call) {
345
+ call.on('state', state => console.log('call state:', state))
346
+ call.on('audio', audio => attachAudio(audio))
347
+ call.on('ended', result => console.log('call ended:', result))
348
+ }
349
+
350
+ function attachAudio(audio) {
351
+ console.log(audio.callId, audio.format)
352
+ audio.on('data', opusPacket => sendBinaryAudioToClient(opusPacket))
353
+ clientAudio.on('data', opusPacket => audio.write(opusPacket))
354
+ audio.on('audioDrop', ({ direction, frames }) => {
355
+ console.warn('real-time audio dropped', { direction, frames })
356
+ })
357
+ }
358
+
359
+ session.on('incomingCall', async call => {
360
+ attachCall(call)
361
+ await call.ring()
362
+ await call.answer()
363
+ })
364
+
365
+ const call = await session.dial({ number: '+15551234567' })
366
+ attachCall(call)
367
+ console.log(call.audioStats())
368
+ ```
369
+
370
+ Each readable chunk and writable write carries one complete 20 ms mono Opus packet. The negotiated
371
+ sample rate is 8 kHz for AMR-NB or 16 kHz for AMR-WB; the application profile uses 20 kbps with FEC
372
+ and DTX disabled initially. Internal PCM does not cross the addon boundary. The host owns WebSocket transport,
373
+ mobile audio capture/playback, echo cancellation, system call UI, and policy for hanging up when a
374
+ remote audio connection disappears.
375
+
321
376
  ## Receiving Events
322
377
 
323
- `nextEvent()` waits for the next event and returns `null` after the event stream closes:
378
+ `VowifiSession` is an `EventEmitter`. Listen to focused events or the complete `event` stream:
379
+
380
+ ```js
381
+ session.on('sms', message => console.log('incoming SMS:', message))
382
+ session.on('smsStatus', event => console.log('outgoing SMS update:', event))
383
+ session.on('incomingCall', call => console.log('incoming call:', call.remoteUri))
384
+ session.on('event', event => console.log('all protocol events:', event))
385
+ ```
386
+
387
+ The complete event union is:
324
388
 
325
389
  ```ts
326
390
  type VowifiEvent =
@@ -331,6 +395,7 @@ type VowifiEvent =
331
395
  | { kind: 'outgoingSmsRpConfirmed'; transactionId: string }
332
396
  | { kind: 'outgoingSmsFailed'; transactionId: string; rpCause: number; rpDiagnostic: Buffer }
333
397
  | { kind: 'outgoingSmsReportTimeout'; transactionId: string }
398
+ | { kind: 'call'; call: CallEvent }
334
399
  ```
335
400
 
336
401
  Incoming SMS messages contain the sender, PID, DCS, the raw service-centre timestamp, and either
@@ -348,9 +413,9 @@ interface SmsMessage {
348
413
  }
349
414
  ```
350
415
 
351
- Multipart incoming messages are reassembled before `incomingSms` is emitted. Consume events
352
- continuously while the session is online so application processing does not fill the bounded event
353
- queue.
416
+ Multipart incoming messages are reassembled before the `sms` event is emitted. Native event
417
+ delivery is bounded; listeners should hand off expensive work rather than block the Node.js event
418
+ loop.
354
419
 
355
420
  ## Sending SMS
356
421
 
@@ -366,7 +431,7 @@ International numbers, national numbers, and operator short codes are accepted.
366
431
  into multipart SMS automatically.
367
432
 
368
433
  `sendSms()` completes as soon as every part receives a final SIP `2xx` response. RP-level reports
369
- arrive later through `nextEvent()` and never keep the send promise pending:
434
+ arrive later through the `smsStatus` event and never keep the send promise pending:
370
435
 
371
436
  ```ts
372
437
  interface SmsSendResult {
@@ -462,12 +527,28 @@ Use `code` for program flow. Error messages are diagnostic text and may change b
462
527
  class VowifiSession {
463
528
  static connect(options: ConnectOptions): Promise<VowifiSession>
464
529
  readonly carrierProfileId: string
530
+ readonly activeCall?: VowifiCall
465
531
  status(): SessionState
466
- nextEvent(): Promise<VowifiEvent | null>
532
+ on(event: 'event' | 'state' | 'sms' | 'smsStatus' | 'incomingCall', listener): this
467
533
  sendSms(message: SmsSendOptions): Promise<SmsSendResult>
534
+ dial(options: { number: string }): Promise<VowifiCall>
468
535
  shutdown(): Promise<void>
469
536
  }
470
537
 
538
+ class VowifiCall {
539
+ readonly id: string
540
+ readonly direction: 'incoming' | 'outgoing'
541
+ readonly remoteNumber?: string
542
+ readonly remoteUri?: string
543
+ readonly state: CallState
544
+ readonly audio?: CallAudioStream
545
+ audioStats(): CallAudioStats
546
+ ring(): Promise<void>
547
+ answer(): Promise<void>
548
+ reject(status?: number): Promise<void>
549
+ hangup(): Promise<void>
550
+ }
551
+
471
552
  function resolveCarrierProfile(options: ResolveCarrierProfileOptions): ResolvedCarrierProfile
472
553
  function initLogging(filter?: string): boolean
473
554
  ```
package/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  /// <reference types="node" />
2
2
 
3
+ import { EventEmitter } from 'node:events'
4
+ import { Duplex } from 'node:stream'
5
+
3
6
  export interface SubscriptionIdentity {
4
7
  imsi: string
5
8
  mcc: string
@@ -25,6 +28,7 @@ export interface OperationalConfig {
25
28
  ikeDpdIntervalMs?: number
26
29
  ikeDpdResponseTimeoutMs?: number
27
30
  ikeDpdMaxMissedProbes?: number
31
+ natKeepaliveIntervalMs?: number
28
32
  registrationTimeoutMs?: number
29
33
  refreshAtPercent?: number
30
34
  recoveryAttempts?: number
@@ -53,9 +57,18 @@ export interface ConnectOptions {
53
57
  explicitEpdgHostnames?: string[]
54
58
  egress?: NetworkEgress
55
59
  operational?: OperationalConfig
60
+ voice?: VoiceConfig
56
61
  aka(request: AkaRequest): Promise<AkaResult>
57
62
  }
58
63
 
64
+ export type VoiceCodec = 'AMR-WB' | 'AMR'
65
+
66
+ export interface VoiceConfig {
67
+ preferredCodecs?: VoiceCodec[]
68
+ uplinkBufferMs?: number
69
+ downlinkBufferMs?: number
70
+ }
71
+
59
72
  export type SessionState =
60
73
  | { kind: 'stopped' | 'connecting' | 'online' | 'recovering' | 'stopping' }
61
74
  | { kind: 'failed'; message?: string }
@@ -80,6 +93,113 @@ export type VowifiEvent =
80
93
  | { kind: 'outgoingSmsRpConfirmed'; transactionId: string }
81
94
  | { kind: 'outgoingSmsFailed'; transactionId: string; rpCause: number; rpDiagnostic: Buffer }
82
95
  | { kind: 'outgoingSmsReportTimeout'; transactionId: string }
96
+ | { kind: 'call'; call: CallEvent }
97
+
98
+ export type CallEvent =
99
+ | { kind: 'dialing'; callId: string }
100
+ | { kind: 'incoming'; callId: string; callerUri: string }
101
+ | { kind: 'ringing'; callId: string; status: number }
102
+ | { kind: 'earlyMedia'; callId: string }
103
+ | { kind: 'established'; callId: string }
104
+ | {
105
+ kind: 'ended'
106
+ callId: string
107
+ endReason: 'rejected' | 'cancelled' | 'localHangup' | 'remoteHangup' | 'networkLost'
108
+ status?: number
109
+ }
110
+
111
+ export type CallDirection = 'incoming' | 'outgoing'
112
+
113
+ export type CallState =
114
+ | 'incoming'
115
+ | 'dialing'
116
+ | 'ringing'
117
+ | 'earlyMedia'
118
+ | 'established'
119
+ | 'terminating'
120
+ | 'ended'
121
+
122
+ export type CallEndReason =
123
+ | 'rejected'
124
+ | 'cancelled'
125
+ | 'localHangup'
126
+ | 'remoteHangup'
127
+ | 'networkLost'
128
+
129
+ export interface CallEndResult {
130
+ reason: CallEndReason
131
+ status?: number
132
+ }
133
+
134
+ export interface DialOptions {
135
+ number: string
136
+ }
137
+
138
+ export interface OpusFormat {
139
+ readonly encoding: 'opus'
140
+ readonly sampleRate: 8000 | 16000
141
+ readonly channels: 1
142
+ readonly frameDurationMs: 20
143
+ readonly bitrate: 20000
144
+ readonly maxPacketBytes: 1275
145
+ }
146
+
147
+ export interface CallAudioStats {
148
+ callId?: string
149
+ active: boolean
150
+ carrierCodec?: 'AMR' | 'AMR-WB' | string
151
+ carrierRtpReceived: number
152
+ carrierRtpSent: number
153
+ carrierRtpLost: number
154
+ carrierRtpDuplicates: number
155
+ carrierRtpOutOfOrder: number
156
+ carrierJitterTimestampUnits: number
157
+ carrierJitterMs: number
158
+ carrierRejectedPackets: number
159
+ concealedFrames: number
160
+ codecFailures: number
161
+ mediaUplinkQueueDrops: number
162
+ mediaDownlinkQueueDrops: number
163
+ applicationUplinkDrops: number
164
+ applicationDownlinkDrops: number
165
+ nativeDispatchDrops: number
166
+ uplinkUnderruns: number
167
+ rtcpSent: number
168
+ rtcpReceived: number
169
+ rtcpRejected: number
170
+ mediaInactive: boolean
171
+ lastRtpSentAtMs?: number
172
+ lastRtpReceivedAtMs?: number
173
+ }
174
+
175
+ export class CallAudioStream extends Duplex {
176
+ readonly callId: string
177
+ readonly format: OpusFormat
178
+ on(
179
+ event: 'audioDrop',
180
+ listener: (event: { direction: 'uplink' | 'downlink'; frames: number }) => void,
181
+ ): this
182
+ }
183
+
184
+ export class VowifiCall extends EventEmitter {
185
+ private constructor()
186
+ readonly id: string
187
+ readonly direction: CallDirection
188
+ readonly remoteNumber?: string
189
+ readonly remoteUri?: string
190
+ readonly state: CallState
191
+ readonly audio?: CallAudioStream
192
+ readonly endReason?: CallEndReason
193
+ readonly endStatus?: number
194
+ audioStats(): CallAudioStats
195
+ ring(): Promise<void>
196
+ answer(): Promise<void>
197
+ reject(status?: number): Promise<void>
198
+ hangup(): Promise<void>
199
+ on(event: 'state', listener: (state: CallState) => void): this
200
+ on(event: 'audio', listener: (stream: CallAudioStream) => void): this
201
+ on(event: 'ended', listener: (result: CallEndResult) => void): this
202
+ }
83
203
 
84
204
  export interface SmsSendOptions {
85
205
  recipient: string
@@ -103,13 +223,19 @@ export interface ResolvedCarrierProfile {
103
223
  matchedBy: string[]
104
224
  }
105
225
 
106
- export class VowifiSession {
226
+ export class VowifiSession extends EventEmitter {
107
227
  private constructor()
108
228
  static connect(options: ConnectOptions): Promise<VowifiSession>
109
229
  readonly carrierProfileId: string
230
+ readonly activeCall?: VowifiCall
110
231
  status(): SessionState
111
- nextEvent(): Promise<VowifiEvent | null>
232
+ on(event: 'state', listener: (state: SessionState) => void): this
233
+ on(event: 'sms', listener: (message: SmsMessage) => void): this
234
+ on(event: 'smsStatus', listener: (event: VowifiEvent) => void): this
235
+ on(event: 'incomingCall', listener: (call: VowifiCall) => void): this
236
+ on(event: 'event', listener: (event: VowifiEvent) => void): this
112
237
  sendSms(message: SmsSendOptions): Promise<SmsSendResult>
238
+ dial(options: DialOptions): Promise<VowifiCall>
113
239
  shutdown(): Promise<void>
114
240
  }
115
241
 
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { EventEmitter, once } = require('node:events')
4
+ const { Duplex } = require('node:stream')
3
5
  const native = require('./native.js')
4
6
 
5
7
  function normalizeError(error) {
@@ -20,11 +22,226 @@ async function nativeCall(operation) {
20
22
  }
21
23
  }
22
24
 
23
- class VowifiSession {
25
+ class CallAudioStream extends Duplex {
24
26
  #native
27
+ #maxPacketBytes
28
+ #readableReady = true
29
+ #nativeEnded = false
30
+ #pendingDrops = { uplink: 0, downlink: 0 }
31
+ #dropReportScheduled = false
32
+
33
+ constructor(inner, info) {
34
+ const nominalPacketBytes = Math.ceil(info.bitrate * info.frameDurationMs / 8000)
35
+ super({
36
+ readableHighWaterMark: nominalPacketBytes * 10,
37
+ writableHighWaterMark: nominalPacketBytes * 10,
38
+ })
39
+ this.#native = inner
40
+ this.#maxPacketBytes = info.maxPacketBytes
41
+ this.callId = info.callId
42
+ this.format = Object.freeze({
43
+ encoding: info.encoding,
44
+ sampleRate: info.sampleRate,
45
+ channels: info.channels,
46
+ frameDurationMs: info.frameDurationMs,
47
+ bitrate: info.bitrate,
48
+ maxPacketBytes: info.maxPacketBytes,
49
+ })
50
+ }
51
+
52
+ _read() {
53
+ this.#readableReady = true
54
+ }
55
+
56
+ _write(chunk, encoding, callback) {
57
+ try {
58
+ const input = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding)
59
+ if (input.length === 0 || input.length > this.#maxPacketBytes) {
60
+ throw new RangeError(`each write must contain one Opus packet of 1-${this.#maxPacketBytes} bytes`)
61
+ }
62
+ if (this.#native.tryWriteCallAudio(input)) this.#reportDrop('uplink', 1)
63
+ callback()
64
+ } catch (error) {
65
+ callback(normalizeError(error))
66
+ }
67
+ }
68
+
69
+ _final(callback) {
70
+ callback()
71
+ }
72
+
73
+ _pushNative(frame) {
74
+ if (this.#nativeEnded || this.destroyed || this.readableEnded) return
75
+ if (!this.#readableReady) {
76
+ this.#reportDrop('downlink', 1)
77
+ return
78
+ }
79
+ this.#readableReady = this.push(frame)
80
+ }
81
+
82
+ _endNative() {
83
+ if (this.#nativeEnded) return
84
+ this.#nativeEnded = true
85
+ if (!this.readableEnded) this.push(null)
86
+ if (!this.writableEnded) this.end()
87
+ }
88
+
89
+ _reportNativeDrops(frames) {
90
+ this.#reportDrop('downlink', frames)
91
+ }
92
+
93
+ #reportDrop(direction, frames) {
94
+ this.#pendingDrops[direction] += frames
95
+ if (this.#dropReportScheduled) return
96
+ this.#dropReportScheduled = true
97
+ queueMicrotask(() => {
98
+ this.#dropReportScheduled = false
99
+ for (const current of ['uplink', 'downlink']) {
100
+ const count = this.#pendingDrops[current]
101
+ this.#pendingDrops[current] = 0
102
+ if (count > 0) this.emit('audioDrop', { direction: current, frames: count })
103
+ }
104
+ })
105
+ }
106
+ }
107
+
108
+ class VowifiCall extends EventEmitter {
109
+ #native
110
+ #state
111
+ #audio
112
+
113
+ constructor(inner, { id, direction, remoteNumber, remoteUri, state }) {
114
+ super()
115
+ this.#native = inner
116
+ this.id = id
117
+ this.direction = direction
118
+ this.remoteNumber = remoteNumber
119
+ this.remoteUri = remoteUri
120
+ this.#state = state
121
+ this.endReason = undefined
122
+ this.endStatus = undefined
123
+ }
124
+
125
+ get state() {
126
+ return this.#state
127
+ }
128
+
129
+ get audio() {
130
+ return this.#audio
131
+ }
132
+
133
+ audioStats() {
134
+ const stats = this.#native.callAudioStats()
135
+ return stats.callId === this.id ? stats : { ...stats, callId: this.id, active: false }
136
+ }
137
+
138
+ async ring() {
139
+ this.#requireIncoming('ring', ['incoming'])
140
+ await nativeCall(() => this.#native.ring(this.id))
141
+ if (this.#state === 'incoming') this.#setState('ringing')
142
+ }
143
+
144
+ answer() {
145
+ this.#requireIncoming('answer', ['incoming', 'ringing'])
146
+ return nativeCall(() => this.#native.answer(this.id))
147
+ }
148
+
149
+ async reject(status = 486) {
150
+ this.#requireIncoming('reject', ['incoming', 'ringing'])
151
+ const previousState = this.#state
152
+ this.#setState('terminating')
153
+ try {
154
+ await nativeCall(() => this.#native.reject(this.id, status))
155
+ } catch (error) {
156
+ if (this.#state === 'terminating') this.#setState(previousState)
157
+ throw error
158
+ }
159
+ }
160
+
161
+ async hangup() {
162
+ if (this.#state === 'ended' || this.#state === 'terminating') return
163
+ const previousState = this.#state
164
+ this.#setState('terminating')
165
+ try {
166
+ await nativeCall(() => this.#native.hangup(this.id))
167
+ } catch (error) {
168
+ if (this.#state === 'terminating') this.#setState(previousState)
169
+ throw error
170
+ }
171
+ }
172
+
173
+ _applyNativeEvent(event) {
174
+ if (event.callId !== this.id || this.#state === 'ended') return
175
+ if (event.callerUri && !this.remoteUri) this.remoteUri = event.callerUri
176
+ if (event.kind === 'ended') {
177
+ this._end(event.endReason, event.status)
178
+ return
179
+ }
180
+ this.#setState(event.kind)
181
+ }
182
+
183
+ _openAudio(info) {
184
+ if (this.#audio || this.#state === 'ended') return this.#audio
185
+ this.#audio = new CallAudioStream(this.#native, info)
186
+ this.emit('audio', this.#audio)
187
+ return this.#audio
188
+ }
189
+
190
+ _pushAudio(frame, droppedFrames) {
191
+ if (!this.#audio) return
192
+ if (droppedFrames > 0) this.#audio._reportNativeDrops(droppedFrames)
193
+ if (frame) this.#audio._pushNative(frame)
194
+ }
195
+
196
+ _close() {
197
+ this.#closeAudio()
198
+ }
199
+
200
+ _end(reason, status) {
201
+ if (this.#state === 'ended') return
202
+ this.endReason = reason
203
+ this.endStatus = status
204
+ this.#setState('ended')
205
+ const result = Object.freeze({ reason, status })
206
+ this.emit('ended', result)
207
+ this.#closeAudio()
208
+ }
209
+
210
+ #setState(state) {
211
+ if (this.#state === state) return
212
+ this.#state = state
213
+ this.emit('state', state)
214
+ }
215
+
216
+ #requireIncoming(operation, allowedStates) {
217
+ if (this.direction !== 'incoming') {
218
+ throw new Error(`${operation} is only valid for an incoming call`)
219
+ }
220
+ if (!allowedStates.includes(this.#state)) {
221
+ throw new Error(`${operation} is not valid while the call is ${this.#state}`)
222
+ }
223
+ }
224
+
225
+ #closeAudio() {
226
+ if (!this.#audio) return
227
+ this.#audio._endNative()
228
+ this.#audio = undefined
229
+ }
230
+ }
231
+
232
+ class VowifiSession extends EventEmitter {
233
+ #native
234
+ #call = null
235
+ #shutdown = false
25
236
 
26
237
  constructor(inner) {
238
+ super()
27
239
  this.#native = inner
240
+ inner.startEventDispatch(event => this.#dispatch(event))
241
+ inner.startAudioDispatch(({ callId, frame, droppedFrames }) => {
242
+ if (this.#call?.id !== callId) return
243
+ this.#call._pushAudio(frame, droppedFrames)
244
+ })
28
245
  }
29
246
 
30
247
  static async connect(options) {
@@ -42,21 +259,120 @@ class VowifiSession {
42
259
  return this.#native.carrierProfileId
43
260
  }
44
261
 
45
- status() {
46
- return this.#native.status()
262
+ get activeCall() {
263
+ return this.#call?.state === 'ended' ? undefined : this.#call ?? undefined
47
264
  }
48
265
 
49
- nextEvent() {
50
- return nativeCall(() => this.#native.nextEvent())
266
+ status() {
267
+ return this.#native.status()
51
268
  }
52
269
 
53
270
  sendSms(message) {
54
271
  return nativeCall(() => this.#native.sendSms(message))
55
272
  }
56
273
 
57
- shutdown() {
58
- return nativeCall(() => this.#native.shutdown())
274
+ async dial(options) {
275
+ if (this.activeCall) {
276
+ const error = new Error('another call is already active')
277
+ error.code = 'CALL_ALREADY_ACTIVE'
278
+ throw error
279
+ }
280
+ const callId = await nativeCall(() => this.#native.dial(options))
281
+ const call = this.#getOrCreateCall(callId, {
282
+ direction: 'outgoing',
283
+ remoteNumber: options.number.trim(),
284
+ state: 'dialing',
285
+ })
286
+ if (!call) {
287
+ const error = new Error('another call became active while dialing')
288
+ error.code = 'CALL_ALREADY_ACTIVE'
289
+ throw error
290
+ }
291
+ return call
292
+ }
293
+
294
+ async shutdown() {
295
+ if (this.#shutdown) return
296
+ this.#shutdown = true
297
+ const call = this.activeCall
298
+ if (call) {
299
+ try {
300
+ const ended = call.state === 'ended' ? Promise.resolve() : once(call, 'ended')
301
+ if (call.state !== 'terminating') await call.hangup()
302
+ await Promise.race([ended, delay(3_000)])
303
+ } catch {
304
+ // Runtime shutdown remains authoritative when graceful call teardown is unavailable.
305
+ }
306
+ }
307
+ this.#call?._close()
308
+ try {
309
+ await nativeCall(() => this.#native.shutdown())
310
+ } finally {
311
+ if (call?.state !== 'ended') call?._end('localHangup')
312
+ }
313
+ }
314
+
315
+ #dispatch(event) {
316
+ this.emit('event', event)
317
+ switch (event.kind) {
318
+ case 'registered':
319
+ case 'refreshed':
320
+ case 'deregistered':
321
+ this.emit('state', this.status())
322
+ break
323
+ case 'incomingSms':
324
+ this.emit('sms', event.message)
325
+ break
326
+ case 'outgoingSmsRpConfirmed':
327
+ case 'outgoingSmsFailed':
328
+ case 'outgoingSmsReportTimeout':
329
+ this.emit('smsStatus', event)
330
+ break
331
+ case 'call': {
332
+ const details = event.call
333
+ const existingCall = this.#call?.id === details.callId
334
+ const call = this.#getOrCreateCall(details.callId, {
335
+ direction: details.kind === 'incoming' ? 'incoming' : 'outgoing',
336
+ remoteUri: details.callerUri,
337
+ state: details.kind,
338
+ })
339
+ if (!call) {
340
+ break
341
+ }
342
+ call._applyNativeEvent(details)
343
+ if (details.kind === 'incoming' && !existingCall) this.emit('incomingCall', call)
344
+ if (details.kind === 'earlyMedia' || details.kind === 'established') {
345
+ this.#openAudio(call)
346
+ }
347
+ break
348
+ }
349
+ }
59
350
  }
351
+
352
+ #getOrCreateCall(callId, defaults) {
353
+ if (this.#call?.id === callId) {
354
+ if (!this.#call.remoteNumber && defaults.remoteNumber) {
355
+ this.#call.remoteNumber = defaults.remoteNumber
356
+ }
357
+ if (!this.#call.remoteUri && defaults.remoteUri) this.#call.remoteUri = defaults.remoteUri
358
+ return this.#call
359
+ }
360
+ if (this.#call && this.#call.state !== 'ended') {
361
+ return undefined
362
+ }
363
+ this.#call = new VowifiCall(this.#native, { id: callId, ...defaults })
364
+ return this.#call
365
+ }
366
+
367
+ #openAudio(call) {
368
+ const info = this.#native.activeCallAudio()
369
+ if (!info || info.callId !== call.id) return
370
+ call._openAudio(info)
371
+ }
372
+ }
373
+
374
+ function delay(milliseconds) {
375
+ return new Promise(resolve => setTimeout(resolve, milliseconds))
60
376
  }
61
377
 
62
378
  function resolveCarrierProfile(options) {
@@ -75,4 +391,4 @@ function initLogging(filter) {
75
391
  }
76
392
  }
77
393
 
78
- module.exports = { VowifiSession, initLogging, resolveCarrierProfile }
394
+ module.exports = { CallAudioStream, VowifiCall, VowifiSession, initLogging, resolveCarrierProfile }
package/native.d.ts CHANGED
@@ -4,8 +4,17 @@ export declare class NodeVowifiSession {
4
4
  static connect(options: JsConnectOptions, aka: AkaCallback): Promise<NodeVowifiSession>
5
5
  get carrierProfileId(): string
6
6
  status(): JsSessionState
7
- nextEvent(): Promise<JsVowifiEvent | null>
7
+ startEventDispatch(callback: EventCallback): void
8
+ startAudioDispatch(callback: AudioCallback): void
9
+ activeCallAudio(): JsCallAudioInfo | null
10
+ callAudioStats(): JsCallAudioStats
11
+ tryWriteCallAudio(frame: Buffer): boolean
8
12
  sendSms(input: JsSmsSendOptions): Promise<JsSmsSendResult>
13
+ dial(input: JsDialOptions): Promise<string>
14
+ ring(callId: string): Promise<void>
15
+ answer(callId: string): Promise<void>
16
+ reject(callId: string, status: number): Promise<void>
17
+ hangup(callId: string): Promise<void>
9
18
  shutdown(): Promise<void>
10
19
  }
11
20
 
@@ -27,6 +36,58 @@ export interface JsAkaResult {
27
36
  sw2?: number
28
37
  }
29
38
 
39
+ export interface JsAudioDispatch {
40
+ callId: string
41
+ frame?: Buffer
42
+ droppedFrames: number
43
+ }
44
+
45
+ export interface JsCallAudioInfo {
46
+ callId: string
47
+ encoding: string
48
+ sampleRate: number
49
+ channels: number
50
+ frameDurationMs: number
51
+ bitrate: number
52
+ maxPacketBytes: number
53
+ }
54
+
55
+ export interface JsCallAudioStats {
56
+ callId?: string
57
+ active: boolean
58
+ carrierCodec?: string
59
+ carrierRtpReceived: number
60
+ carrierRtpSent: number
61
+ carrierRtpLost: number
62
+ carrierRtpDuplicates: number
63
+ carrierRtpOutOfOrder: number
64
+ carrierJitterTimestampUnits: number
65
+ carrierJitterMs: number
66
+ carrierRejectedPackets: number
67
+ concealedFrames: number
68
+ codecFailures: number
69
+ mediaUplinkQueueDrops: number
70
+ mediaDownlinkQueueDrops: number
71
+ applicationUplinkDrops: number
72
+ applicationDownlinkDrops: number
73
+ nativeDispatchDrops: number
74
+ uplinkUnderruns: number
75
+ rtcpSent: number
76
+ rtcpReceived: number
77
+ rtcpRejected: number
78
+ mediaInactive: boolean
79
+ lastRtpSentAtMs?: number
80
+ lastRtpReceivedAtMs?: number
81
+ }
82
+
83
+ export interface JsCallEvent {
84
+ kind: string
85
+ callId: string
86
+ callerUri?: string
87
+ status?: number
88
+ endReason?: string
89
+ }
90
+
30
91
  export interface JsConnectOptions {
31
92
  subscription: JsSubscriptionIdentity
32
93
  carrierProfile?: string
@@ -34,6 +95,11 @@ export interface JsConnectOptions {
34
95
  explicitEpdgHostnames?: Array<string>
35
96
  egress?: JsNetworkEgress
36
97
  operational?: JsOperationalConfig
98
+ voice?: JsVoiceConfig
99
+ }
100
+
101
+ export interface JsDialOptions {
102
+ number: string
37
103
  }
38
104
 
39
105
  export interface JsNetworkEgress {
@@ -51,6 +117,7 @@ export interface JsOperationalConfig {
51
117
  ikeDpdIntervalMs?: number
52
118
  ikeDpdResponseTimeoutMs?: number
53
119
  ikeDpdMaxMissedProbes?: number
120
+ natKeepaliveIntervalMs?: number
54
121
  registrationTimeoutMs?: number
55
122
  refreshAtPercent?: number
56
123
  recoveryAttempts?: number
@@ -117,6 +184,12 @@ export interface JsSubscriptionIdentity {
117
184
  iccid?: string
118
185
  }
119
186
 
187
+ export interface JsVoiceConfig {
188
+ preferredCodecs?: Array<string>
189
+ uplinkBufferMs?: number
190
+ downlinkBufferMs?: number
191
+ }
192
+
120
193
  export interface JsVowifiEvent {
121
194
  kind: string
122
195
  expiresSeconds?: number
@@ -125,6 +198,7 @@ export interface JsVowifiEvent {
125
198
  transactionId?: string
126
199
  rpCause?: number
127
200
  rpDiagnostic?: Buffer
201
+ call?: JsCallEvent
128
202
  }
129
203
 
130
204
  export declare function resolveCarrierProfile(options: JsResolveCarrierProfileOptions): JsResolvedCarrierProfile
package/native.js CHANGED
@@ -77,8 +77,8 @@ function requireNative() {
77
77
  try {
78
78
  const binding = require('@vowifi-rs/vowifi-android-arm64')
79
79
  const bindingPackageVersion = require('@vowifi-rs/vowifi-android-arm64/package.json').version
80
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
80
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
82
82
  }
83
83
  return binding
84
84
  } catch (e) {
@@ -93,8 +93,8 @@ function requireNative() {
93
93
  try {
94
94
  const binding = require('@vowifi-rs/vowifi-android-arm-eabi')
95
95
  const bindingPackageVersion = require('@vowifi-rs/vowifi-android-arm-eabi/package.json').version
96
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
96
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
98
98
  }
99
99
  return binding
100
100
  } catch (e) {
@@ -114,8 +114,8 @@ function requireNative() {
114
114
  try {
115
115
  const binding = require('@vowifi-rs/vowifi-win32-x64-gnu')
116
116
  const bindingPackageVersion = require('@vowifi-rs/vowifi-win32-x64-gnu/package.json').version
117
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
117
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
119
119
  }
120
120
  return binding
121
121
  } catch (e) {
@@ -130,8 +130,8 @@ function requireNative() {
130
130
  try {
131
131
  const binding = require('@vowifi-rs/vowifi-win32-x64-msvc')
132
132
  const bindingPackageVersion = require('@vowifi-rs/vowifi-win32-x64-msvc/package.json').version
133
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
133
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
135
135
  }
136
136
  return binding
137
137
  } catch (e) {
@@ -147,8 +147,8 @@ function requireNative() {
147
147
  try {
148
148
  const binding = require('@vowifi-rs/vowifi-win32-ia32-msvc')
149
149
  const bindingPackageVersion = require('@vowifi-rs/vowifi-win32-ia32-msvc/package.json').version
150
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
150
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
152
152
  }
153
153
  return binding
154
154
  } catch (e) {
@@ -163,8 +163,8 @@ function requireNative() {
163
163
  try {
164
164
  const binding = require('@vowifi-rs/vowifi-win32-arm64-msvc')
165
165
  const bindingPackageVersion = require('@vowifi-rs/vowifi-win32-arm64-msvc/package.json').version
166
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
166
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
168
168
  }
169
169
  return binding
170
170
  } catch (e) {
@@ -182,8 +182,8 @@ function requireNative() {
182
182
  try {
183
183
  const binding = require('@vowifi-rs/vowifi-darwin-universal')
184
184
  const bindingPackageVersion = require('@vowifi-rs/vowifi-darwin-universal/package.json').version
185
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
185
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
187
187
  }
188
188
  return binding
189
189
  } catch (e) {
@@ -198,8 +198,8 @@ function requireNative() {
198
198
  try {
199
199
  const binding = require('@vowifi-rs/vowifi-darwin-x64')
200
200
  const bindingPackageVersion = require('@vowifi-rs/vowifi-darwin-x64/package.json').version
201
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
201
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
203
203
  }
204
204
  return binding
205
205
  } catch (e) {
@@ -214,8 +214,8 @@ function requireNative() {
214
214
  try {
215
215
  const binding = require('@vowifi-rs/vowifi-darwin-arm64')
216
216
  const bindingPackageVersion = require('@vowifi-rs/vowifi-darwin-arm64/package.json').version
217
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
217
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
219
219
  }
220
220
  return binding
221
221
  } catch (e) {
@@ -234,8 +234,8 @@ function requireNative() {
234
234
  try {
235
235
  const binding = require('@vowifi-rs/vowifi-freebsd-x64')
236
236
  const bindingPackageVersion = require('@vowifi-rs/vowifi-freebsd-x64/package.json').version
237
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
237
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
239
239
  }
240
240
  return binding
241
241
  } catch (e) {
@@ -250,8 +250,8 @@ function requireNative() {
250
250
  try {
251
251
  const binding = require('@vowifi-rs/vowifi-freebsd-arm64')
252
252
  const bindingPackageVersion = require('@vowifi-rs/vowifi-freebsd-arm64/package.json').version
253
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
253
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
255
255
  }
256
256
  return binding
257
257
  } catch (e) {
@@ -271,8 +271,8 @@ function requireNative() {
271
271
  try {
272
272
  const binding = require('@vowifi-rs/vowifi-linux-x64-musl')
273
273
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-x64-musl/package.json').version
274
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
274
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
276
276
  }
277
277
  return binding
278
278
  } catch (e) {
@@ -287,8 +287,8 @@ function requireNative() {
287
287
  try {
288
288
  const binding = require('@vowifi-rs/vowifi-linux-x64-gnu')
289
289
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-x64-gnu/package.json').version
290
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
290
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
292
292
  }
293
293
  return binding
294
294
  } catch (e) {
@@ -305,8 +305,8 @@ function requireNative() {
305
305
  try {
306
306
  const binding = require('@vowifi-rs/vowifi-linux-arm64-musl')
307
307
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-arm64-musl/package.json').version
308
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
308
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
310
310
  }
311
311
  return binding
312
312
  } catch (e) {
@@ -321,8 +321,8 @@ function requireNative() {
321
321
  try {
322
322
  const binding = require('@vowifi-rs/vowifi-linux-arm64-gnu')
323
323
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-arm64-gnu/package.json').version
324
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
324
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
326
326
  }
327
327
  return binding
328
328
  } catch (e) {
@@ -339,8 +339,8 @@ function requireNative() {
339
339
  try {
340
340
  const binding = require('@vowifi-rs/vowifi-linux-arm-musleabihf')
341
341
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-arm-musleabihf/package.json').version
342
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
342
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
344
344
  }
345
345
  return binding
346
346
  } catch (e) {
@@ -355,8 +355,8 @@ function requireNative() {
355
355
  try {
356
356
  const binding = require('@vowifi-rs/vowifi-linux-arm-gnueabihf')
357
357
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-arm-gnueabihf/package.json').version
358
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
358
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
360
360
  }
361
361
  return binding
362
362
  } catch (e) {
@@ -373,8 +373,8 @@ function requireNative() {
373
373
  try {
374
374
  const binding = require('@vowifi-rs/vowifi-linux-loong64-musl')
375
375
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-loong64-musl/package.json').version
376
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
376
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
378
378
  }
379
379
  return binding
380
380
  } catch (e) {
@@ -389,8 +389,8 @@ function requireNative() {
389
389
  try {
390
390
  const binding = require('@vowifi-rs/vowifi-linux-loong64-gnu')
391
391
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-loong64-gnu/package.json').version
392
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
392
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
394
394
  }
395
395
  return binding
396
396
  } catch (e) {
@@ -407,8 +407,8 @@ function requireNative() {
407
407
  try {
408
408
  const binding = require('@vowifi-rs/vowifi-linux-riscv64-musl')
409
409
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-riscv64-musl/package.json').version
410
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
410
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
412
412
  }
413
413
  return binding
414
414
  } catch (e) {
@@ -423,8 +423,8 @@ function requireNative() {
423
423
  try {
424
424
  const binding = require('@vowifi-rs/vowifi-linux-riscv64-gnu')
425
425
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-riscv64-gnu/package.json').version
426
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
426
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
428
428
  }
429
429
  return binding
430
430
  } catch (e) {
@@ -440,8 +440,8 @@ function requireNative() {
440
440
  try {
441
441
  const binding = require('@vowifi-rs/vowifi-linux-ppc64-gnu')
442
442
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-ppc64-gnu/package.json').version
443
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
443
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
445
445
  }
446
446
  return binding
447
447
  } catch (e) {
@@ -456,8 +456,8 @@ function requireNative() {
456
456
  try {
457
457
  const binding = require('@vowifi-rs/vowifi-linux-s390x-gnu')
458
458
  const bindingPackageVersion = require('@vowifi-rs/vowifi-linux-s390x-gnu/package.json').version
459
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
459
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
461
461
  }
462
462
  return binding
463
463
  } catch (e) {
@@ -476,8 +476,8 @@ function requireNative() {
476
476
  try {
477
477
  const binding = require('@vowifi-rs/vowifi-openharmony-arm64')
478
478
  const bindingPackageVersion = require('@vowifi-rs/vowifi-openharmony-arm64/package.json').version
479
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
479
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
481
481
  }
482
482
  return binding
483
483
  } catch (e) {
@@ -492,8 +492,8 @@ function requireNative() {
492
492
  try {
493
493
  const binding = require('@vowifi-rs/vowifi-openharmony-x64')
494
494
  const bindingPackageVersion = require('@vowifi-rs/vowifi-openharmony-x64/package.json').version
495
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
495
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
497
497
  }
498
498
  return binding
499
499
  } catch (e) {
@@ -508,8 +508,8 @@ function requireNative() {
508
508
  try {
509
509
  const binding = require('@vowifi-rs/vowifi-openharmony-arm')
510
510
  const bindingPackageVersion = require('@vowifi-rs/vowifi-openharmony-arm/package.json').version
511
- if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
- throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
511
+ if (bindingPackageVersion !== '0.1.14' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
+ throw new Error(`Native binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
513
  }
514
514
  return binding
515
515
  } catch (e) {
@@ -648,8 +648,8 @@ if (!nativeBinding || forceWasi) {
648
648
  if (!candidateFailed) {
649
649
  if (process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
650
650
  const bindingPackageVersion = require('@vowifi-rs/vowifi-wasm32-wasi/package.json').version
651
- if (bindingPackageVersion !== '0.1.11') {
652
- throw new Error(`WASI binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
651
+ if (bindingPackageVersion !== '0.1.14') {
652
+ throw new Error(`WASI binding package version mismatch, expected 0.1.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
653
653
  }
654
654
  }
655
655
  wasiBinding = require('@vowifi-rs/vowifi-wasm32-wasi')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vowifi-rs/vowifi",
3
- "version": "0.1.13",
3
+ "version": "0.2.0",
4
4
  "description": "Hardware-independent VoWiFi SMS session for Node.js",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "repository": {
@@ -45,8 +45,8 @@
45
45
  ]
46
46
  },
47
47
  "optionalDependencies": {
48
- "@vowifi-rs/vowifi-win32-x64-msvc": "0.1.13",
49
- "@vowifi-rs/vowifi-linux-x64-gnu": "0.1.13",
50
- "@vowifi-rs/vowifi-linux-arm64-gnu": "0.1.13"
48
+ "@vowifi-rs/vowifi-win32-x64-msvc": "0.2.0",
49
+ "@vowifi-rs/vowifi-linux-x64-gnu": "0.2.0",
50
+ "@vowifi-rs/vowifi-linux-arm64-gnu": "0.2.0"
51
51
  }
52
52
  }