arifa-client 1.0.13 → 1.1.14
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/dist/arifa-client.esm.js
CHANGED
|
@@ -4,27 +4,28 @@ class ArifaClient {
|
|
|
4
4
|
this.isConnected = false;
|
|
5
5
|
this.reconnectAttempts = 0;
|
|
6
6
|
this.reconnectTimer = null;
|
|
7
|
+
this.reconnectScheduled = false;
|
|
7
8
|
this.internetOnline = true;
|
|
8
9
|
this.internetTimer = null;
|
|
9
10
|
this.recipient = null;
|
|
10
|
-
this.listeners =
|
|
11
|
-
this.connectionListeners =
|
|
11
|
+
this.listeners = new Set();
|
|
12
|
+
this.connectionListeners = new Set();
|
|
12
13
|
this.HEALTH_URL = "https://notifications.arifa.dev/health";
|
|
13
14
|
this.MAX_BACKOFF = 60000;
|
|
14
15
|
this.apiKey = apiKey;
|
|
15
16
|
this.client = client;
|
|
16
17
|
this.wsUrl = wsUrl || "wss://notifications.arifa.dev/ws";
|
|
17
18
|
this.apiEndpoint = apiEndpoint || "https://notifications.arifa.dev/notify";
|
|
18
|
-
|
|
19
|
+
if (typeof window !== "undefined") {
|
|
20
|
+
this.startInternetMonitor();
|
|
21
|
+
}
|
|
19
22
|
}
|
|
20
23
|
safeParse(input) {
|
|
21
24
|
try {
|
|
22
25
|
if (typeof input === "object")
|
|
23
26
|
return input;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
parsed = JSON.parse(parsed);
|
|
27
|
-
return parsed;
|
|
27
|
+
const parsed = JSON.parse(String(input));
|
|
28
|
+
return typeof parsed === "string" ? JSON.parse(parsed) : parsed;
|
|
28
29
|
}
|
|
29
30
|
catch (_a) {
|
|
30
31
|
return null;
|
|
@@ -36,7 +37,7 @@ class ArifaClient {
|
|
|
36
37
|
const controller = new AbortController();
|
|
37
38
|
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
38
39
|
await fetch(this.HEALTH_URL, {
|
|
39
|
-
method: "
|
|
40
|
+
method: "HEAD",
|
|
40
41
|
cache: "no-store",
|
|
41
42
|
signal: controller.signal,
|
|
42
43
|
});
|
|
@@ -76,43 +77,58 @@ class ArifaClient {
|
|
|
76
77
|
}
|
|
77
78
|
return {
|
|
78
79
|
listen: (callback) => {
|
|
79
|
-
this.listeners.
|
|
80
|
+
this.listeners.add(callback);
|
|
81
|
+
return () => this.listeners.delete(callback);
|
|
80
82
|
},
|
|
81
|
-
unsubscribe: () => {
|
|
82
|
-
|
|
83
|
+
unsubscribe: (callback) => {
|
|
84
|
+
if (callback)
|
|
85
|
+
this.listeners.delete(callback);
|
|
86
|
+
else
|
|
87
|
+
this.listeners.clear();
|
|
83
88
|
},
|
|
84
89
|
};
|
|
85
90
|
}
|
|
86
91
|
onConnectionChange(callback) {
|
|
87
|
-
this.connectionListeners.
|
|
92
|
+
this.connectionListeners.add(callback);
|
|
93
|
+
return () => this.connectionListeners.delete(callback);
|
|
88
94
|
}
|
|
89
95
|
emitConnection(state) {
|
|
90
96
|
this.connectionListeners.forEach((cb) => cb(state));
|
|
91
97
|
}
|
|
92
98
|
/* ---------------- WEBSOCKET ---------------- */
|
|
93
99
|
connect() {
|
|
94
|
-
if (!this.recipient)
|
|
95
|
-
return;
|
|
96
|
-
if (!this.internetOnline)
|
|
100
|
+
if (!this.recipient || !this.internetOnline)
|
|
97
101
|
return;
|
|
98
|
-
if (this.ws &&
|
|
102
|
+
if (this.ws &&
|
|
103
|
+
(this.ws.readyState === WebSocket.OPEN ||
|
|
104
|
+
this.ws.readyState === WebSocket.CONNECTING)) {
|
|
99
105
|
return;
|
|
100
|
-
|
|
106
|
+
}
|
|
107
|
+
this.ws = new WebSocket(`${this.wsUrl}/connect?api_key=${encodeURIComponent(this.apiKey)}&recipient=${encodeURIComponent(this.recipient)}&client=${this.client}`);
|
|
101
108
|
this.ws.onopen = () => {
|
|
102
109
|
this.isConnected = true;
|
|
103
110
|
this.reconnectAttempts = 0;
|
|
111
|
+
this.reconnectScheduled = false;
|
|
112
|
+
if (this.reconnectTimer) {
|
|
113
|
+
clearTimeout(this.reconnectTimer);
|
|
114
|
+
this.reconnectTimer = null;
|
|
115
|
+
}
|
|
104
116
|
this.emitConnection("connected");
|
|
105
117
|
};
|
|
106
118
|
this.ws.onmessage = (event) => {
|
|
119
|
+
var _a, _b;
|
|
107
120
|
const parsed = this.safeParse(event.data);
|
|
108
121
|
if (!parsed)
|
|
109
122
|
return;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
if ((_a = parsed === null || parsed === void 0 ? void 0 : parsed.ack) === null || _a === void 0 ? void 0 : _a.event_id) {
|
|
124
|
+
(_b = this.ws) === null || _b === void 0 ? void 0 : _b.send(JSON.stringify({
|
|
125
|
+
kind: "ack",
|
|
126
|
+
event_id: parsed.ack.event_id,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
if (parsed.event) {
|
|
130
|
+
this.listeners.forEach((fn) => fn(parsed.event));
|
|
131
|
+
}
|
|
116
132
|
};
|
|
117
133
|
this.ws.onclose = () => {
|
|
118
134
|
this.isConnected = false;
|
|
@@ -125,34 +141,44 @@ class ArifaClient {
|
|
|
125
141
|
};
|
|
126
142
|
}
|
|
127
143
|
scheduleReconnect() {
|
|
128
|
-
if (!this.internetOnline)
|
|
144
|
+
if (!this.internetOnline || this.reconnectScheduled)
|
|
129
145
|
return;
|
|
146
|
+
this.reconnectScheduled = true;
|
|
130
147
|
const base = Math.min(this.MAX_BACKOFF, 1000 * 2 ** this.reconnectAttempts);
|
|
131
148
|
const jitter = Math.random() * 1000;
|
|
132
|
-
const timeout = base + jitter;
|
|
133
149
|
this.reconnectAttempts++;
|
|
134
150
|
this.reconnectTimer = window.setTimeout(() => {
|
|
151
|
+
this.reconnectScheduled = false;
|
|
135
152
|
this.connect();
|
|
136
|
-
},
|
|
153
|
+
}, base + jitter);
|
|
137
154
|
}
|
|
138
155
|
/* ---------------- HTTP NOTIFY ---------------- */
|
|
139
156
|
async notify({ recipient, payload, client, origin, }) {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
const controller = new AbortController();
|
|
158
|
+
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
159
|
+
let res;
|
|
160
|
+
try {
|
|
161
|
+
res = await fetch(this.apiEndpoint, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "application/json",
|
|
165
|
+
},
|
|
166
|
+
signal: controller.signal,
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
recipient,
|
|
169
|
+
payload,
|
|
170
|
+
api_key: this.apiKey,
|
|
171
|
+
client: client || this.client,
|
|
172
|
+
origin,
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
clearTimeout(timeout);
|
|
178
|
+
}
|
|
153
179
|
const json = await res.json();
|
|
154
180
|
if (!res.ok) {
|
|
155
|
-
throw new Error(json.message || "Notification failed");
|
|
181
|
+
throw new Error((json === null || json === void 0 ? void 0 : json.message) || "Notification failed");
|
|
156
182
|
}
|
|
157
183
|
return json;
|
|
158
184
|
}
|
|
@@ -7,27 +7,28 @@ var ArifaClient = (function () {
|
|
|
7
7
|
this.isConnected = false;
|
|
8
8
|
this.reconnectAttempts = 0;
|
|
9
9
|
this.reconnectTimer = null;
|
|
10
|
+
this.reconnectScheduled = false;
|
|
10
11
|
this.internetOnline = true;
|
|
11
12
|
this.internetTimer = null;
|
|
12
13
|
this.recipient = null;
|
|
13
|
-
this.listeners =
|
|
14
|
-
this.connectionListeners =
|
|
14
|
+
this.listeners = new Set();
|
|
15
|
+
this.connectionListeners = new Set();
|
|
15
16
|
this.HEALTH_URL = "https://notifications.arifa.dev/health";
|
|
16
17
|
this.MAX_BACKOFF = 60000;
|
|
17
18
|
this.apiKey = apiKey;
|
|
18
19
|
this.client = client;
|
|
19
20
|
this.wsUrl = wsUrl || "wss://notifications.arifa.dev/ws";
|
|
20
21
|
this.apiEndpoint = apiEndpoint || "https://notifications.arifa.dev/notify";
|
|
21
|
-
|
|
22
|
+
if (typeof window !== "undefined") {
|
|
23
|
+
this.startInternetMonitor();
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
26
|
safeParse(input) {
|
|
24
27
|
try {
|
|
25
28
|
if (typeof input === "object")
|
|
26
29
|
return input;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
parsed = JSON.parse(parsed);
|
|
30
|
-
return parsed;
|
|
30
|
+
const parsed = JSON.parse(String(input));
|
|
31
|
+
return typeof parsed === "string" ? JSON.parse(parsed) : parsed;
|
|
31
32
|
}
|
|
32
33
|
catch (_a) {
|
|
33
34
|
return null;
|
|
@@ -39,7 +40,7 @@ var ArifaClient = (function () {
|
|
|
39
40
|
const controller = new AbortController();
|
|
40
41
|
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
41
42
|
await fetch(this.HEALTH_URL, {
|
|
42
|
-
method: "
|
|
43
|
+
method: "HEAD",
|
|
43
44
|
cache: "no-store",
|
|
44
45
|
signal: controller.signal,
|
|
45
46
|
});
|
|
@@ -79,43 +80,58 @@ var ArifaClient = (function () {
|
|
|
79
80
|
}
|
|
80
81
|
return {
|
|
81
82
|
listen: (callback) => {
|
|
82
|
-
this.listeners.
|
|
83
|
+
this.listeners.add(callback);
|
|
84
|
+
return () => this.listeners.delete(callback);
|
|
83
85
|
},
|
|
84
|
-
unsubscribe: () => {
|
|
85
|
-
|
|
86
|
+
unsubscribe: (callback) => {
|
|
87
|
+
if (callback)
|
|
88
|
+
this.listeners.delete(callback);
|
|
89
|
+
else
|
|
90
|
+
this.listeners.clear();
|
|
86
91
|
},
|
|
87
92
|
};
|
|
88
93
|
}
|
|
89
94
|
onConnectionChange(callback) {
|
|
90
|
-
this.connectionListeners.
|
|
95
|
+
this.connectionListeners.add(callback);
|
|
96
|
+
return () => this.connectionListeners.delete(callback);
|
|
91
97
|
}
|
|
92
98
|
emitConnection(state) {
|
|
93
99
|
this.connectionListeners.forEach((cb) => cb(state));
|
|
94
100
|
}
|
|
95
101
|
/* ---------------- WEBSOCKET ---------------- */
|
|
96
102
|
connect() {
|
|
97
|
-
if (!this.recipient)
|
|
98
|
-
return;
|
|
99
|
-
if (!this.internetOnline)
|
|
103
|
+
if (!this.recipient || !this.internetOnline)
|
|
100
104
|
return;
|
|
101
|
-
if (this.ws &&
|
|
105
|
+
if (this.ws &&
|
|
106
|
+
(this.ws.readyState === WebSocket.OPEN ||
|
|
107
|
+
this.ws.readyState === WebSocket.CONNECTING)) {
|
|
102
108
|
return;
|
|
103
|
-
|
|
109
|
+
}
|
|
110
|
+
this.ws = new WebSocket(`${this.wsUrl}/connect?api_key=${encodeURIComponent(this.apiKey)}&recipient=${encodeURIComponent(this.recipient)}&client=${this.client}`);
|
|
104
111
|
this.ws.onopen = () => {
|
|
105
112
|
this.isConnected = true;
|
|
106
113
|
this.reconnectAttempts = 0;
|
|
114
|
+
this.reconnectScheduled = false;
|
|
115
|
+
if (this.reconnectTimer) {
|
|
116
|
+
clearTimeout(this.reconnectTimer);
|
|
117
|
+
this.reconnectTimer = null;
|
|
118
|
+
}
|
|
107
119
|
this.emitConnection("connected");
|
|
108
120
|
};
|
|
109
121
|
this.ws.onmessage = (event) => {
|
|
122
|
+
var _a, _b;
|
|
110
123
|
const parsed = this.safeParse(event.data);
|
|
111
124
|
if (!parsed)
|
|
112
125
|
return;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
if ((_a = parsed === null || parsed === void 0 ? void 0 : parsed.ack) === null || _a === void 0 ? void 0 : _a.event_id) {
|
|
127
|
+
(_b = this.ws) === null || _b === void 0 ? void 0 : _b.send(JSON.stringify({
|
|
128
|
+
kind: "ack",
|
|
129
|
+
event_id: parsed.ack.event_id,
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
if (parsed.event) {
|
|
133
|
+
this.listeners.forEach((fn) => fn(parsed.event));
|
|
134
|
+
}
|
|
119
135
|
};
|
|
120
136
|
this.ws.onclose = () => {
|
|
121
137
|
this.isConnected = false;
|
|
@@ -128,34 +144,44 @@ var ArifaClient = (function () {
|
|
|
128
144
|
};
|
|
129
145
|
}
|
|
130
146
|
scheduleReconnect() {
|
|
131
|
-
if (!this.internetOnline)
|
|
147
|
+
if (!this.internetOnline || this.reconnectScheduled)
|
|
132
148
|
return;
|
|
149
|
+
this.reconnectScheduled = true;
|
|
133
150
|
const base = Math.min(this.MAX_BACKOFF, 1000 * 2 ** this.reconnectAttempts);
|
|
134
151
|
const jitter = Math.random() * 1000;
|
|
135
|
-
const timeout = base + jitter;
|
|
136
152
|
this.reconnectAttempts++;
|
|
137
153
|
this.reconnectTimer = window.setTimeout(() => {
|
|
154
|
+
this.reconnectScheduled = false;
|
|
138
155
|
this.connect();
|
|
139
|
-
},
|
|
156
|
+
}, base + jitter);
|
|
140
157
|
}
|
|
141
158
|
/* ---------------- HTTP NOTIFY ---------------- */
|
|
142
159
|
async notify({ recipient, payload, client, origin, }) {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
162
|
+
let res;
|
|
163
|
+
try {
|
|
164
|
+
res = await fetch(this.apiEndpoint, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: {
|
|
167
|
+
"Content-Type": "application/json",
|
|
168
|
+
},
|
|
169
|
+
signal: controller.signal,
|
|
170
|
+
body: JSON.stringify({
|
|
171
|
+
recipient,
|
|
172
|
+
payload,
|
|
173
|
+
api_key: this.apiKey,
|
|
174
|
+
client: client || this.client,
|
|
175
|
+
origin,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
clearTimeout(timeout);
|
|
181
|
+
}
|
|
156
182
|
const json = await res.json();
|
|
157
183
|
if (!res.ok) {
|
|
158
|
-
throw new Error(json.message || "Notification failed");
|
|
184
|
+
throw new Error((json === null || json === void 0 ? void 0 : json.message) || "Notification failed");
|
|
159
185
|
}
|
|
160
186
|
return json;
|
|
161
187
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type ArifaEvent =
|
|
1
|
+
type ArifaEvent = Record<string, unknown>;
|
|
2
2
|
type ConnectionState = "connected" | "disconnected";
|
|
3
3
|
interface ArifaClientOptions {
|
|
4
4
|
apiKey: string;
|
|
@@ -8,7 +8,7 @@ interface ArifaClientOptions {
|
|
|
8
8
|
}
|
|
9
9
|
interface NotifyPayload {
|
|
10
10
|
recipient: string;
|
|
11
|
-
payload: Record<string,
|
|
11
|
+
payload: Record<string, unknown>;
|
|
12
12
|
client?: "web" | "mobile";
|
|
13
13
|
origin?: string;
|
|
14
14
|
}
|
|
@@ -26,6 +26,7 @@ export declare class ArifaClient {
|
|
|
26
26
|
private isConnected;
|
|
27
27
|
private reconnectAttempts;
|
|
28
28
|
private reconnectTimer;
|
|
29
|
+
private reconnectScheduled;
|
|
29
30
|
private internetOnline;
|
|
30
31
|
private internetTimer;
|
|
31
32
|
private recipient;
|
|
@@ -38,10 +39,10 @@ export declare class ArifaClient {
|
|
|
38
39
|
private checkInternet;
|
|
39
40
|
private startInternetMonitor;
|
|
40
41
|
subscribe(recipient: string): {
|
|
41
|
-
listen: (callback: (event: ArifaEvent) => void) =>
|
|
42
|
-
unsubscribe: () => void;
|
|
42
|
+
listen: (callback: (event: ArifaEvent) => void) => () => boolean;
|
|
43
|
+
unsubscribe: (callback?: (event: ArifaEvent) => void) => void;
|
|
43
44
|
};
|
|
44
|
-
onConnectionChange(callback: ConnectionCallback):
|
|
45
|
+
onConnectionChange(callback: ConnectionCallback): () => boolean;
|
|
45
46
|
private emitConnection;
|
|
46
47
|
private connect;
|
|
47
48
|
private scheduleReconnect;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArifaClient.d.ts","sourceRoot":"","sources":["../../src/ArifaClient.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"ArifaClient.d.ts","sourceRoot":"","sources":["../../src/ArifaClient.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC1C,KAAK,eAAe,GAAG,WAAW,GAAG,cAAc,CAAC;AAEpD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,cAAc;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAE3D,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,kBAAkB,CAAS;IAEnC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,aAAa,CAAuB;IAE5C,OAAO,CAAC,SAAS,CAAuB;IAExC,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,mBAAmB,CAAiC;IAE5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4C;IACvE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;gBAE1B,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,kBAAkB;IAWtE,OAAO,CAAC,SAAS;YAYH,aAAa;IAkB3B,OAAO,CAAC,oBAAoB;IA2B5B,SAAS,CAAC,SAAS,EAAE,MAAM;2BAOJ,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;iCAIrB,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;;IAOxD,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB;IAK/C,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,OAAO;IA2Df,OAAO,CAAC,iBAAiB;IAkBnB,MAAM,CAAC,EACX,SAAS,EACT,OAAO,EACP,MAAM,EACN,MAAM,GACP,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAoC1C,UAAU;IAUV,SAAS;CAGV"}
|
package/package.json
CHANGED