asterisk-voice-sdk 0.0.1

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 ADDED
@@ -0,0 +1,82 @@
1
+ # asterisk-voice-sdk
2
+
3
+ SDK SIP cấp cao cho Asterisk/FreePBX trên React Native (wrapper `react-native-pjsip`).
4
+
5
+ ## Cài đặt
6
+
7
+ ```bash
8
+ npm i asterisk-voice-sdk
9
+ ```
10
+
11
+ ## Peer dependencies
12
+
13
+ - `react` (>= 18)
14
+ - `react-native` (>= 0.73)
15
+
16
+ ## Dependencies (tự được cài kèm)
17
+
18
+ - `react-native-pjsip` (>= 2.7.0)
19
+
20
+ ## Ví dụ sử dụng
21
+
22
+ ```ts
23
+ import { VoiceClient } from 'asterisk-voice-sdk';
24
+
25
+ const client = new VoiceClient();
26
+
27
+ await client.initialize();
28
+
29
+ client.on('registration', (s) => {
30
+ console.log('registration', s);
31
+ });
32
+
33
+ await client.register({
34
+ name: 'RN Test',
35
+ username: '1000',
36
+ domain: 'pbx.example.com',
37
+ password: 'secret',
38
+ });
39
+
40
+ const callId = await client.startCall('1001');
41
+ // await client.hangup(callId);
42
+ ```
43
+
44
+ ## Build SDK & publish lên npm
45
+
46
+ ### Build (ra `dist/`)
47
+
48
+ ```bash
49
+ cd "E:\VTS VOICE\packages\asterisk-voice-sdk"
50
+ npm run build
51
+ ```
52
+
53
+ ### Kiểm tra gói trước khi publish (khuyến nghị)
54
+
55
+ ```bash
56
+ cd "E:\VTS VOICE\packages\asterisk-voice-sdk"
57
+ npm pack --dry-run
58
+ ```
59
+
60
+ ### Tăng version
61
+
62
+ ```bash
63
+ cd "E:\VTS VOICE\packages\asterisk-voice-sdk"
64
+ npm version patch
65
+ # hoặc: npm version minor
66
+ # hoặc: npm version major
67
+ ```
68
+
69
+ ### Publish
70
+
71
+ ```bash
72
+ cd "E:\VTS VOICE\packages\asterisk-voice-sdk"
73
+ npm login
74
+ npm publish --access public
75
+ ```
76
+
77
+ Nếu package là private (hoặc org của bạn mặc định private) thì dùng:
78
+
79
+ ```bash
80
+ npm publish
81
+ ```
82
+
@@ -0,0 +1,14 @@
1
+ export type Listener<T> = (payload: T) => void;
2
+ /**
3
+ * Emitter nhỏ gọn cho React Native, tránh phụ thuộc Node `events`.
4
+ * - `on/off/once` kiểu-safe theo payload
5
+ * - Không dựa vào polyfill Node runtime
6
+ */
7
+ export declare class TypedEmitter<EventMap extends Record<string, unknown>> {
8
+ private listeners;
9
+ on<K extends keyof EventMap>(event: K, listener: Listener<EventMap[K]>): this;
10
+ off<K extends keyof EventMap>(event: K, listener: Listener<EventMap[K]>): this;
11
+ once<K extends keyof EventMap>(event: K, listener: Listener<EventMap[K]>): this;
12
+ emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void;
13
+ removeAllListeners(): void;
14
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypedEmitter = void 0;
4
+ /**
5
+ * Emitter nhỏ gọn cho React Native, tránh phụ thuộc Node `events`.
6
+ * - `on/off/once` kiểu-safe theo payload
7
+ * - Không dựa vào polyfill Node runtime
8
+ */
9
+ class TypedEmitter {
10
+ constructor() {
11
+ this.listeners = new Map();
12
+ }
13
+ on(event, listener) {
14
+ let set = this.listeners.get(event);
15
+ if (!set) {
16
+ set = new Set();
17
+ this.listeners.set(event, set);
18
+ }
19
+ set.add(listener);
20
+ return this;
21
+ }
22
+ off(event, listener) {
23
+ var _a;
24
+ (_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(listener);
25
+ return this;
26
+ }
27
+ once(event, listener) {
28
+ const wrapped = (payload) => {
29
+ this.off(event, wrapped);
30
+ listener(payload);
31
+ };
32
+ this.on(event, wrapped);
33
+ return this;
34
+ }
35
+ emit(event, payload) {
36
+ const set = this.listeners.get(event);
37
+ if (!set || set.size === 0)
38
+ return;
39
+ for (const l of [...set]) {
40
+ l(payload);
41
+ }
42
+ }
43
+ removeAllListeners() {
44
+ this.listeners.clear();
45
+ }
46
+ }
47
+ exports.TypedEmitter = TypedEmitter;
@@ -0,0 +1,79 @@
1
+ import type { AsteriskRegistrationConfig, CallInfo, SipRegistrationState, VoiceSdkEventMap, VoiceSdkEventName } from './types';
2
+ /**
3
+ * VoiceClient là lớp façade duy nhất đối tác cần dùng:
4
+ * - Che giấu chi tiết `Endpoint` / `Account` / `Call` của react-native-pjsip
5
+ * - Chuẩn hoá sự kiện (registration / incoming / callUpdated / callEnded)
6
+ * - Gom lookup cuộc gọi theo `callId` để API hangup/mute không cần object native
7
+ *
8
+ * Dùng composition với `EventEmitter` (thay vì kế thừa) để TypeScript suy luận kiểu ổn định trên mọi phiên bản `@types/node` / RN.
9
+ *
10
+ * Luồng khởi tạo bắt buộc:
11
+ * 1) `await client.initialize()` — nạp native PJSIP, đăng ký listener nền
12
+ * 2) `await client.register(credentials)` — tạo SIP account + REGISTER
13
+ * 3) `await client.startCall(destination)` hoặc chờ sự kiện `incoming` rồi `answerCall`
14
+ *
15
+ * Lý do tách bước (1) và (2): khi app Android ở background, JS có thể bị suspend
16
+ * nhưng native PJSIP vẫn chạy; khi user mở lại app cần `initialize()` để đồng bộ
17
+ * accounts/calls hiện có (theo khuyến nghị của tác giả react-native-pjsip).
18
+ */
19
+ export declare class VoiceClient {
20
+ private readonly emitter;
21
+ private EndpointClass;
22
+ private endpoint;
23
+ /** Account SIP hiện tại (một client demo 1 account; có thể mở rộng map) */
24
+ private account;
25
+ /** Map callId -> instance Call mới nhất (PJSIP có thể emit object mới cùng id) */
26
+ private calls;
27
+ private boundRegistration?;
28
+ private boundCallReceived?;
29
+ private boundCallChanged?;
30
+ private boundCallTerminated?;
31
+ private boundConnectivity?;
32
+ private initialized;
33
+ on<K extends VoiceSdkEventName>(event: K, listener: (payload: VoiceSdkEventMap[K]) => void): this;
34
+ once<K extends VoiceSdkEventName>(event: K, listener: (payload: VoiceSdkEventMap[K]) => void): this;
35
+ off<K extends VoiceSdkEventName>(event: K, listener: (payload: VoiceSdkEventMap[K]) => void): this;
36
+ private emit;
37
+ /**
38
+ * Nạp module native và khởi động PJSIP.
39
+ * Phải gọi trước mọi thao tác SIP khác.
40
+ */
41
+ initialize(): Promise<void>;
42
+ private requireEndpoint;
43
+ private wireEndpointEvents;
44
+ private emitRegistrationSnapshot;
45
+ private toCallInfo;
46
+ /**
47
+ * Tạo account PJSIP và gửi REGISTER tới PBX.
48
+ * Với FreePBX: username thường là số máy nhánh, domain là domain SIP của endpoint.
49
+ */
50
+ register(config: AsteriskRegistrationConfig): Promise<void>;
51
+ /**
52
+ * Gỡ REGISTER và xoá account khỏi stack (không destroy Endpoint).
53
+ */
54
+ unregister(): Promise<void>;
55
+ /**
56
+ * Gọi ra: destination có thể là "1001" hoặc "sip:1001@domain".
57
+ * Thư viện sẽ chuẩn hoá URI dựa trên account.
58
+ */
59
+ startCall(destination: string, sipHeaders?: Record<string, string>): Promise<number>;
60
+ answerCall(callId: number): Promise<void>;
61
+ hangup(callId: number): Promise<void>;
62
+ hold(callId: number): Promise<void>;
63
+ unhold(callId: number): Promise<void>;
64
+ /** Tắt mic gửi đi (khác hold — không gửi re-INVITE hold SDP) */
65
+ mute(callId: number): Promise<void>;
66
+ unmute(callId: number): Promise<void>;
67
+ sendDtmf(callId: number, digits: string): Promise<void>;
68
+ useSpeaker(callId: number): Promise<void>;
69
+ useEarpiece(callId: number): Promise<void>;
70
+ getSnapshot(): {
71
+ registration: SipRegistrationState | null;
72
+ calls: CallInfo[];
73
+ };
74
+ private resolveCall;
75
+ /**
76
+ * Huỷ listener và giải phóng tài nguyên SIP (dùng khi logout user).
77
+ */
78
+ dispose(): Promise<void>;
79
+ }
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VoiceClient = void 0;
4
+ const errors_1 = require("./errors");
5
+ const TypedEmitter_1 = require("./TypedEmitter");
6
+ /**
7
+ * VoiceClient là lớp façade duy nhất đối tác cần dùng:
8
+ * - Che giấu chi tiết `Endpoint` / `Account` / `Call` của react-native-pjsip
9
+ * - Chuẩn hoá sự kiện (registration / incoming / callUpdated / callEnded)
10
+ * - Gom lookup cuộc gọi theo `callId` để API hangup/mute không cần object native
11
+ *
12
+ * Dùng composition với `EventEmitter` (thay vì kế thừa) để TypeScript suy luận kiểu ổn định trên mọi phiên bản `@types/node` / RN.
13
+ *
14
+ * Luồng khởi tạo bắt buộc:
15
+ * 1) `await client.initialize()` — nạp native PJSIP, đăng ký listener nền
16
+ * 2) `await client.register(credentials)` — tạo SIP account + REGISTER
17
+ * 3) `await client.startCall(destination)` hoặc chờ sự kiện `incoming` rồi `answerCall`
18
+ *
19
+ * Lý do tách bước (1) và (2): khi app Android ở background, JS có thể bị suspend
20
+ * nhưng native PJSIP vẫn chạy; khi user mở lại app cần `initialize()` để đồng bộ
21
+ * accounts/calls hiện có (theo khuyến nghị của tác giả react-native-pjsip).
22
+ */
23
+ class VoiceClient {
24
+ constructor() {
25
+ this.emitter = new TypedEmitter_1.TypedEmitter();
26
+ this.EndpointClass = null;
27
+ this.endpoint = null;
28
+ /** Account SIP hiện tại (một client demo 1 account; có thể mở rộng map) */
29
+ this.account = null;
30
+ /** Map callId -> instance Call mới nhất (PJSIP có thể emit object mới cùng id) */
31
+ this.calls = new Map();
32
+ this.initialized = false;
33
+ }
34
+ on(event, listener) {
35
+ this.emitter.on(event, listener);
36
+ return this;
37
+ }
38
+ once(event, listener) {
39
+ this.emitter.once(event, listener);
40
+ return this;
41
+ }
42
+ off(event, listener) {
43
+ this.emitter.off(event, listener);
44
+ return this;
45
+ }
46
+ emit(event, payload) {
47
+ this.emitter.emit(event, payload);
48
+ }
49
+ /**
50
+ * Nạp module native và khởi động PJSIP.
51
+ * Phải gọi trước mọi thao tác SIP khác.
52
+ */
53
+ async initialize() {
54
+ var _a;
55
+ if (this.initialized) {
56
+ throw new errors_1.VoiceSdkError('ALREADY_INITIALIZED', 'VoiceClient đã được initialize().');
57
+ }
58
+ let EndpointClass;
59
+ try {
60
+ // require động: tránh crash khi import SDK trong môi trường không có native (vd: web tests)
61
+ EndpointClass = require('react-native-pjsip').Endpoint;
62
+ }
63
+ catch (e) {
64
+ throw new errors_1.VoiceSdkError('MISSING_NATIVE', 'Không load được react-native-pjsip. Hãy cài dependency và rebuild app native.', { cause: e });
65
+ }
66
+ this.EndpointClass = EndpointClass;
67
+ this.endpoint = new EndpointClass();
68
+ const state = await this.endpoint.start();
69
+ /**
70
+ * Khi JS restart nhưng service Android vẫn giữ stack SIP, `start()` trả về
71
+ * accounts/calls hiện có — ta hydrate lại map để hangup/mute vẫn hoạt động.
72
+ */
73
+ for (const c of (_a = state.calls) !== null && _a !== void 0 ? _a : []) {
74
+ this.calls.set(c.getId(), c);
75
+ }
76
+ this.wireEndpointEvents();
77
+ this.initialized = true;
78
+ }
79
+ requireEndpoint() {
80
+ if (!this.endpoint) {
81
+ throw new errors_1.VoiceSdkError('NOT_INITIALIZED', 'Gọi initialize() trước.');
82
+ }
83
+ return this.endpoint;
84
+ }
85
+ wireEndpointEvents() {
86
+ const ep = this.requireEndpoint();
87
+ this.boundRegistration = (acc) => {
88
+ if (this.account && acc.getUsername() === this.account.getUsername()) {
89
+ this.emitRegistrationSnapshot(acc);
90
+ }
91
+ };
92
+ this.boundCallReceived = (call) => {
93
+ this.calls.set(call.getId(), call);
94
+ this.emit('incoming', this.toCallInfo(call));
95
+ };
96
+ this.boundCallChanged = (call) => {
97
+ this.calls.set(call.getId(), call);
98
+ this.emit('callUpdated', this.toCallInfo(call));
99
+ };
100
+ this.boundCallTerminated = (call) => {
101
+ const id = call.getId();
102
+ this.calls.delete(id);
103
+ this.emit('callEnded', { id });
104
+ };
105
+ this.boundConnectivity = (online) => {
106
+ this.emit('connectivity', { online });
107
+ };
108
+ ep.addListener('registration_changed', this.boundRegistration);
109
+ ep.addListener('call_received', this.boundCallReceived);
110
+ ep.addListener('call_changed', this.boundCallChanged);
111
+ ep.addListener('call_terminated', this.boundCallTerminated);
112
+ ep.addListener('connectivity_changed', this.boundConnectivity);
113
+ }
114
+ emitRegistrationSnapshot(account) {
115
+ const reg = account.getRegistration();
116
+ const payload = {
117
+ active: reg.isActive(),
118
+ statusText: reg.getStatus(),
119
+ };
120
+ this.emit('registration', payload);
121
+ }
122
+ toCallInfo(call) {
123
+ return {
124
+ id: call.getId(),
125
+ remoteLabel: call.getRemoteFormattedNumber(),
126
+ remoteUri: call.getRemoteUri(),
127
+ state: call.getState(),
128
+ stateText: call.getStateText(),
129
+ };
130
+ }
131
+ /**
132
+ * Tạo account PJSIP và gửi REGISTER tới PBX.
133
+ * Với FreePBX: username thường là số máy nhánh, domain là domain SIP của endpoint.
134
+ */
135
+ async register(config) {
136
+ var _a, _b, _c, _d;
137
+ const ep = this.requireEndpoint();
138
+ if (this.account) {
139
+ await ep.deleteAccount(this.account);
140
+ this.account = null;
141
+ }
142
+ const account = await ep.createAccount({
143
+ name: config.name,
144
+ username: config.username,
145
+ domain: config.domain,
146
+ password: config.password,
147
+ proxy: (_a = config.proxy) !== null && _a !== void 0 ? _a : null,
148
+ regServer: (_b = config.regServer) !== null && _b !== void 0 ? _b : null,
149
+ transport: (_c = config.transport) !== null && _c !== void 0 ? _c : null,
150
+ regTimeout: (_d = config.regTimeout) !== null && _d !== void 0 ? _d : null,
151
+ regHeaders: config.regHeaders,
152
+ regContactParams: config.regContactParams,
153
+ });
154
+ this.account = account;
155
+ this.emitRegistrationSnapshot(account);
156
+ }
157
+ /**
158
+ * Gỡ REGISTER và xoá account khỏi stack (không destroy Endpoint).
159
+ */
160
+ async unregister() {
161
+ const ep = this.requireEndpoint();
162
+ if (!this.account) {
163
+ return;
164
+ }
165
+ await ep.registerAccount(this.account, false);
166
+ await ep.deleteAccount(this.account);
167
+ this.account = null;
168
+ }
169
+ /**
170
+ * Gọi ra: destination có thể là "1001" hoặc "sip:1001@domain".
171
+ * Thư viện sẽ chuẩn hoá URI dựa trên account.
172
+ */
173
+ async startCall(destination, sipHeaders) {
174
+ const ep = this.requireEndpoint();
175
+ if (!this.account) {
176
+ throw new errors_1.VoiceSdkError('NO_ACCOUNT', 'Chưa register SIP. Gọi register() trước.');
177
+ }
178
+ const call = await ep.makeCall(this.account, destination, {}, sipHeaders ? { headers: sipHeaders } : {});
179
+ this.calls.set(call.getId(), call);
180
+ this.emit('callUpdated', this.toCallInfo(call));
181
+ return call.getId();
182
+ }
183
+ async answerCall(callId) {
184
+ const ep = this.requireEndpoint();
185
+ const call = this.resolveCall(callId);
186
+ await ep.answerCall(call);
187
+ }
188
+ async hangup(callId) {
189
+ const ep = this.requireEndpoint();
190
+ const call = this.resolveCall(callId);
191
+ await ep.hangupCall(call);
192
+ }
193
+ async hold(callId) {
194
+ const ep = this.requireEndpoint();
195
+ await ep.holdCall(this.resolveCall(callId));
196
+ }
197
+ async unhold(callId) {
198
+ const ep = this.requireEndpoint();
199
+ await ep.unholdCall(this.resolveCall(callId));
200
+ }
201
+ /** Tắt mic gửi đi (khác hold — không gửi re-INVITE hold SDP) */
202
+ async mute(callId) {
203
+ const ep = this.requireEndpoint();
204
+ await ep.muteCall(this.resolveCall(callId));
205
+ }
206
+ async unmute(callId) {
207
+ const ep = this.requireEndpoint();
208
+ await ep.unMuteCall(this.resolveCall(callId));
209
+ }
210
+ async sendDtmf(callId, digits) {
211
+ const ep = this.requireEndpoint();
212
+ await ep.dtmfCall(this.resolveCall(callId), digits);
213
+ }
214
+ async useSpeaker(callId) {
215
+ const ep = this.requireEndpoint();
216
+ await ep.useSpeaker(this.resolveCall(callId));
217
+ }
218
+ async useEarpiece(callId) {
219
+ const ep = this.requireEndpoint();
220
+ await ep.useEarpiece(this.resolveCall(callId));
221
+ }
222
+ getSnapshot() {
223
+ const registration = this.account === null
224
+ ? null
225
+ : {
226
+ active: this.account.getRegistration().isActive(),
227
+ statusText: this.account.getRegistration().getStatus(),
228
+ };
229
+ const calls = [...this.calls.values()].map((c) => this.toCallInfo(c));
230
+ return { registration, calls };
231
+ }
232
+ resolveCall(callId) {
233
+ const call = this.calls.get(callId);
234
+ if (!call) {
235
+ throw new errors_1.VoiceSdkError('CALL_NOT_FOUND', `Không tìm thấy cuộc gọi id=${callId}`, {
236
+ details: { knownIds: [...this.calls.keys()] },
237
+ });
238
+ }
239
+ return call;
240
+ }
241
+ /**
242
+ * Huỷ listener và giải phóng tài nguyên SIP (dùng khi logout user).
243
+ */
244
+ async dispose() {
245
+ var _a, _b, _c, _d, _e;
246
+ if (!this.endpoint) {
247
+ return;
248
+ }
249
+ const ep = this.endpoint;
250
+ if (this.boundRegistration) {
251
+ (_a = ep.removeListener) === null || _a === void 0 ? void 0 : _a.call(ep, 'registration_changed', this.boundRegistration);
252
+ }
253
+ if (this.boundCallReceived) {
254
+ (_b = ep.removeListener) === null || _b === void 0 ? void 0 : _b.call(ep, 'call_received', this.boundCallReceived);
255
+ }
256
+ if (this.boundCallChanged) {
257
+ (_c = ep.removeListener) === null || _c === void 0 ? void 0 : _c.call(ep, 'call_changed', this.boundCallChanged);
258
+ }
259
+ if (this.boundCallTerminated) {
260
+ (_d = ep.removeListener) === null || _d === void 0 ? void 0 : _d.call(ep, 'call_terminated', this.boundCallTerminated);
261
+ }
262
+ if (this.boundConnectivity) {
263
+ (_e = ep.removeListener) === null || _e === void 0 ? void 0 : _e.call(ep, 'connectivity_changed', this.boundConnectivity);
264
+ }
265
+ for (const c of [...this.calls.values()]) {
266
+ try {
267
+ await ep.hangupCall(c);
268
+ }
269
+ catch {
270
+ /* noop: có thể đã kết thúc */
271
+ }
272
+ }
273
+ this.calls.clear();
274
+ if (this.account) {
275
+ try {
276
+ await ep.deleteAccount(this.account);
277
+ }
278
+ catch {
279
+ /* noop */
280
+ }
281
+ this.account = null;
282
+ }
283
+ this.endpoint = null;
284
+ this.EndpointClass = null;
285
+ this.initialized = false;
286
+ this.emitter.removeAllListeners();
287
+ }
288
+ }
289
+ exports.VoiceClient = VoiceClient;
@@ -0,0 +1,12 @@
1
+ export type VoiceSdkErrorCode = 'NOT_INITIALIZED' | 'ALREADY_INITIALIZED' | 'MISSING_NATIVE' | 'NO_ACCOUNT' | 'CALL_NOT_FOUND' | 'SIP_ERROR';
2
+ /**
3
+ * Lỗi có cấu trúc để app đối tác map sang toast / logging / analytics.
4
+ */
5
+ export declare class VoiceSdkError extends Error {
6
+ readonly code: VoiceSdkErrorCode;
7
+ readonly details?: unknown;
8
+ constructor(code: VoiceSdkErrorCode, message: string, options?: {
9
+ cause?: unknown;
10
+ details?: unknown;
11
+ });
12
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VoiceSdkError = void 0;
4
+ /**
5
+ * Lỗi có cấu trúc để app đối tác map sang toast / logging / analytics.
6
+ */
7
+ class VoiceSdkError extends Error {
8
+ constructor(code, message, options) {
9
+ super(message);
10
+ this.name = 'VoiceSdkError';
11
+ this.code = code;
12
+ this.details = options === null || options === void 0 ? void 0 : options.details;
13
+ if ((options === null || options === void 0 ? void 0 : options.cause) !== undefined) {
14
+ this.cause = options.cause;
15
+ }
16
+ }
17
+ }
18
+ exports.VoiceSdkError = VoiceSdkError;
@@ -0,0 +1,4 @@
1
+ export { VoiceClient } from './VoiceClient';
2
+ export { VoiceSdkError } from './errors';
3
+ export type { VoiceSdkErrorCode } from './errors';
4
+ export type { AsteriskRegistrationConfig, CallInfo, SipRegistrationState, VoiceSdkEventMap, VoiceSdkEventName, } from './types';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VoiceSdkError = exports.VoiceClient = void 0;
4
+ var VoiceClient_1 = require("./VoiceClient");
5
+ Object.defineProperty(exports, "VoiceClient", { enumerable: true, get: function () { return VoiceClient_1.VoiceClient; } });
6
+ var errors_1 = require("./errors");
7
+ Object.defineProperty(exports, "VoiceSdkError", { enumerable: true, get: function () { return errors_1.VoiceSdkError; } });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Thông tin đăng ký SIP lên Asterisk/FreePBX (PJSIP).
3
+ *
4
+ * Với topo VIP của bạn (UDP 5060 / TLS 5061):
5
+ * - UDP: transport = 'UDP', proxy/regServer trỏ tới `sip:asterisk.example.com:5060`
6
+ * - TLS: transport = 'TLS', proxy/regServer trỏ tới `sip:asterisk.example.com:5061`
7
+ *
8
+ * `domain` thường là domain nội bộ PBX (vd: pbx.local) — phải khớp cấu hình
9
+ * auth trên FreePBX; nếu PBX chỉ dùng IP, nhiều site vẫn đặt domain = IP.
10
+ *
11
+ * (Không import type từ `react-native-pjsip` ở đây để file types.ts tách biệt hoàn toàn
12
+ * với native module — tiện cho tooling/IDE khi package chưa được cài.)
13
+ */
14
+ export type AsteriskRegistrationConfig = {
15
+ name: string;
16
+ username: string;
17
+ domain: string;
18
+ password: string;
19
+ /** Ví dụ: sip:asterisk.example.com:5061 — quyết định đường đi thực tế tới tổng đài */
20
+ proxy?: string | null;
21
+ /** Máy chủ REGISTER (thường trùng proxy) */
22
+ regServer?: string | null;
23
+ transport?: 'UDP' | 'TCP' | 'TLS' | string | null;
24
+ regTimeout?: number | null;
25
+ regHeaders?: Record<string, string>;
26
+ regContactParams?: string;
27
+ };
28
+ export type SipRegistrationState = {
29
+ /** true khi session REGISTER đang active (theo isActive() của thư viện) */
30
+ active: boolean;
31
+ /** Chuỗi trạng thái từ PJSIP (phụ thuộc bản build native) */
32
+ statusText: string;
33
+ };
34
+ export type CallInfo = {
35
+ id: number;
36
+ /** Chuỗi hiển thị thân thiện nếu native cung cấp */
37
+ remoteLabel: string;
38
+ /** SIP URI đầu xa */
39
+ remoteUri: string;
40
+ /** Trạng thái PJSIP (RINGING, CONFIRMED, ...) */
41
+ state: string;
42
+ stateText: string;
43
+ };
44
+ export type VoiceSdkEventMap = {
45
+ /** Sau mỗi lần REGISTER/refresh thay đổi */
46
+ registration: SipRegistrationState;
47
+ /** Cuộc gọi đến (INVITE) */
48
+ incoming: CallInfo;
49
+ /** Mọi thay đổi trạng thái cuộc (re-INVITE, media, ...) */
50
+ callUpdated: CallInfo;
51
+ /** Cuộc kết thúc (BYE/CANCEL/fail) */
52
+ callEnded: {
53
+ id: number;
54
+ };
55
+ /** Mạng thiết bị (wifi/lte) đổi — có thể cần refresh registration */
56
+ connectivity: {
57
+ online: boolean;
58
+ };
59
+ /** Lỗi không map được sang Promise rejection (tuỳ bản native) */
60
+ error: {
61
+ message: string;
62
+ raw?: unknown;
63
+ };
64
+ };
65
+ export type VoiceSdkEventName = keyof VoiceSdkEventMap;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "asterisk-voice-sdk",
3
+ "version": "0.0.1",
4
+ "description": "SDK SIP cấp cao cho Asterisk/FreePBX trên React Native (wrapper react-native-pjsip)",
5
+ "license": "MIT",
6
+ "author": "hoanbv",
7
+ "keywords": ["asterisk", "voip", "sdk"],
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "react-native": "dist/index.js",
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.build.json",
20
+ "prepublishOnly": "npm run build",
21
+ "typecheck": "tsc -p tsconfig.json --noEmit"
22
+ },
23
+ "dependencies": {
24
+ "react-native-pjsip": ">=2.7.0"
25
+ },
26
+ "peerDependencies": {
27
+ "react": ">=18",
28
+ "react-native": ">=0.73"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^22.13.0",
32
+ "react": "19.2.3",
33
+ "react-native": "0.85.1",
34
+ "typescript": "^5.8.3"
35
+ }
36
+ }