relayx-js 1.0.17 → 1.0.19
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/examples/example_chat.js +6 -7
- package/package.json +1 -1
- package/realtime/realtime.js +107 -87
package/examples/example_chat.js
CHANGED
|
@@ -28,18 +28,17 @@ async function run(){
|
|
|
28
28
|
console.log(`[IMPL] DISONNECT`)
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
await realtime.on("power-telemetry", (data) => {
|
|
32
|
+
console.log("power-telemetry", data);
|
|
33
|
+
});
|
|
34
34
|
|
|
35
35
|
// await realtime.on("hello.*", (data) => {
|
|
36
36
|
// console.log("hello.*", data);
|
|
37
37
|
// });
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// });
|
|
39
|
+
await realtime.on("hello.>", async (data) => {
|
|
40
|
+
console.log("hello.>", data);
|
|
41
|
+
});
|
|
43
42
|
|
|
44
43
|
// await realtime.on("hello.hey.*", (data) => {
|
|
45
44
|
// console.log("hell.hey.*", data);
|
package/package.json
CHANGED
package/realtime/realtime.js
CHANGED
|
@@ -12,18 +12,17 @@ export class Realtime {
|
|
|
12
12
|
#codec = JSONCodec();
|
|
13
13
|
#jetstream = null;
|
|
14
14
|
#consumerMap = {};
|
|
15
|
-
#consumer = null;
|
|
16
15
|
|
|
17
16
|
#event_func = {};
|
|
18
17
|
#topicMap = [];
|
|
19
18
|
|
|
20
|
-
#config = "CiAgICAgICAgLS0tLS1CRUdJTiBOQVRTIFVTRVIgSldULS0tLS0KICAgICAgICBKV1RfS0VZCiAgICAgICAgLS0tLS0tRU5EIE5BVFMgVVNFUiBKV1QtLS0tLS0KCiAgICAgICAgKioqKioqKioqKioqKioqKioqKioqKioqKiBJTVBPUlRBTlQgKioqKioqKioqKioqKioqKioqKioqKioqKgogICAgICAgIE5LRVkgU2VlZCBwcmludGVkIGJlbG93IGNhbiBiZSB1c2VkIHRvIHNpZ24gYW5kIHByb3ZlIGlkZW50aXR5LgogICAgICAgIE5LRVlzIGFyZSBzZW5zaXRpdmUgYW5kIHNob3VsZCBiZSB0cmVhdGVkIGFzIHNlY3JldHMuCgogICAgICAgIC0tLS0tQkVHSU4gVVNFUiBOS0VZIFNFRUQtLS0tLQogICAgICAgIFNFQ1JFVF9LRVkKICAgICAgICAtLS0tLS1FTkQgVVNFUiBOS0VZIFNFRUQtLS0tLS0KCiAgICAgICAgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKgogICAgICAgIA=="
|
|
21
|
-
|
|
22
19
|
// Status Codes
|
|
23
20
|
#RECONNECTING = "RECONNECTING";
|
|
24
21
|
#RECONNECTED = "RECONNECTED";
|
|
25
22
|
#RECONN_FAIL = "RECONN_FAIL";
|
|
26
23
|
|
|
24
|
+
#reservedSystemTopics = [CONNECTED, DISCONNECTED, RECONNECT, this.#RECONNECTED, this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND, SERVER_DISCONNECT];
|
|
25
|
+
|
|
27
26
|
setRemoteUserAttempts = 0;
|
|
28
27
|
setRemoteUserRetries = 5;
|
|
29
28
|
|
|
@@ -43,6 +42,8 @@ export class Realtime {
|
|
|
43
42
|
|
|
44
43
|
#maxPublishRetries = 5;
|
|
45
44
|
|
|
45
|
+
#connectCalled = false;
|
|
46
|
+
|
|
46
47
|
constructor(config){
|
|
47
48
|
if(typeof config != "object"){
|
|
48
49
|
throw new Error("Realtime($config). $config not object => {}")
|
|
@@ -98,10 +99,13 @@ export class Realtime {
|
|
|
98
99
|
if(arguments[0] instanceof Object){
|
|
99
100
|
opts = arguments[0];
|
|
100
101
|
staging = false;
|
|
101
|
-
}else{
|
|
102
|
+
}else if(typeof arguments[0] == "boolean"){
|
|
102
103
|
opts = {};
|
|
103
104
|
staging = arguments[0];
|
|
104
105
|
this.#log(staging)
|
|
106
|
+
}else{
|
|
107
|
+
opts = {};
|
|
108
|
+
staging = false
|
|
105
109
|
}
|
|
106
110
|
}else{
|
|
107
111
|
staging = false;
|
|
@@ -172,6 +176,10 @@ export class Realtime {
|
|
|
172
176
|
* Connects to the relay network
|
|
173
177
|
*/
|
|
174
178
|
async connect(){
|
|
179
|
+
if(this.#connectCalled){
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
175
183
|
this.SEVER_URL = this.#baseUrl;
|
|
176
184
|
|
|
177
185
|
var credsFile = this.#getUserCreds(this.api_key, this.secret)
|
|
@@ -195,6 +203,7 @@ export class Realtime {
|
|
|
195
203
|
await this.#getNameSpace()
|
|
196
204
|
|
|
197
205
|
this.connected = true;
|
|
206
|
+
this.#connectCalled = true;
|
|
198
207
|
}catch(err){
|
|
199
208
|
this.#log("ERR")
|
|
200
209
|
this.#log(err);
|
|
@@ -205,17 +214,13 @@ export class Realtime {
|
|
|
205
214
|
if (this.connected == true){
|
|
206
215
|
this.#log("Connected to server!");
|
|
207
216
|
|
|
208
|
-
// Callback on client side
|
|
209
|
-
if (CONNECTED in this.#event_func){
|
|
210
|
-
if (this.#event_func[CONNECTED] !== null || this.#event_func[CONNECTED] !== undefined){
|
|
211
|
-
this.#event_func[CONNECTED]()
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
217
|
this.#natsClient.closed().then(() => {
|
|
216
218
|
this.#log("the connection closed!");
|
|
217
219
|
|
|
218
220
|
this.#offlineMessageBuffer.length = 0;
|
|
221
|
+
this.connected = false;
|
|
222
|
+
this.reconnecting = false;
|
|
223
|
+
this.#connectCalled = false;
|
|
219
224
|
|
|
220
225
|
if (DISCONNECTED in this.#event_func){
|
|
221
226
|
if (this.#event_func[DISCONNECTED] !== null || this.#event_func[DISCONNECTED] !== undefined){
|
|
@@ -233,12 +238,6 @@ export class Realtime {
|
|
|
233
238
|
this.#log(`client disconnected - ${s.data}`);
|
|
234
239
|
|
|
235
240
|
this.connected = false;
|
|
236
|
-
|
|
237
|
-
if (DISCONNECTED in this.#event_func){
|
|
238
|
-
if (this.#event_func[DISCONNECTED] !== null || this.#event_func[DISCONNECTED] !== undefined){
|
|
239
|
-
this.#event_func[DISCONNECTED]()
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
241
|
break;
|
|
243
242
|
case Events.LDM:
|
|
244
243
|
this.#log("client has been requested to reconnect");
|
|
@@ -268,6 +267,7 @@ export class Realtime {
|
|
|
268
267
|
this.#log("client is attempting to reconnect");
|
|
269
268
|
|
|
270
269
|
this.reconnecting = true;
|
|
270
|
+
this.connected = false;
|
|
271
271
|
|
|
272
272
|
if(RECONNECT in this.#event_func && this.reconnecting){
|
|
273
273
|
this.#event_func[RECONNECT](this.#RECONNECTING);
|
|
@@ -285,6 +285,13 @@ export class Realtime {
|
|
|
285
285
|
// Subscribe to topics
|
|
286
286
|
this.#subscribeToTopics();
|
|
287
287
|
this.#log("Subscribed to topics");
|
|
288
|
+
|
|
289
|
+
// Callback on client side
|
|
290
|
+
if (CONNECTED in this.#event_func){
|
|
291
|
+
if (this.#event_func[CONNECTED] !== null || this.#event_func[CONNECTED] !== undefined){
|
|
292
|
+
this.#event_func[CONNECTED]()
|
|
293
|
+
}
|
|
294
|
+
}
|
|
288
295
|
}
|
|
289
296
|
}
|
|
290
297
|
|
|
@@ -295,12 +302,11 @@ export class Realtime {
|
|
|
295
302
|
if(this.#natsClient !== null){
|
|
296
303
|
this.reconnected = false;
|
|
297
304
|
this.disconnected = true;
|
|
298
|
-
|
|
299
|
-
this.#consumer?.delete()
|
|
305
|
+
this.#connectCalled = false;
|
|
300
306
|
|
|
301
307
|
this.#offlineMessageBuffer.length = 0;
|
|
302
308
|
|
|
303
|
-
await this.#
|
|
309
|
+
await this.#deleteAllConsumers();
|
|
304
310
|
|
|
305
311
|
this.#natsClient.close();
|
|
306
312
|
}else{
|
|
@@ -312,8 +318,20 @@ export class Realtime {
|
|
|
312
318
|
* Start consumers for topics initialized by user
|
|
313
319
|
*/
|
|
314
320
|
async #subscribeToTopics(){
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
this.#topicMap.forEach(async (topic) => {
|
|
322
|
+
// Subscribe to stream
|
|
323
|
+
await this.#startConsumer(topic);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Delete consumers for topics initialized by user
|
|
329
|
+
*/
|
|
330
|
+
async #deleteAllConsumers(){
|
|
331
|
+
for(let i = 0; i < this.#topicMap.length; i++){
|
|
332
|
+
let topic = this.#topicMap[i];
|
|
333
|
+
|
|
334
|
+
await this.#deleteConsumer(topic);
|
|
317
335
|
}
|
|
318
336
|
}
|
|
319
337
|
|
|
@@ -335,6 +353,8 @@ export class Realtime {
|
|
|
335
353
|
this.#topicMap = this.#topicMap.filter(item => item !== topic);
|
|
336
354
|
|
|
337
355
|
delete this.#event_func[topic];
|
|
356
|
+
|
|
357
|
+
return await this.#deleteConsumer(topic);
|
|
338
358
|
}
|
|
339
359
|
|
|
340
360
|
/**
|
|
@@ -360,31 +380,23 @@ export class Realtime {
|
|
|
360
380
|
throw new Error(`Expected $topic type -> string. Instead receieved -> ${typeof topic}`);
|
|
361
381
|
}
|
|
362
382
|
|
|
363
|
-
if(
|
|
364
|
-
this.#event_func[topic] = func;
|
|
365
|
-
}else{
|
|
383
|
+
if(topic in this.#event_func || this.#topicMap.includes(topic)){
|
|
366
384
|
return false
|
|
367
385
|
}
|
|
368
386
|
|
|
369
|
-
|
|
370
|
-
this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND, SERVER_DISCONNECT].includes(topic)){
|
|
371
|
-
if(!this.isTopicValid(topic)){
|
|
372
|
-
// We have an invalid topic, lets remove it
|
|
373
|
-
if(topic in this.#event_func){
|
|
374
|
-
delete this.#event_func[topic];
|
|
375
|
-
}
|
|
387
|
+
this.#event_func[topic] = func;
|
|
376
388
|
|
|
377
|
-
|
|
378
|
-
|
|
389
|
+
if (!this.#reservedSystemTopics.includes(topic)){
|
|
390
|
+
if(!this.isTopicValid(topic)){
|
|
391
|
+
throw new Error("Invalid topic, use isTopicValid($topic) to validate topic")
|
|
392
|
+
}
|
|
379
393
|
|
|
380
|
-
|
|
381
|
-
this.#topicMap.push(topic);
|
|
382
|
-
}
|
|
394
|
+
this.#topicMap.push(topic);
|
|
383
395
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
396
|
+
if(this.connected){
|
|
397
|
+
// Connected we need to create a topic in a stream
|
|
398
|
+
await this.#startConsumer(topic);
|
|
399
|
+
}
|
|
388
400
|
}
|
|
389
401
|
|
|
390
402
|
return true;
|
|
@@ -414,7 +426,7 @@ export class Realtime {
|
|
|
414
426
|
throw new Error("Invalid topic, use isTopicValid($topic) to validate topic")
|
|
415
427
|
}
|
|
416
428
|
|
|
417
|
-
if(!this
|
|
429
|
+
if(!this.isMessageValid(data)){
|
|
418
430
|
throw new Error("$message must be JSON, string or number")
|
|
419
431
|
}
|
|
420
432
|
|
|
@@ -429,15 +441,9 @@ export class Realtime {
|
|
|
429
441
|
"start": Date.now()
|
|
430
442
|
}
|
|
431
443
|
|
|
432
|
-
this.#log("Encoding message via msg pack...")
|
|
433
|
-
var encodedMessage = encode(message);
|
|
434
|
-
|
|
435
444
|
if(this.connected){
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}else{
|
|
439
|
-
this.#log(`${topic} exists locally, moving on...`)
|
|
440
|
-
}
|
|
445
|
+
this.#log("Encoding message via msg pack...")
|
|
446
|
+
var encodedMessage = encode(message);
|
|
441
447
|
|
|
442
448
|
this.#log(`Publishing to topic => ${this.#getStreamTopic(topic)}`)
|
|
443
449
|
|
|
@@ -464,7 +470,6 @@ export class Realtime {
|
|
|
464
470
|
* @param {string} topic
|
|
465
471
|
*/
|
|
466
472
|
async history(topic, start, end){
|
|
467
|
-
this.#log(start)
|
|
468
473
|
if(topic == null || topic == undefined){
|
|
469
474
|
throw new Error("$topic is null or undefined");
|
|
470
475
|
}
|
|
@@ -501,11 +506,16 @@ export class Realtime {
|
|
|
501
506
|
end = end.toISOString();
|
|
502
507
|
}
|
|
503
508
|
|
|
509
|
+
if(!this.connected){
|
|
510
|
+
return [];
|
|
511
|
+
}
|
|
512
|
+
|
|
504
513
|
var opts = {
|
|
505
|
-
name:
|
|
514
|
+
name: `nodejs_${topic}_${uuidv4()}_history_consumer`,
|
|
506
515
|
filter_subjects: [this.#getStreamTopic(topic)],
|
|
507
516
|
replay_policy: ReplayPolicy.Instant,
|
|
508
517
|
opt_start_time: start,
|
|
518
|
+
delivery_policy: DeliverPolicy.StartTime,
|
|
509
519
|
ack_policy: AckPolicy.Explicit,
|
|
510
520
|
}
|
|
511
521
|
|
|
@@ -534,7 +544,12 @@ export class Realtime {
|
|
|
534
544
|
var data = decode(msg.data);
|
|
535
545
|
this.#log(data);
|
|
536
546
|
|
|
537
|
-
history.push(
|
|
547
|
+
history.push({
|
|
548
|
+
"id": data.id,
|
|
549
|
+
"topic": data.room,
|
|
550
|
+
"message": data.message,
|
|
551
|
+
"timestamp": msg.timestamp
|
|
552
|
+
});
|
|
538
553
|
}
|
|
539
554
|
|
|
540
555
|
var del = await consumer.delete();
|
|
@@ -581,45 +596,43 @@ export class Realtime {
|
|
|
581
596
|
* Starts consumer for particular topic if stream exists
|
|
582
597
|
* @param {string} topic
|
|
583
598
|
*/
|
|
584
|
-
async #startConsumer(){
|
|
585
|
-
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
599
|
+
async #startConsumer(topic){
|
|
600
|
+
const consumerName = `nodejs_${topic}_${uuidv4()}_consumer`;
|
|
588
601
|
|
|
589
602
|
var opts = {
|
|
590
|
-
name:
|
|
591
|
-
filter_subjects: [this.#getStreamTopic(
|
|
603
|
+
name: consumerName,
|
|
604
|
+
filter_subjects: [this.#getStreamTopic(topic)],
|
|
592
605
|
replay_policy: ReplayPolicy.Instant,
|
|
593
606
|
opt_start_time: new Date(),
|
|
594
607
|
ack_policy: AckPolicy.Explicit,
|
|
595
608
|
delivery_policy: DeliverPolicy.New
|
|
596
609
|
}
|
|
597
610
|
|
|
598
|
-
|
|
611
|
+
const consumer = await this.#jetstream.consumers.get(this.#getStreamName(), opts);
|
|
599
612
|
this.#log(this.#topicMap)
|
|
600
613
|
|
|
601
|
-
|
|
614
|
+
this.#consumerMap[topic] = consumer;
|
|
615
|
+
|
|
616
|
+
await consumer.consume({
|
|
602
617
|
callback: async (msg) => {
|
|
603
618
|
try{
|
|
604
619
|
const now = Date.now();
|
|
620
|
+
msg.working()
|
|
605
621
|
this.#log("Decoding msgpack message...")
|
|
606
622
|
var data = decode(msg.data);
|
|
607
623
|
|
|
608
|
-
var
|
|
624
|
+
var msgTopic = this.#stripStreamHash(msg.subject);
|
|
609
625
|
|
|
610
626
|
this.#log(data);
|
|
611
627
|
|
|
612
628
|
// Push topic message to main thread
|
|
613
629
|
if (data.client_id != this.#getClientId()){
|
|
614
|
-
var
|
|
615
|
-
this.#log(topics)
|
|
630
|
+
var topicMatch = this.#topicPatternMatcher(topic, msgTopic)
|
|
616
631
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
this.#event_func[top]({
|
|
632
|
+
if(topicMatch){
|
|
633
|
+
this.#event_func[topic]({
|
|
621
634
|
"id": data.id,
|
|
622
|
-
"topic":
|
|
635
|
+
"topic": msgTopic,
|
|
623
636
|
"data": data.message
|
|
624
637
|
});
|
|
625
638
|
}
|
|
@@ -637,15 +650,20 @@ export class Realtime {
|
|
|
637
650
|
this.#log("Consumer is consuming");
|
|
638
651
|
}
|
|
639
652
|
|
|
640
|
-
async #deleteConsumer(){
|
|
653
|
+
async #deleteConsumer(topic){
|
|
654
|
+
this.#log(topic)
|
|
655
|
+
const consumer = this.#consumerMap[topic]
|
|
656
|
+
|
|
641
657
|
var del = false;
|
|
642
658
|
|
|
643
|
-
if (
|
|
644
|
-
del = await
|
|
659
|
+
if (consumer != null && consumer != undefined){
|
|
660
|
+
del = await consumer.delete();
|
|
645
661
|
}else{
|
|
646
662
|
del = false
|
|
647
663
|
}
|
|
648
664
|
|
|
665
|
+
delete this.#consumerMap[topic];
|
|
666
|
+
|
|
649
667
|
return del;
|
|
650
668
|
}
|
|
651
669
|
|
|
@@ -655,11 +673,6 @@ export class Realtime {
|
|
|
655
673
|
return;
|
|
656
674
|
}
|
|
657
675
|
|
|
658
|
-
if(this.#latency.length >= 100){
|
|
659
|
-
this.#log("Latency array is full, skipping log");
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
676
|
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
664
677
|
|
|
665
678
|
this.#log(`Timezone: ${timeZone}`);
|
|
@@ -676,7 +689,7 @@ export class Realtime {
|
|
|
676
689
|
this.#latencyPush = setTimeout(async () => {
|
|
677
690
|
this.#log("setTimeout called");
|
|
678
691
|
|
|
679
|
-
if(this.#latency.length > 0 && this.connected){
|
|
692
|
+
if(this.#latency.length > 0 && this.connected && !this.#isSendingLatency){
|
|
680
693
|
this.#log("Push from setTimeout")
|
|
681
694
|
await this.#pushLatencyData({
|
|
682
695
|
timezone: timeZone,
|
|
@@ -689,7 +702,7 @@ export class Realtime {
|
|
|
689
702
|
}, 30000);
|
|
690
703
|
}
|
|
691
704
|
|
|
692
|
-
if(this.#latency.length
|
|
705
|
+
if(this.#latency.length >= 100 && !this.#isSendingLatency){
|
|
693
706
|
this.#log("Push from Length Check: " + this.#latency.length);
|
|
694
707
|
await this.#pushLatencyData({
|
|
695
708
|
timezone: timeZone,
|
|
@@ -745,8 +758,7 @@ export class Realtime {
|
|
|
745
758
|
*/
|
|
746
759
|
isTopicValid(topic){
|
|
747
760
|
if(topic !== null && topic !== undefined && (typeof topic) == "string"){
|
|
748
|
-
var arrayCheck = !
|
|
749
|
-
this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND, SERVER_DISCONNECT].includes(topic);
|
|
761
|
+
var arrayCheck = !this.#reservedSystemTopics.includes(topic);
|
|
750
762
|
|
|
751
763
|
const TOPIC_REGEX = /^(?!.*\$)(?:[A-Za-z0-9_*~-]+(?:\.[A-Za-z0-9_*~-]+)*(?:\.>)?|>)$/u;
|
|
752
764
|
|
|
@@ -758,7 +770,7 @@ export class Realtime {
|
|
|
758
770
|
}
|
|
759
771
|
}
|
|
760
772
|
|
|
761
|
-
|
|
773
|
+
isMessageValid(message){
|
|
762
774
|
if(message == null || message == undefined){
|
|
763
775
|
throw new Error("$message cannot be null / undefined")
|
|
764
776
|
}
|
|
@@ -955,13 +967,21 @@ export class Realtime {
|
|
|
955
967
|
return methodDataOutput;
|
|
956
968
|
}
|
|
957
969
|
|
|
958
|
-
|
|
959
|
-
|
|
970
|
+
#getUserCreds(jwt, secret){
|
|
971
|
+
return `
|
|
972
|
+
-----BEGIN NATS USER JWT-----
|
|
973
|
+
${jwt}
|
|
974
|
+
------END NATS USER JWT------
|
|
975
|
+
|
|
976
|
+
************************* IMPORTANT *************************
|
|
977
|
+
NKEY Seed printed below can be used to sign and prove identity.
|
|
978
|
+
NKEYs are sensitive and should be treated as secrets.
|
|
960
979
|
|
|
961
|
-
|
|
962
|
-
|
|
980
|
+
-----BEGIN USER NKEY SEED-----
|
|
981
|
+
${secret}
|
|
982
|
+
------END USER NKEY SEED------
|
|
963
983
|
|
|
964
|
-
|
|
984
|
+
*************************************************************`
|
|
965
985
|
}
|
|
966
986
|
|
|
967
987
|
// Exposure for tests
|