@per_moeller/asterisk-ari 1.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +291 -0
  3. package/dist/client.d.ts +123 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +297 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/connection.d.ts +70 -0
  8. package/dist/connection.d.ts.map +1 -0
  9. package/dist/connection.js +157 -0
  10. package/dist/connection.js.map +1 -0
  11. package/dist/events/emitter.d.ts +82 -0
  12. package/dist/events/emitter.d.ts.map +1 -0
  13. package/dist/events/emitter.js +132 -0
  14. package/dist/events/emitter.js.map +1 -0
  15. package/dist/events/types.d.ts +283 -0
  16. package/dist/events/types.d.ts.map +1 -0
  17. package/dist/events/types.js +5 -0
  18. package/dist/events/types.js.map +1 -0
  19. package/dist/index.d.ts +27 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +34 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/models/bridge.d.ts +102 -0
  24. package/dist/models/bridge.d.ts.map +1 -0
  25. package/dist/models/bridge.js +186 -0
  26. package/dist/models/bridge.js.map +1 -0
  27. package/dist/models/channel.d.ts +166 -0
  28. package/dist/models/channel.d.ts.map +1 -0
  29. package/dist/models/channel.js +278 -0
  30. package/dist/models/channel.js.map +1 -0
  31. package/dist/models/index.d.ts +8 -0
  32. package/dist/models/index.d.ts.map +1 -0
  33. package/dist/models/index.js +8 -0
  34. package/dist/models/index.js.map +1 -0
  35. package/dist/models/playback.d.ts +82 -0
  36. package/dist/models/playback.d.ts.map +1 -0
  37. package/dist/models/playback.js +146 -0
  38. package/dist/models/playback.js.map +1 -0
  39. package/dist/models/recording.d.ts +104 -0
  40. package/dist/models/recording.d.ts.map +1 -0
  41. package/dist/models/recording.js +191 -0
  42. package/dist/models/recording.js.map +1 -0
  43. package/dist/pool.d.ts +57 -0
  44. package/dist/pool.d.ts.map +1 -0
  45. package/dist/pool.js +111 -0
  46. package/dist/pool.js.map +1 -0
  47. package/dist/queue.d.ts +85 -0
  48. package/dist/queue.d.ts.map +1 -0
  49. package/dist/queue.js +208 -0
  50. package/dist/queue.js.map +1 -0
  51. package/dist/resources/applications.d.ts +37 -0
  52. package/dist/resources/applications.d.ts.map +1 -0
  53. package/dist/resources/applications.js +45 -0
  54. package/dist/resources/applications.js.map +1 -0
  55. package/dist/resources/asterisk.d.ts +71 -0
  56. package/dist/resources/asterisk.d.ts.map +1 -0
  57. package/dist/resources/asterisk.js +94 -0
  58. package/dist/resources/asterisk.js.map +1 -0
  59. package/dist/resources/base.d.ts +18 -0
  60. package/dist/resources/base.d.ts.map +1 -0
  61. package/dist/resources/base.js +21 -0
  62. package/dist/resources/base.js.map +1 -0
  63. package/dist/resources/bridges.d.ts +66 -0
  64. package/dist/resources/bridges.d.ts.map +1 -0
  65. package/dist/resources/bridges.js +109 -0
  66. package/dist/resources/bridges.js.map +1 -0
  67. package/dist/resources/channels.d.ts +130 -0
  68. package/dist/resources/channels.d.ts.map +1 -0
  69. package/dist/resources/channels.js +228 -0
  70. package/dist/resources/channels.js.map +1 -0
  71. package/dist/resources/devicestates.d.ts +30 -0
  72. package/dist/resources/devicestates.d.ts.map +1 -0
  73. package/dist/resources/devicestates.js +37 -0
  74. package/dist/resources/devicestates.js.map +1 -0
  75. package/dist/resources/endpoints.d.ts +38 -0
  76. package/dist/resources/endpoints.d.ts.map +1 -0
  77. package/dist/resources/endpoints.js +61 -0
  78. package/dist/resources/endpoints.js.map +1 -0
  79. package/dist/resources/mailboxes.d.ts +30 -0
  80. package/dist/resources/mailboxes.d.ts.map +1 -0
  81. package/dist/resources/mailboxes.js +38 -0
  82. package/dist/resources/mailboxes.js.map +1 -0
  83. package/dist/resources/playbacks.d.ts +27 -0
  84. package/dist/resources/playbacks.d.ts.map +1 -0
  85. package/dist/resources/playbacks.js +31 -0
  86. package/dist/resources/playbacks.js.map +1 -0
  87. package/dist/resources/recordings.d.ts +76 -0
  88. package/dist/resources/recordings.d.ts.map +1 -0
  89. package/dist/resources/recordings.js +105 -0
  90. package/dist/resources/recordings.js.map +1 -0
  91. package/dist/resources/sounds.d.ts +22 -0
  92. package/dist/resources/sounds.d.ts.map +1 -0
  93. package/dist/resources/sounds.js +26 -0
  94. package/dist/resources/sounds.js.map +1 -0
  95. package/dist/types/api.d.ts +331 -0
  96. package/dist/types/api.d.ts.map +1 -0
  97. package/dist/types/api.js +5 -0
  98. package/dist/types/api.js.map +1 -0
  99. package/dist/types/options.d.ts +77 -0
  100. package/dist/types/options.d.ts.map +1 -0
  101. package/dist/types/options.js +23 -0
  102. package/dist/types/options.js.map +1 -0
  103. package/dist/version.d.ts +86 -0
  104. package/dist/version.d.ts.map +1 -0
  105. package/dist/version.js +122 -0
  106. package/dist/version.js.map +1 -0
  107. package/dist/websocket.d.ts +70 -0
  108. package/dist/websocket.d.ts.map +1 -0
  109. package/dist/websocket.js +195 -0
  110. package/dist/websocket.js.map +1 -0
  111. package/package.json +59 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Version detection and compatibility layer
