relayx-webjs 1.0.4 → 1.1.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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__ide__getDiagnostics",
5
+ "Bash(grep:*)"
6
+ ],
7
+ "deny": [],
8
+ "ask": []
9
+ }
10
+ }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ V1.0.5
2
+ - Removed DNS testing code
3
+
1
4
  V1.0.4
2
5
  - Removed DNS Spoofing for testing
3
6
 
package/LICENSE CHANGED
@@ -175,18 +175,7 @@
175
175
 
176
176
  END OF TERMS AND CONDITIONS
177
177
 
178
- APPENDIX: How to apply the Apache License to your work.
179
-
180
- To apply the Apache License to your work, attach the following
181
- boilerplate notice, with the fields enclosed by brackets "[]"
182
- replaced with your own identifying information. (Don't include
183
- the brackets!) The text should be enclosed in the appropriate
184
- comment syntax for the file format. We also recommend that a
185
- file or class name and description of purpose be included on the
186
- same "printed page" as the copyright notice for easier
187
- identification within third-party archives.
188
-
189
- Copyright 2025 TerraOrbital
178
+ Copyright 2025 Terraorbital Networks (OPC) Pvt Ltd
190
179
 
191
180
  Licensed under the Apache License, Version 2.0 (the "License");
192
181
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -1,186 +1,136 @@
1
- # Relay JS Library for the Web
2
- ![License](https://img.shields.io/badge/Apache_2.0-green?label=License)<br>
3
- A powerful library for integrating real-time communication into your web app, powered by the Relay Network.
1
+ # RelayX JavaScript SDK
4
2
 
5
- ## Features
6
- 1. Real-time communication made easy—connect, publish, and subscribe with minimal effort.
7
- 2. Automatic reconnection built-in, with a 2-minute retry window for network disruptions.
8
- 3. Message persistence during reconnection ensures no data loss when the client reconnects.
3
+ ![License](https://img.shields.io/badge/Apache_2.0-green?label=License)
4
+
5
+ A fast, simple SDK for messaging, queues, and key-value storage in JavaScript.
6
+
7
+ ---
8
+
9
+ ## What is RelayX?
10
+
11
+ RelayX is a real-time messaging platform that makes it easy to build distributed systems. It provides pub/sub messaging, durable queues, and a key-value store with a simple API.
12
+
13
+ ---
9
14
 
10
15
  ## Installation
11
- Install the relay library by running the command below in your terminal<br>
12
- `npm install relayx-webjs`
13
-
14
- ## Usage
15
- ### Prerequisites
16
- 1. Obtain API key and Secret key
17
- 2. Initialize the library
18
- ```javascript
19
- import { Realtime, CONNECTED, RECONNECT, DISCONNECTED } from "relayx-webjs"
20
-
21
- var realtime = new Realtime({
22
- api_key: process.env.api_key,
23
- secret: process.env.secret,
24
- });
25
- realtime.init();
26
-
27
- // Initialization of topic listeners go here... (look at examples/example_chat.js for full implementation)
28
-
29
- realtime.connect();
30
-
31
- // Other application logic...
32
- ```
33
-
34
- ### Usage
35
- 1. <b>Publish</b><br>
36
- Send a message to a topic:<br>
37
- ```javascript
38
- var sent = await realtime.publish("power_telemetry", {
39
- "voltage_V": 5,
40
- "current_mA": 400,
41
- "power_W": 2
42
- });
43
-
44
- if(sent){
45
- console.log("Message was successfully sent to topic => power_telemetry");
46
- }else{
47
- console.log("Message was not sent to topic => power_telemetry");
48
- }
49
- ```
50
- 2. <b>Listen</b><br>
51
- Subscribe to a topic to receive messages:<br>
52
- ```javascript
53
- await realtime.on("power_telemetry", (data) => {
54
- console.log(data);
55
- });
56
- ```
57
- 3. <b>Turn Off Listener</b><br>
58
- Unsubscribe from a topic:<br>
59
- ```javascript
60
- var unsubscribed = await realtime.off("power_telemetry");
61
-
62
- if(unsubscribed){
63
- console.log("Successfully unsubscribed from power_telemetry");
64
- }else{
65
- console.log("Unable to unsubscribe from power_telemetry");
66
- }
67
- ```
68
- 4. <b>History</b><br>
69
- Get previously published messages between a start date and end date. Dates are in UTC.
70
- ```javascript
71
- var start = new Date();
72
- var past = start.setDate(start.getDate() - 4) // Get start date from 4 days ago
73
- var startDate = new Date(past)
74
-
75
- var end = new Date();
76
- var past = end.setDate(end.getDate() - 2) // Get end date from 2 days ago
77
- var endDate = new Date(past)
78
-
79
- var history = await realtime.history(topic, startDate, endDate)
80
- ```
81
- The end date is optional. Supplying only the start time will fetch all messages from the start time to now.
82
- ```javascript
83
- var start = new Date();
84
- var past = start.setDate(start.getDate() - 4) // Get start date from 4 days ago
85
- var startDate = new Date(past)
86
-
87
- // This will get all messages from 4 days ago to now
88
- var history = await realtime.history(topic, startDate)
89
- ```
90
- 5. <b>Valid Topic Check</b><br>
91
- Utility function to check if a particular topic is valid
92
- ```javascript
93
- var isValid = realtime.isTopicValid("topic");
94
-
95
- console.log(`Topic Valid => ${isValid}`);
96
- ```
97
- 6. <b>Sleep</b><br>
98
- Utility async function to delay code execution
99
- ```javascript
100
- console.log("Starting code execution...");
101
- await realtime.sleep(2000) // arg is in ms
102
- console.log("This line executed after 2 seconds");
103
- ```
104
- 7. <b>Close Connection to Relay</b><br>
105
- Manually disconnect from the Relay Network
106
- ```javascript
107
- // Logic here
108
-
109
- realtime.close();
110
- ```
111
-
112
- ## System Events
113
- 1. <b>CONNECTED</b><br>
114
- This event is fired when the library connects to the Relay Network.
115
- ```javascript
116
- await realtime.on(CONNECTED, () => {
117
- console.log("Connected to the Relay Network!");
118
- });
119
- ```
120
-
121
- 2. <b>RECONNECT</b><br>
122
- This event is fired when the library reconnects to the Relay Network. This is only fired when the disconnection event is not manual, i.e, disconnection due to network issues.
123
- ```javascript
124
- await realtime.on(RECONNECT, (status) => {
125
- console.log(`Reconnected! => ${status}`);
126
- });
127
- ```
128
- `status` can have values of `RECONNECTING` & `RECONNECTED`.
129
-
130
- `RECONNECTING` => Reconnection attempts have begun. If `status == RECONNECTING`, the `RECONNECT` event is fired every 1 second.<br>
131
- `RECONNECTED` => Reconnected to the Relay Network.
132
- 3. <b>DISCONNECTED</b><br>
133
- This event is fired when the library disconnects from the Relay Network. This includes disconnection due to network issues as well.
134
- ```javascript
135
- await realtime.on(DISCONNECTED, () => {
136
- console.log("Disconnected from the Relay Network");
137
- });
138
- ```
139
- 4. <b>MESSAGE_RESEND</b><br>
140
- This event is fired when the library resends the messages upon reconnection to the Relay Network.
141
- ```javascript
142
- await realtime.on(MESSAGE_RESEND, (messages) => {
143
- console.log("Offline messages may have been resent");
144
- console.log("Messages");
145
- console.log(messages);
146
- });
147
- ```
148
- `messages` is an array of the following object,<br>
149
- ```json
150
- {
151
- "topic": "<topic the message belongs to>",
152
- "message": "<message you sent>",
153
- "resent": "<boolean, indicating if the message was sent successully>"
154
- }
155
- ```
156
-
157
- ## API Reference
158
- 1. init()<br>
159
- Initializes library with configuration options.
160
- * debug (boolean): enables library level logging
161
- 2. connect()<br>
162
- Connects the library to the Relay Network. This is an async function.
163
- 3. close()<br>
164
- Disconnects the library from the Relay Network.
165
- 3. on()<br>
166
- Subscribes to a topic. This is an async function.
167
- * @param {string} topic - Name of the event
168
- * @param {function} func - Callback function to call on user thread
169
- * @returns {boolean} - To check if topic subscription was successful
170
- 3. off()<br>
171
- Deletes reference to user defined event callback for a topic. This will stop listening to a topic. This is an async function.
172
- * @param {string} topic
173
- * @returns {boolean} - To check if topic unsubscribe was successful
174
- 4. history()<br>
175
- Get a list of messages published in the past. This is an async function.<br>
176
- A list of messages can be obtained using a start time and end time. End time is optional. If end time is not specified, all messages from the start time to now is returned.
177
- * @param {string} topic
178
- * @param {Date} start
179
- * @param {Date} end
180
- * @returns {JSON Array} - List of messages published in the past
181
- 5. isTopicValid()<br>
182
- Checks if a topic can be used to send messages to.
183
- * @param {string} topic - Name of event
184
- * @returns {boolean} - If topic is valid or not
185
- 6. sleep()<br>
186
- Pauses code execution for a user defined time. Time passed into the method is in milliseconds. This is an async function.
16
+
17
+ Install the SDK using npm:
18
+
19
+ ```bash
20
+ npm install relayx-webjs
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Quick Start
26
+
27
+ Here's a complete example that publishes and subscribes to a message:
28
+
29
+ ```javascript
30
+ import { Realtime } from 'relayx-webjs';
31
+
32
+ const client = new Realtime({
33
+ api_key: 'your-api-key',
34
+ secret: 'your-secret'
35
+ });
36
+
37
+ await client.init();
38
+ await client.connect();
39
+
40
+ await client.on('chat', (message) => {
41
+ console.log('Received:', message);
42
+ });
43
+
44
+ await client.publish('chat', { text: 'Hello, RelayX!' });
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Messaging (Pub/Sub)
50
+
51
+ ### Publishing a Message
52
+
53
+ ```javascript
54
+ await client.publish('notifications', {
55
+ type: 'alert',
56
+ message: 'System update complete'
57
+ });
58
+ ```
59
+
60
+ ### Subscribing to Messages
61
+
62
+ ```javascript
63
+ await client.on('notifications', (message) => {
64
+ console.log('Notification:', message);
65
+ });
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Queues
71
+
72
+ ### Publishing a Job
73
+
74
+ ```javascript
75
+ const queue = await client.initQueue('queue-id');
76
+
77
+ await queue.publish('image-processing', {
78
+ imageUrl: 'https://example.com/photo.jpg',
79
+ operation: 'resize'
80
+ });
81
+ ```
82
+
83
+ ### Processing Jobs
84
+
85
+ ```javascript
86
+ await queue.consume(
87
+ {
88
+ name: 'image-worker',
89
+ topic: 'image-processing',
90
+ group: 'workers'
91
+ },
92
+ async (job) => {
93
+ console.log('Processing:', job.message);
94
+
95
+ await job.ack();
96
+ }
97
+ );
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Key-Value Store
103
+
104
+ ### Storing Data
105
+
106
+ ```javascript
107
+ const kvStore = await client.initKVStore();
108
+
109
+ await kvStore.put('user:123', {
110
+ name: 'Alice',
111
+ status: 'active'
112
+ });
113
+ ```
114
+
115
+ ### Retrieving Data
116
+
117
+ ```javascript
118
+ const user = await kvStore.get('user:123');
119
+ console.log(user);
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Documentation
125
+
126
+ For complete documentation including delivery guarantees, error handling, limits, and advanced features, visit:
127
+
128
+ **https://docs.relay-x.io**
129
+
130
+ All system behavior and configuration options are documented there.
131
+
132
+ ---
133
+
134
+ ## License
135
+
136
+ This SDK is licensed under the Apache 2.0 License.
@@ -11,9 +11,12 @@ async function run(){
11
11
  api_key: process.env.AUTH_JWT,
12
12
  secret: process.env.AUTH_SECRET
13
13
  });
14
- await realtime.init(false, {
15
- max_retries: 2,
16
- debug: true
14
+ await realtime.init({
15
+ staging: true,
16
+ opts: {
17
+ max_retries: 2,
18
+ debug: true
19
+ }
17
20
  });
18
21
 
19
22
  realtime.on(CONNECTED, async () => {
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "relayx-webjs",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "A powerful library for integrating real-time communication into your webapps, powered by the Relay Network.",
5
5
  "main": "realtime/realtime.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "test": "NODE_ENV=test node tests/test.js"
8
+ "test": "set -o allexport && source .env && set +o allexport && NODE_ENV=test node tests/test.js",
9
+ "test:queue": "set -o allexport && source .env && set +o allexport && NODE_ENV=test node tests/test_queue.js",
10
+ "test:kv": "set -o allexport && source .env && set +o allexport && NODE_ENV=test node tests/test_kv.js"
9
11
  },
10
12
  "repository": {
11
13
  "type": "git",
@@ -25,8 +27,9 @@
25
27
  },
26
28
  "homepage": "https://github.com/Realtime-Relay/relayx-webjs#readme",
27
29
  "dependencies": {
28
- "@msgpack/msgpack": "^3.1.2",
30
+ "@msgpack/msgpack": "3.1.2",
29
31
  "@nats-io/jetstream": "3.0.0-35",
32
+ "@nats-io/kv": "3.2.0",
30
33
  "jest": "30.0.4",
31
34
  "nats.ws": "1.30.3",
32
35
  "uuid": "11.1.0"
@@ -0,0 +1,195 @@
1
+ import { Kvm } from "@nats-io/kv";
2
+ import { ErrorLogging, Logging } from "./utils.js";
3
+
4
+ export class KVStore{
5
+
6
+ #kvManager = null
7
+ #kvStore = null
8
+
9
+ #namespace = null;
10
+
11
+ #logger = null;
12
+ #errorLogger = null;
13
+
14
+ constructor(data){
15
+ this.#namespace = data.namespace;
16
+
17
+ this.#kvManager = new Kvm(data.jetstream)
18
+
19
+ this.#logger = new Logging(data.debug)
20
+
21
+ this.#errorLogger = new ErrorLogging()
22
+ }
23
+
24
+ async init(){
25
+ this.#validateInput()
26
+
27
+ this.#kvStore = await this.#kvManager.open(this.#namespace)
28
+
29
+ return this.#kvStore != null
30
+ }
31
+
32
+ async get(key){
33
+ this.#validateKey(key)
34
+
35
+ try{
36
+ const val = await this.#kvStore.get(key);
37
+
38
+ this.#logger.log("Value for", key)
39
+
40
+ var json = null;
41
+
42
+ json = JSON.parse(val.string())
43
+ return json
44
+ }catch(err){
45
+ this.#errorLogger.logError({
46
+ err: err,
47
+ op: "kv_read"
48
+ })
49
+
50
+ return null
51
+ }
52
+ }
53
+
54
+ async put(key, value){
55
+ this.#validateKey(key)
56
+ this.#validateValue(value)
57
+
58
+ this.#logger.log(`Creating KV pair for ${key}`)
59
+
60
+ try{
61
+ await this.#kvStore.create(key, JSON.stringify(value))
62
+ }catch(err){
63
+ // The assumption here is that the key might already exist
64
+ try{
65
+ await this.#kvStore.put(key, JSON.stringify(value))
66
+ }catch(err2){
67
+ // The key creation failed because we don't have permission
68
+ this.#errorLogger.logError({
69
+ err: err2,
70
+ op: "kv_write"
71
+ })
72
+ }
73
+ }
74
+ }
75
+
76
+ async delete(key){
77
+ this.#validateKey(key)
78
+
79
+ try{
80
+ await this.#kvStore.purge(key);
81
+ }catch(err){
82
+ // The key delete failed because we don't have permission
83
+ this.#errorLogger.logError({
84
+ err: err,
85
+ op: "kv_delete"
86
+ })
87
+ }
88
+
89
+ }
90
+
91
+ async keys(){
92
+ var keys = [];
93
+
94
+ try{
95
+ var qKeys = await this.#kvStore.keys();
96
+
97
+ for await (const key of qKeys) {
98
+ this.#logger.log("Key: ", key);
99
+ keys.push(key);
100
+ }
101
+ }catch(err){
102
+ this.#errorLogger.logError({
103
+ err: err,
104
+ op: "kv_read"
105
+ })
106
+ }
107
+
108
+ return keys;
109
+
110
+ }
111
+
112
+ // Utility functions
113
+ #validateInput(){
114
+ if(this.#namespace === null || this.#namespace === undefined || this.#namespace == ""){
115
+ throw new Error("$namespace cannot be null / undefined / empty")
116
+ }
117
+ }
118
+
119
+ #validateKey(key){
120
+ if(key == null || key == undefined){
121
+ throw new Error("$key cannot be null / undefined")
122
+ }
123
+
124
+ if(typeof key != "string"){
125
+ throw new Error("$key cannot be a string")
126
+ }
127
+
128
+ if(key == ""){
129
+ throw new Error("$key cannot be empty")
130
+ }
131
+
132
+ // Validate key characters: only a-z, A-Z, 0-9, _, -, ., = and / are allowed
133
+ const validKeyPattern = /^[a-zA-Z0-9_\-\.=\/]+$/;
134
+ if(!validKeyPattern.test(key)){
135
+ throw new Error("$key can only contain alphanumeric characters and the following: _ - . = /")
136
+ }
137
+ }
138
+
139
+ #validateValue(value){
140
+ var valueValid = (
141
+ value === null ||
142
+ typeof value == "string" ||
143
+ typeof value == "number" ||
144
+ typeof value == "boolean" ||
145
+ Array.isArray(value) ||
146
+ this.#isJSON(value)
147
+ );
148
+
149
+ if(!valueValid){
150
+ throw new Error(`$value MUST be null, string, number, boolean, array or json! $value is "${typeof value}"`)
151
+ }
152
+ }
153
+
154
+ #isJSON(data){
155
+ try{
156
+ JSON.stringify(data?.toString())
157
+ return true;
158
+ }catch(err){
159
+ return false
160
+ }
161
+ }
162
+
163
+ // Test helper methods - expose private methods/state for testing
164
+ // Only available when process.env.NODE_ENV == "test"
165
+ testGetNamespace(){
166
+ if(process.env.NODE_ENV !== "test") return undefined;
167
+ return this.#namespace;
168
+ }
169
+
170
+ testIsKvStoreInitialized(){
171
+ if(process.env.NODE_ENV !== "test") return undefined;
172
+ return this.#kvStore != null;
173
+ }
174
+
175
+ testValidateKey(){
176
+ if(process.env.NODE_ENV !== "test") return undefined;
177
+ return (key) => this.#validateKey(key);
178
+ }
179
+
180
+ testValidateValue(){
181
+ if(process.env.NODE_ENV !== "test") return undefined;
182
+ return (value) => this.#validateValue(value);
183
+ }
184
+
185
+ testValidateInput(){
186
+ if(process.env.NODE_ENV !== "test") return undefined;
187
+ return () => this.#validateInput();
188
+ }
189
+
190
+ testIsJSON(){
191
+ if(process.env.NODE_ENV !== "test") return undefined;
192
+ return (data) => this.#isJSON(data);
193
+ }
194
+
195
+ }
@@ -0,0 +1,26 @@
1
+
2
+ export default class Message {
3
+ id = null
4
+ message = null
5
+ topic = null
6
+
7
+ msg = null
8
+
9
+ constructor(message){
10
+ this.id = message.id;
11
+
12
+ this.message = message.message;
13
+
14
+ this.topic = message.topic;
15
+
16
+ this.msg = message.msg;
17
+ }
18
+
19
+ ack(){
20
+ this.msg?.ack()
21
+ }
22
+
23
+ nack(millis){
24
+ this.msg?.nak(millis)
25
+ }
26
+ }