relayx-js 1.0.8 → 1.0.10

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
@@ -66,7 +66,7 @@ Unsubscribe from a topic:<br>
66
66
  }
67
67
  ```
68
68
  4. <b>History</b><br>
69
- Get previously published messages between a start date and end date
69
+ Get previously published messages between a start date and end date. Dates are in UTC.
70
70
  ```javascript
71
71
  var start = new Date();
72
72
  var past = start.setDate(start.getDate() - 4) // Get start date from 4 days ago
@@ -11,7 +11,7 @@ async function run(){
11
11
  api_key: process.env.user_key,
12
12
  secret: process.env.secret
13
13
  });
14
- await realtime.init(true, {
14
+ await realtime.init(false, {
15
15
  max_retries: 2,
16
16
  debug: true
17
17
  });
@@ -28,12 +28,12 @@ async function run(){
28
28
  console.log(`[IMPL] DISONNECT`)
29
29
  });
30
30
 
31
- await realtime.on("hello", (data) => {
32
- console.log("hello", data);
31
+ await realtime.on("power-telemetry", (data) => {
32
+ console.log("power-telemetry", data);
33
33
  });
34
34
 
35
- await realtime.on("hello1", (data) => {
36
- console.log("hello1", data);
35
+ await realtime.on("test232", (data) => {
36
+ console.log("test232", data);
37
37
  });
38
38
 
39
39
  realtime.on(MESSAGE_RESEND, (data) => {
@@ -57,11 +57,11 @@ async function run(){
57
57
  var pastDate = new Date(past)
58
58
 
59
59
  var end = new Date();
60
- var past = end.setDate(end.getDate() - 2)
60
+ var past = end.setDate(end.getDate())
61
61
  var endDate = new Date(past)
62
62
 
63
- var history = await realtime.history(topic, pastDate, endDate)
64
- // console.log(history)
63
+ var history = await realtime.history(topic, pastDate)
64
+ console.log(history)
65
65
  })
66
66
  }else if(input == "off"){
67
67
  rl.question("topic to off(): ", async (topic) => {
@@ -83,9 +83,7 @@ async function run(){
83
83
  })
84
84
  }else{
85
85
  rl.question("topic: ", async (topic) => {
86
- var output = await realtime.publish(topic, {
87
- "data": input
88
- });
86
+ var output = await realtime.publish(topic, input);
89
87
  })
90
88
  }
91
89
  });
@@ -7,7 +7,7 @@ async function run(){
7
7
  });
8
8
  await realtime.init(true, {
9
9
  max_retries: 2,
10
- // debug: true
10
+ debug: true
11
11
  });
12
12
 
13
13
  realtime.on(CONNECTED, async () => {
@@ -16,12 +16,6 @@ async function run(){
16
16
  for (let angle = 0; angle <= 18000; angle++){
17
17
 
18
18
  var value = Math.floor(Math.random() * (100 + 1))
19
- console.log(value)
20
-
21
- // await realtime.publish("test-power-1", {
22
- // "value": value,
23
- // "time": Date.now() + 2000
24
- // })
25
19
 
26
20
  var sent = await realtime.publish("power-telemetry", {
27
21
  "value": value,
package/package.json CHANGED
@@ -1,20 +1,28 @@
1
1
  {
2
2
  "name": "relayx-js",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "main": "realtime/realtime.js",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "test": "NODE_ENV=test node tests/test.js"
8
8
  },
9
- "keywords": ["realtime", "realtime-communication", "relay", "relay-x", "relay-x-js"],
9
+ "keywords": [
10
+ "realtime",
11
+ "realtime-communication",
12
+ "relay",
13
+ "relay-x",
14
+ "relay-x-js"
15
+ ],
10
16
  "author": "Relay",
11
17
  "license": "Apache 2.0",
12
18
  "description": "A powerful library for integrating real-time communication into your software stack, powered by the Relay Network.",
13
19
  "dependencies": {
20
+ "@msgpack/msgpack": "^3.1.1",
14
21
  "@nats-io/jetstream": "^3.0.0-35",
15
- "axios": "1.7.7",
22
+ "axios": "^1.8.4",
16
23
  "jest": "^29.7.0",
17
- "nats": "^2.28.2"
24
+ "nats": "^2.28.2",
25
+ "uuid": "^11.1.0"
18
26
  },
19
27
  "devDependencies": {
20
28
  "@babel/core": "^7.26.0",
@@ -1,7 +1,8 @@
1
1
  import axios from 'axios';
2
2
  import { connect, JSONCodec, Events, DebugEvents, AckPolicy, ReplayPolicy, credsAuthenticator } from "nats";
3
- import { DeliverPolicy, jetstream, jetstreamManager } from "@nats-io/jetstream";
4
- import { readFileSync } from "fs"
3
+ import { DeliverPolicy, jetstream } from "@nats-io/jetstream";
4
+ import { encode, decode } from "@msgpack/msgpack";
5
+ import { v4 as uuidv4 } from 'uuid';
5
6
 
6
7
  export class Realtime {
7
8
 
@@ -10,8 +11,6 @@ export class Realtime {
10
11
  #natsClient = null;
11
12
  #codec = JSONCodec();
12
13
  #jetstream = null;
13
- #jsManager = null;
14
- #streamTracker = [];
15
14
  #consumerMap = {};
16
15
 
17
16
  #event_func = {};
@@ -36,9 +35,6 @@ export class Realtime {
36
35
  // Offline messages
37
36
  #offlineMessageBuffer = [];
38
37
 
39
- // Test Variables
40
- #timeout = 1000;
41
-
42
38
  #maxPublishRetries = 5;
43
39
 
44
40
  constructor(config){
@@ -62,6 +58,7 @@ export class Realtime {
62
58
  }
63
59
 
64
60
  this.namespace = null;
61
+ this.topicHash = null;
65
62
  }
66
63
 
67
64
  /*
@@ -114,7 +111,8 @@ export class Realtime {
114
111
  "nats://0.0.0.0:4223",
115
112
  "nats://0.0.0.0:4224",
116
113
  "nats://0.0.0.0:4225",
117
- "nats://0.0.0.0:4226"] :
114
+ "nats://0.0.0.0:4226"
115
+ ] :
118
116
  [
119
117
  "nats://api.relay-x.io:4221",
120
118
  "nats://api.relay-x.io:4222",
@@ -160,8 +158,10 @@ export class Realtime {
160
158
 
161
159
  if(data["status"] == "NAMESPACE_RETRIEVE_SUCCESS"){
162
160
  this.namespace = data["data"]["namespace"]
161
+ this.topicHash = data["data"]["hash"]
163
162
  }else{
164
163
  this.namespace = null;
164
+ this.topicHash = null;
165
165
  return
166
166
  }
167
167
  }
@@ -185,10 +185,9 @@ export class Realtime {
185
185
  reconnect: true,
186
186
  reconnectTimeWait: 1000,
187
187
  authenticator: credsAuth,
188
- token: this.api_key
188
+ token: this.api_key,
189
189
  });
190
190
 
191
- this.#jsManager = await jetstreamManager(this.#natsClient);
192
191
  this.#jetstream = await jetstream(this.#natsClient);
193
192
 
194
193
  await this.#getNameSpace()
@@ -224,7 +223,6 @@ export class Realtime {
224
223
  this.#log(`client disconnected - ${s.data}`);
225
224
 
226
225
  this.connected = false;
227
- this.#streamTracker = [];
228
226
  this.#consumerMap = {};
229
227
 
230
228
  if (DISCONNECTED in this.#event_func){
@@ -247,8 +245,6 @@ export class Realtime {
247
245
  this.reconnecting = false;
248
246
  this.connected = true;
249
247
 
250
- // this.#subscribeToTopics();
251
-
252
248
  if(RECONNECT in this.#event_func){
253
249
  this.#event_func[RECONNECT](this.#RECONNECTED);
254
250
  }
@@ -279,6 +275,7 @@ export class Realtime {
279
275
 
280
276
  // Subscribe to topics
281
277
  this.#subscribeToTopics();
278
+ this.#log("Subscribed to topics");
282
279
  }
283
280
  }
284
281
 
@@ -358,7 +355,7 @@ export class Realtime {
358
355
  }
359
356
 
360
357
  if (![CONNECTED, DISCONNECTED, RECONNECT, this.#RECONNECTED,
361
- this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND].includes(topic)){
358
+ this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND, SERVER_DISCONNECT].includes(topic)){
362
359
  if(!this.isTopicValid(topic)){
363
360
  // We have an invalid topic, lets remove it
364
361
  if(topic in this.#event_func){
@@ -416,13 +413,12 @@ export class Realtime {
416
413
  "start": Date.now()
417
414
  }
418
415
 
419
- var encodedMessage = this.#codec.encode(message)
416
+ this.#log("Encoding message via msg pack...")
417
+ var encodedMessage = encode(message);
420
418
 
421
419
  if(this.connected){
422
420
  if(!this.#topicMap.includes(topic)){
423
421
  this.#topicMap.push(topic);
424
-
425
- await this.#createOrGetStream();
426
422
  }else{
427
423
  this.#log(`${topic} exists locally, moving on...`)
428
424
  }
@@ -485,12 +481,8 @@ export class Realtime {
485
481
  end = end.toISOString();
486
482
  }
487
483
 
488
- console.log(`END => ${end}`)
489
-
490
- await this.#createOrGetStream();
491
-
492
484
  var opts = {
493
- name: topic,
485
+ name: `${topic}_${uuidv4()}_history`,
494
486
  filter_subjects: [this.#getStreamTopic(topic)],
495
487
  replay_policy: ReplayPolicy.Instant,
496
488
  opt_start_time: start,
@@ -501,30 +493,33 @@ export class Realtime {
501
493
  this.#log(this.#topicMap)
502
494
  this.#log("Consumer is consuming");
503
495
 
504
- this.#consumerMap[topic] = consumer;
505
-
506
- const msgs = await consumer.consume();
507
-
508
496
  var history = [];
509
497
 
510
- for await (const m of msgs){
511
- m.ack();
498
+ while(true){
499
+ var msg = await consumer.next({
500
+ expires: 1000
501
+ });
502
+
503
+ if(msg == null){
504
+ break;
505
+ }
512
506
 
513
507
  if(end != null || end != undefined){
514
- if(m.timestamp > end){
508
+ if(msg.timestamp > end){
515
509
  break
516
510
  }
517
511
  }
518
512
 
519
- console.log(m.timestamp)
520
-
521
- var data = m.json();
513
+ this.#log("Decoding msgpack message...")
514
+ var data = decode(msg.data);
515
+ this.#log(data);
516
+
522
517
  history.push(data.message);
523
518
  }
524
519
 
525
520
  var del = await consumer.delete();
526
521
 
527
- this.#log("History pull done", del)
522
+ this.#log("History pull done: " + del);
528
523
 
529
524
  return history;
530
525
  }
@@ -567,10 +562,8 @@ export class Realtime {
567
562
  * @param {string} topic
568
563
  */
569
564
  async #startConsumer(topic){
570
- await this.#createOrGetStream();
571
-
572
565
  var opts = {
573
- name: topic,
566
+ name: `${topic}_${uuidv4()}`,
574
567
  filter_subjects: [this.#getStreamTopic(topic), this.#getStreamTopic(topic) + "_presence"],
575
568
  replay_policy: ReplayPolicy.Instant,
576
569
  opt_start_time: new Date(),
@@ -580,16 +573,15 @@ export class Realtime {
580
573
 
581
574
  const consumer = await this.#jetstream.consumers.get(this.#getStreamName(), opts);
582
575
  this.#log(this.#topicMap)
583
- this.#log("Consumer is consuming");
584
576
 
585
577
  this.#consumerMap[topic] = consumer;
586
578
 
587
579
  await consumer.consume({
588
580
  callback: (msg) => {
589
-
590
- msg.ack();
591
581
  try{
592
- var data = this.#codec.decode(msg.data);
582
+ this.#log("Decoding msgpack message...")
583
+ var data = decode(msg.data);
584
+
593
585
  var room = data.room;
594
586
 
595
587
  this.#log(data);
@@ -603,12 +595,15 @@ export class Realtime {
603
595
  "data": data.message
604
596
  });
605
597
  }
598
+
599
+ msg.ack();
606
600
  }catch(err){
607
601
  this.#log("Consumer err " + err);
608
- msg.nack();
602
+ msg.nack(5000);
609
603
  }
610
604
  }
611
605
  });
606
+ this.#log("Consumer is consuming");
612
607
  }
613
608
 
614
609
  /**
@@ -631,39 +626,6 @@ export class Realtime {
631
626
  return del;
632
627
  }
633
628
 
634
- /**
635
- * Gets stream if it exists or creates one
636
- * @param {string} streamName
637
- */
638
- async #createOrGetStream(){
639
- const streamName = this.#getStreamName();
640
- var stream = null;
641
-
642
- try{
643
- stream = await this.#jsManager.streams.info(streamName);
644
- }catch(err){
645
- stream = null;
646
- }
647
-
648
- this.#log(`STREAM => ${stream}`)
649
-
650
- if (stream == null){
651
- // Stream does not exist, create one
652
- await this.#jsManager.streams.add({
653
- name: streamName,
654
- subjects: [...this.#getStreamTopicList(), ...this.#getPresenceTopics()],
655
- });
656
-
657
- this.#log(`${streamName} created`);
658
- }else{
659
- var subs = [...stream.config.subjects, ...this.#getStreamTopicList(), ...this.#getPresenceTopics()];
660
- stream.config.subjects = [...new Set(subs)];
661
- await this.#jsManager.streams.update(streamName, stream.config);
662
-
663
- this.#log(`${streamName} exists, updating and moving on...`);
664
- }
665
- }
666
-
667
629
  // Utility functions
668
630
  #getClientId(){
669
631
  return this.#natsClient?.info?.client_id
@@ -677,9 +639,9 @@ export class Realtime {
677
639
  isTopicValid(topic){
678
640
  if(topic !== null && topic !== undefined && (typeof topic) == "string"){
679
641
  var arrayCheck = ![CONNECTED, DISCONNECTED, RECONNECT, this.#RECONNECTED,
680
- this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND].includes(topic);
642
+ this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND, SERVER_DISCONNECT].includes(topic);
681
643
 
682
- var spaceStarCheck = !topic.includes(" ") && !topic.includes("*");
644
+ var spaceStarCheck = !topic.includes(" ") && !topic.includes("*") && !topic.includes(".");
683
645
 
684
646
  return arrayCheck && spaceStarCheck;
685
647
  }else{
@@ -697,34 +659,14 @@ export class Realtime {
697
659
  }
698
660
 
699
661
  #getStreamTopic(topic){
700
- if(this.namespace != null){
701
- return this.namespace + "_stream_" + topic;
662
+ if(this.topicHash != null){
663
+ return this.topicHash + "." + topic;
702
664
  }else{
703
665
  this.close();
704
- throw new Error("$namespace is null. Cannot initialize program with null $namespace")
666
+ throw new Error("$topicHash is null. Cannot initialize program with null $topicHash")
705
667
  }
706
668
  }
707
669
 
708
- #getStreamTopicList(){
709
- var topics = [];
710
-
711
- this.#topicMap.forEach((topic) => {
712
- topics.push(this.#getStreamTopic(topic))
713
- })
714
-
715
- return topics
716
- }
717
-
718
- #getPresenceTopics(){
719
- var presence = [];
720
-
721
- this.#topicMap.forEach((topic) => {
722
- presence.push(this.#getStreamTopic(topic) + "_presence")
723
- })
724
-
725
- return presence
726
- }
727
-
728
670
  sleep(ms) {
729
671
  return new Promise(resolve => setTimeout(resolve, ms));
730
672
  }
@@ -861,4 +803,5 @@ export class Realtime {
861
803
  export const CONNECTED = "CONNECTED";
862
804
  export const RECONNECT = "RECONNECT";
863
805
  export const MESSAGE_RESEND = "MESSAGE_RESEND";
864
- export const DISCONNECTED = "DISCONNECTED";
806
+ export const DISCONNECTED = "DISCONNECTED";
807
+ export const SERVER_DISCONNECT = "SERVER_DISCONNECT";
package/tests/test.js CHANGED
@@ -116,6 +116,7 @@ test('init() function test', async () => {
116
116
 
117
117
  test("Namespace check test", async () => {
118
118
  assert.strictEqual(realTimeEnabled.namespace.length > 0, true)
119
+ assert.strictEqual(realTimeEnabled.topicHash.length > 0, true)
119
120
  });
120
121
 
121
122
  test("Retry method test", async () => {
@@ -401,17 +402,19 @@ test("Get stream name test", () => {
401
402
  });
402
403
 
403
404
  realtime.namespace = "spacex-dragon-program"
405
+ realtime.topicHash = "topic_hash";
404
406
 
405
407
  var getStreamName = realtime.testGetStreamName();
406
408
  var getStreamTopic = realtime.testGetStreamTopic();
407
409
 
408
410
  var name = getStreamName();
409
- assert.strictEqual(name, "spacex-dragon-program_stream")
411
+ assert.strictEqual(name, `${realtime.namespace}_stream`);
410
412
 
411
413
  var topic = getStreamTopic("hello_world")
412
- assert.strictEqual(topic, "spacex-dragon-program_stream_hello_world")
414
+ assert.strictEqual(topic, `${realtime.topicHash}.hello_world`)
413
415
 
414
416
  realtime.namespace = null;
417
+ realtime.topicHash = null;
415
418
 
416
419
  assert.throws(() => {
417
420
  getStreamName();
@@ -422,7 +425,7 @@ test("Get stream name test", () => {
422
425
  assert.throws(() => {
423
426
  getStreamTopic("hello_world");
424
427
  },
425
- new Error("$namespace is null. Cannot initialize program with null $namespace"),
428
+ new Error("$topicHash is null. Cannot initialize program with null $topicHash"),
426
429
  "Expected error was not thrown")
427
430
  });
428
431
 
@@ -508,19 +511,4 @@ test("History test", async () => {
508
511
  },
509
512
  new Error("$start must be a Date object"),
510
513
  "Expected error was not thrown");
511
-
512
- await realTimeEnabled.history("hello", new Date());
513
-
514
- await realTimeEnabled.history("hello", new Date(), new Date());
515
-
516
- var start = new Date();
517
- var past = start.setDate(start.getDate() - 4)
518
- var pastDate = new Date(past)
519
-
520
- var end = new Date();
521
- var past = end.setDate(end.getDate() - 2)
522
- var endDate = new Date(past)
523
-
524
- var history = await realTimeEnabled.history("hello", pastDate, endDate)
525
- assert.strictEqual(history.length > 0, true)
526
514
  })