3
+ */
4
+ import type { HttpConnection } from './connection.js';
5
+ /**
6
+ * Parsed ARI version
7
+ */
8
+ export interface AriVersion {
9
+ /** ARI major version (corresponds to Asterisk version) */
10
+ major: number;
11
+ /** Breaking changes version */
12
+ breaking: number;
13
+ /** Non-breaking changes version */
14
+ nonBreaking: number;
15
+ /** Full version string */
16
+ full: string;
17
+ }
18
+ /**
19
+ * Asterisk version mapping from ARI version
20
+ * ARI 4.x.x = Asterisk 16
21
+ * ARI 5.x.x = Asterisk 17
22
+ * ARI 6.x.x = Asterisk 18
23
+ * ARI 7.x.x = Asterisk 19
24
+ * ARI 8.x.x = Asterisk 20
25
+ * ARI 9.x.x = Asterisk 21
26
+ * ARI 10.x.x = Asterisk 22
27
+ * ARI 11.x.x = Asterisk 23
28
+ */
29
+ export declare function getAsteriskVersion(ariVersion: AriVersion): number;
30
+ /**
31
+ * Parse ARI version string (e.g., "6.0.0")
32
+ */
33
+ export declare function parseAriVersion(versionString: string): AriVersion;
34
+ /**
35
+ * Fetch ARI version from the server
36
+ */
37
+ export declare function fetchAriVersion(http: HttpConnection): Promise<AriVersion>;
38
+ /**
39
+ * Version compatibility checker
40
+ */
41
+ export declare class VersionCompat {
42
+ readonly version: AriVersion;
43
+ readonly asteriskVersion: number;
44
+ constructor(version: AriVersion);
45
+ /**
46
+ * Check if external media is supported (Asterisk 18+, ARI 6+)
47
+ */
48
+ get hasExternalMedia(): boolean;
49
+ /**
50
+ * Check if protocol_id field is available on channels (Asterisk 20+, ARI 8+)
51
+ */
52
+ get hasProtocolId(): boolean;
53
+ /**
54
+ * Check if tone detection events are supported (Asterisk 22+, ARI 10+)
55
+ */
56
+ get hasToneDetected(): boolean;
57
+ /**
58
+ * Check if tech-specific hangup cause is available (Asterisk 22.7+)
59
+ */
60
+ get hasTechCause(): boolean;
61
+ /**
62
+ * Check if transfer progress events are supported (Asterisk 22+, ARI 10+)
63
+ */
64
+ get hasTransferProgress(): boolean;
65
+ /**
66
+ * Check if video SFU bridge type is supported (Asterisk 18+)
67
+ */
68
+ get hasVideoSfuBridge(): boolean;
69
+ /**
70
+ * Check if channel move is supported (Asterisk 18+)
71
+ */
72
+ get hasChannelMove(): boolean;
73
+ /**
74
+ * Check if talking detection is supported
75
+ */
76
+ get hasTalkingDetection(): boolean;
77
+ /**
78
+ * Validate that a method is available for the current version
79
+ */
80
+ validateMethod(method: string): void;
81
+ /**
82
+ * Get a human-readable version description
83
+ */
84
+ toString(): string;
85
+ }
86
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEjE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CAYjE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAG/E;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,SAAgB,OAAO,EAAE,UAAU,CAAC;IACpC,SAAgB,eAAe,EAAE,MAAM,CAAC;gBAE5B,OAAO,EAAE,UAAU;IAK/B;;OAEG;IACH,IAAI,gBAAgB,IAAI,OAAO,CAE9B;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,OAAO,CAI1B;IAED;;OAEG;IACH,IAAI,mBAAmB,IAAI,OAAO,CAEjC;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAED;;OAEG;IACH,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;OAEG;IACH,IAAI,mBAAmB,IAAI,OAAO,CAGjC;IAED;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAepC;;OAEG;IACH,QAAQ,IAAI,MAAM;CAGnB"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Version detection and compatibility layer
3
+ */
4
+ /**
5
+ * Asterisk version mapping from ARI version
6
+ * ARI 4.x.x = Asterisk 16
7
+ * ARI 5.x.x = Asterisk 17
8
+ * ARI 6.x.x = Asterisk 18
9
+ * ARI 7.x.x = Asterisk 19
10
+ * ARI 8.x.x = Asterisk 20
11
+ * ARI 9.x.x = Asterisk 21
12
+ * ARI 10.x.x = Asterisk 22
13
+ * ARI 11.x.x = Asterisk 23
14
+ */
15
+ export function getAsteriskVersion(ariVersion) {
16
+ return ariVersion.major + 12;
17
+ }
18
+ /**
19
+ * Parse ARI version string (e.g., "6.0.0")
20
+ */
21
+ export function parseAriVersion(versionString) {
22
+ const parts = versionString.split('.');
23
+ if (parts.length < 3) {
24
+ throw new Error(`Invalid ARI version format: ${versionString}`);
25
+ }
26
+ return {
27
+ major: parseInt(parts[0], 10),
28
+ breaking: parseInt(parts[1], 10),
29
+ nonBreaking: parseInt(parts[2], 10),
30
+ full: versionString,
31
+ };
32
+ }
33
+ /**
34
+ * Fetch ARI version from the server
35
+ */
36
+ export async function fetchAriVersion(http) {
37
+ const resources = await http.get('/api-docs/resources.json');
38
+ return parseAriVersion(resources.apiVersion);
39
+ }
40
+ /**
41
+ * Version compatibility checker
42
+ */
43
+ export class VersionCompat {
44
+ version;
45
+ asteriskVersion;
46
+ constructor(version) {
47
+ this.version = version;
48
+ this.asteriskVersion = getAsteriskVersion(version);
49
+ }
50
+ /**
51
+ * Check if external media is supported (Asterisk 18+, ARI 6+)
52
+ */
53
+ get hasExternalMedia() {
54
+ return this.version.major >= 6;
55
+ }
56
+ /**
57
+ * Check if protocol_id field is available on channels (Asterisk 20+, ARI 8+)
58
+ */
59
+ get hasProtocolId() {
60
+ return this.version.major >= 8;
61
+ }
62
+ /**
63
+ * Check if tone detection events are supported (Asterisk 22+, ARI 10+)
64
+ */
65
+ get hasToneDetected() {
66
+ return this.version.major >= 10;
67
+ }
68
+ /**
69
+ * Check if tech-specific hangup cause is available (Asterisk 22.7+)
70
+ */
71
+ get hasTechCause() {
72
+ // This was added in Asterisk 22.7, but we can't detect minor version
73
+ // So we just check for ARI 10+
74
+ return this.version.major >= 10;
75
+ }
76
+ /**
77
+ * Check if transfer progress events are supported (Asterisk 22+, ARI 10+)
78
+ */
79
+ get hasTransferProgress() {
80
+ return this.version.major >= 10;
81
+ }
82
+ /**
83
+ * Check if video SFU bridge type is supported (Asterisk 18+)
84
+ */
85
+ get hasVideoSfuBridge() {
86
+ return this.version.major >= 6;
87
+ }
88
+ /**
89
+ * Check if channel move is supported (Asterisk 18+)
90
+ */
91
+ get hasChannelMove() {
92
+ return this.version.major >= 6;
93
+ }
94
+ /**
95
+ * Check if talking detection is supported
96
+ */
97
+ get hasTalkingDetection() {
98
+ // Available since early ARI versions
99
+ return true;
100
+ }
101
+ /**
102
+ * Validate that a method is available for the current version
103
+ */
104
+ validateMethod(method) {
105
+ const requirements = {
106
+ externalMedia: { check: this.hasExternalMedia, minAsterisk: '18' },
107
+ move: { check: this.hasChannelMove, minAsterisk: '18' },
108
+ };
109
+ const requirement = requirements[method];
110
+ if (requirement && !requirement.check) {
111
+ throw new Error(`Method '${method}' requires Asterisk ${requirement.minAsterisk}+ ` +
112
+ `(current: Asterisk ${this.asteriskVersion}, ARI ${this.version.full})`);
113
+ }
114
+ }
115
+ /**
116
+ * Get a human-readable version description
117
+ */
118
+ toString() {
119
+ return `Asterisk ${this.asteriskVersion} (ARI ${this.version.full})`;
120
+ }
121
+ }
122
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * WebSocket manager with aggressive reconnection
3
+ */
4
+ import { ConnectionEventEmitter } from './events/emitter.js';
5
+ import type { ResolvedOptions } from './types/options.js';
6
+ export interface WebSocketManagerOptions {
7
+ url: string;
8
+ username: string;
9
+ password: string;
10
+ app: string;
11
+ subscribeAll: boolean;
12
+ reconnect: boolean;
13
+ reconnectInterval: number;
14
+ maxReconnectInterval: number;
15
+ reconnectBackoffMultiplier: number;
16
+ pingInterval: number;
17
+ pingTimeout: number;
18
+ }
19
+ /**
20
+ * WebSocket connection manager with automatic reconnection
21
+ */
22
+ export declare class WebSocketManager extends ConnectionEventEmitter {
23
+ private ws;
24
+ private reconnecting;
25
+ private reconnectAttempts;
26
+ private reconnectTimer;
27
+ private pingTimer;
28
+ private pongTimer;
29
+ private intentionalClose;
30
+ private readonly wsUrl;
31
+ private readonly options;
32
+ constructor(options: ResolvedOptions);
33
+ /**
34
+ * Build the WebSocket URL with authentication and parameters
35
+ */
36
+ private buildWebSocketUrl;
37
+ /**
38
+ * Calculate reconnect delay with exponential backoff
39
+ */
40
+ private getReconnectDelay;
41
+ /**
42
+ * Connect to the WebSocket
43
+ */
44
+ connect(): Promise<void>;
45
+ /**
46
+ * Start the ping interval for keep-alive
47
+ */
48
+ private startPingInterval;
49
+ /**
50
+ * Stop the ping interval
51
+ */
52
+ private stopPingInterval;
53
+ /**
54
+ * Schedule a reconnection attempt
55
+ */
56
+ private scheduleReconnect;
57
+ /**
58
+ * Disconnect from the WebSocket
59
+ */
60
+ disconnect(): void;
61
+ /**
62
+ * Check if connected
63
+ */
64
+ isConnected(): boolean;
65
+ /**
66
+ * Get current reconnect attempts
67
+ */
68
+ getReconnectAttempts(): number;
69
+ }
70
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1D,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,0BAA0B,EAAE,MAAM,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,sBAAsB;IAC1D,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAEtC,OAAO,EAAE,eAAe;IAkBpC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8DxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsBzB;;OAEG;IACH,UAAU,IAAI,IAAI;IAiBlB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,oBAAoB,IAAI,MAAM;CAG/B"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * WebSocket manager with aggressive reconnection
3
+ */
4
+ import WebSocket from 'ws';
5
+ import { ConnectionEventEmitter } from './events/emitter.js';
6
+ /**
7
+ * WebSocket connection manager with automatic reconnection
8
+ */
9
+ export class WebSocketManager extends ConnectionEventEmitter {
10
+ ws = null;
11
+ reconnecting = false;
12
+ reconnectAttempts = 0;
13
+ reconnectTimer = null;
14
+ pingTimer = null;
15
+ pongTimer = null;
16
+ intentionalClose = false;
17
+ wsUrl;
18
+ options;
19
+ constructor(options) {
20
+ super();
21
+ this.options = {
22
+ url: options.url,
23
+ username: options.username,
24
+ password: options.password,
25
+ app: options.app,
26
+ subscribeAll: options.subscribeAll,
27
+ reconnect: options.reconnect,
28
+ reconnectInterval: options.reconnectInterval,
29
+ maxReconnectInterval: options.maxReconnectInterval,
30
+ reconnectBackoffMultiplier: options.reconnectBackoffMultiplier,
31
+ pingInterval: options.pingInterval,
32
+ pingTimeout: options.pingTimeout,
33
+ };
34
+ this.wsUrl = this.buildWebSocketUrl();
35
+ }
36
+ /**
37
+ * Build the WebSocket URL with authentication and parameters
38
+ */
39
+ buildWebSocketUrl() {
40
+ const httpUrl = new URL(this.options.url);
41
+ const protocol = httpUrl.protocol === 'https:' ? 'wss:' : 'ws:';
42
+ const wsUrl = new URL(`${protocol}//${httpUrl.host}/ari/events`);
43
+ wsUrl.searchParams.set('api_key', `${this.options.username}:${this.options.password}`);
44
+ wsUrl.searchParams.set('app', this.options.app);
45
+ if (this.options.subscribeAll) {
46
+ wsUrl.searchParams.set('subscribeAll', 'true');
47
+ }
48
+ return wsUrl.toString();
49
+ }
50
+ /**
51
+ * Calculate reconnect delay with exponential backoff
52
+ */
53
+ getReconnectDelay() {
54
+ const delay = this.options.reconnectInterval *
55
+ Math.pow(this.options.reconnectBackoffMultiplier, this.reconnectAttempts);
56
+ return Math.min(delay, this.options.maxReconnectInterval);
57
+ }
58
+ /**
59
+ * Connect to the WebSocket
60
+ */
61
+ connect() {
62
+ return new Promise((resolve, reject) => {
63
+ if (this.ws?.readyState === WebSocket.OPEN) {
64
+ resolve();
65
+ return;
66
+ }
67
+ this.intentionalClose = false;
68
+ this.ws = new WebSocket(this.wsUrl);
69
+ const onOpen = () => {
70
+ this.ws?.removeListener('error', onError);
71
+ this.reconnectAttempts = 0;
72
+ this.reconnecting = false;
73
+ this.startPingInterval();
74
+ this.emit('connected', undefined);
75
+ resolve();
76
+ };
77
+ const onError = (error) => {
78
+ this.ws?.removeListener('open', onOpen);
79
+ reject(error);
80
+ };
81
+ this.ws.once('open', onOpen);
82
+ this.ws.once('error', onError);
83
+ this.ws.on('message', (data) => {
84
+ try {
85
+ const event = JSON.parse(data.toString());
86
+ this.emit('message', event);
87
+ }
88
+ catch (error) {
89
+ console.error('Failed to parse WebSocket message:', error);
90
+ }
91
+ });
92
+ this.ws.on('close', (code, reason) => {
93
+ this.stopPingInterval();
94
+ if (!this.intentionalClose) {
95
+ this.emit('disconnected', { intentional: false });
96
+ if (this.options.reconnect) {
97
+ this.scheduleReconnect();
98
+ }
99
+ }
100
+ else {
101
+ this.emit('disconnected', { intentional: true });
102
+ }
103
+ });
104
+ this.ws.on('error', (error) => {
105
+ this.emit('error', error);
106
+ });
107
+ this.ws.on('pong', () => {
108
+ if (this.pongTimer) {
109
+ clearTimeout(this.pongTimer);
110
+ this.pongTimer = null;
111
+ }
112
+ });
113
+ });
114
+ }
115
+ /**
116
+ * Start the ping interval for keep-alive
117
+ */
118
+ startPingInterval() {
119
+ this.stopPingInterval();
120
+ this.pingTimer = setInterval(() => {
121
+ if (this.ws?.readyState === WebSocket.OPEN) {
122
+ this.ws.ping();
123
+ // Set pong timeout
124
+ this.pongTimer = setTimeout(() => {
125
+ console.warn('WebSocket ping timeout, reconnecting...');
126
+ this.ws?.terminate();
127
+ }, this.options.pingTimeout);
128
+ }
129
+ }, this.options.pingInterval);
130
+ }
131
+ /**
132
+ * Stop the ping interval
133
+ */
134
+ stopPingInterval() {
135
+ if (this.pingTimer) {
136
+ clearInterval(this.pingTimer);
137
+ this.pingTimer = null;
138
+ }
139
+ if (this.pongTimer) {
140
+ clearTimeout(this.pongTimer);
141
+ this.pongTimer = null;
142
+ }
143
+ }
144
+ /**
145
+ * Schedule a reconnection attempt
146
+ */
147
+ scheduleReconnect() {
148
+ if (this.reconnecting || this.intentionalClose) {
149
+ return;
150
+ }
151
+ this.reconnecting = true;
152
+ const delay = this.getReconnectDelay();
153
+ this.reconnectAttempts++;
154
+ this.emit('reconnecting', { attempt: this.reconnectAttempts, delay });
155
+ this.reconnectTimer = setTimeout(async () => {
156
+ try {
157
+ await this.connect();
158
+ this.emit('reconnected', undefined);
159
+ }
160
+ catch (error) {
161
+ // Will trigger another reconnect via the close handler
162
+ console.error('Reconnection failed:', error);
163
+ }
164
+ }, delay);
165
+ }
166
+ /**
167
+ * Disconnect from the WebSocket
168
+ */
169
+ disconnect() {
170
+ this.intentionalClose = true;
171
+ this.reconnecting = false;
172
+ if (this.reconnectTimer) {
173
+ clearTimeout(this.reconnectTimer);
174
+ this.reconnectTimer = null;
175
+ }
176
+ this.stopPingInterval();
177
+ if (this.ws) {
178
+ this.ws.close(1000, 'Client disconnect');
179
+ this.ws = null;
180
+ }
181
+ }
182
+ /**
183
+ * Check if connected
184
+ */
185
+ isConnected() {
186
+ return this.ws?.readyState === WebSocket.OPEN;
187
+ }
188
+ /**
189
+ * Get current reconnect attempts
190
+ */
191
+ getReconnectAttempts() {
192
+ return this.reconnectAttempts;
193
+ }
194
+ }
195
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@per_moeller/asterisk-ari",
3
+ "version": "1.0.0",
4
+ "description": "Modern TypeScript ARI (Asterisk REST Interface) client with WebSocket reconnection and full API coverage",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "engines": {
15
+ "node": ">=20.0.0"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "watch": "tsc --watch",
20
+ "test": "vitest",
21
+ "test:run": "vitest run",
22
+ "lint": "eslint src/",
23
+ "format": "prettier --write src/",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "keywords": [
27
+ "asterisk",
28
+ "ari",
29
+ "voip",
30
+ "telephony",
31
+ "pbx",
32
+ "websocket",
33
+ "typescript"
34
+ ],
35
+ "author": "Per Moeller",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/per-moeller/asterisk-ari.git"
40
+ },
41
+ "dependencies": {
42
+ "ws": "^8.16.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.0.0",
46
+ "@types/ws": "^8.5.10",
47
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
48
+ "@typescript-eslint/parser": "^8.0.0",
49
+ "eslint": "^9.0.0",
50
+ "prettier": "^3.2.0",
51
+ "typescript": "^5.3.0",
52
+ "vitest": "^2.0.0"
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "README.md",
57
+ "LICENSE"
58
+ ]
59
+ }