@hashgrid/sdk 0.2.8 → 0.3.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.
package/README.md CHANGED
@@ -50,9 +50,6 @@ The SDK provides the following resources:
50
50
  - **`User`** - User data model
51
51
  - **`Quota`** - Quota data model
52
52
  - **`Message`** - Message for recv/send operations
53
- - Constructor: `new Message(peer_id, round, message = "", score = null)`
54
- - **`Status`** - Status response from send operations
55
- - Properties: `peer_id`, `round`, `success`
56
53
 
57
54
  ## Examples
58
55
 
package/dist/client.d.ts CHANGED
@@ -6,7 +6,8 @@ export declare class Hashgrid {
6
6
  private timeout;
7
7
  constructor(api_key?: string, base_url?: string, timeout?: number);
8
8
  private _getHeaders;
9
- _request(method: string, endpoint: string, params?: Record<string, any>, json_data?: any): Promise<any>;
9
+ request(method: string, endpoint: string, params?: Record<string, any>, json_data?: any): Promise<any>;
10
10
  _handleResponse(response: Response): Promise<any>;
11
+ stream(method: string, endpoint: string, params?: Record<string, any>, json_data?: any): AsyncGenerator<string>;
11
12
  static connect(api_key?: string, base_url?: string, timeout?: number): Promise<Grid>;
12
13
  }
package/dist/client.js CHANGED
@@ -20,7 +20,7 @@ class Hashgrid {
20
20
  }
21
21
  return headers;
22
22
  }
23
- async _request(method, endpoint, params, json_data) {
23
+ async request(method, endpoint, params, json_data) {
24
24
  let url;
25
25
  if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
26
26
  url = endpoint;
@@ -104,9 +104,58 @@ class Hashgrid {
104
104
  throw new exceptions_1.HashgridAPIError(`Response handling failed: ${error}`);
105
105
  }
106
106
  }
107
+ async *stream(method, endpoint, params, json_data) {
108
+ const base = this.base_url.endsWith("/") ? this.base_url.slice(0, -1) : this.base_url;
109
+ const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
110
+ let url = `${base}${path}`;
111
+ if (params) {
112
+ const searchParams = new URLSearchParams();
113
+ Object.entries(params).forEach(([key, value]) => {
114
+ searchParams.append(key, String(value));
115
+ });
116
+ url += `?${searchParams.toString()}`;
117
+ }
118
+ const headers = this._getHeaders();
119
+ headers["Accept"] = "text/event-stream";
120
+ const options = {
121
+ method,
122
+ headers,
123
+ };
124
+ if (json_data !== undefined) {
125
+ options.body = JSON.stringify(json_data);
126
+ }
127
+ const response = await fetch(url, options);
128
+ if (!response.ok) {
129
+ throw new exceptions_1.HashgridAPIError(`SSE connection failed: ${response.status}`);
130
+ }
131
+ const reader = response.body?.getReader();
132
+ const decoder = new TextDecoder();
133
+ let buffer = "";
134
+ if (!reader) {
135
+ throw new exceptions_1.HashgridAPIError("Response body is not readable");
136
+ }
137
+ try {
138
+ while (true) {
139
+ const { done, value } = await reader.read();
140
+ if (done)
141
+ break;
142
+ buffer += decoder.decode(value, { stream: true });
143
+ const lines = buffer.split("\n\n");
144
+ buffer = lines.pop() || "";
145
+ for (const line of lines) {
146
+ if (line.startsWith("data: ")) {
147
+ yield line.slice(6).trim();
148
+ }
149
+ }
150
+ }
151
+ }
152
+ finally {
153
+ reader.releaseLock();
154
+ }
155
+ }
107
156
  static async connect(api_key, base_url = "https://dna.hashgrid.ai", timeout = 30000) {
108
157
  const client = new Hashgrid(api_key, base_url, timeout);
109
- const data = await client._request("GET", "/api/v1");
158
+ const data = await client.request("GET", "/api/v1");
110
159
  const grid = new resources_1.Grid(data.name, data.tick, client);
111
160
  return grid;
112
161
  }
@@ -40,7 +40,7 @@ export declare class Grid {
40
40
  tick: number;
41
41
  private _client;
42
42
  constructor(name: string, tick: number, client: Hashgrid);
43
- listen(poll_interval?: number): AsyncGenerator<number>;
43
+ listen(): AsyncGenerator<number>;
44
44
  nodes(): AsyncGenerator<Node>;
45
45
  create_node(name: string, message?: string, capacity?: number): Promise<Node>;
46
46
  }
@@ -53,7 +53,7 @@ export declare class Node {
53
53
  private _client;
54
54
  constructor(node_id: string, owner_id: string, name: string, message: string, capacity: number, client: Hashgrid);
55
55
  recv(): Promise<Message[]>;
56
- send(replies: Message[]): Promise<Status[]>;
56
+ send(replies: Message[]): Promise<Message[]>;
57
57
  update(name?: string, message?: string, capacity?: number): Promise<Node>;
58
58
  delete(): Promise<void>;
59
59
  }
package/dist/resources.js CHANGED
@@ -53,35 +53,24 @@ class Grid {
53
53
  this.tick = tick;
54
54
  this._client = client;
55
55
  }
56
- async *listen(poll_interval = 30000) {
57
- let last_tick = -1;
58
- while (true) {
59
- try {
60
- const data = await this._client._request("GET", "/api/v1");
61
- this.name = data.name;
62
- this.tick = data.tick;
63
- const current_tick = this.tick;
64
- if (current_tick !== last_tick) {
65
- yield current_tick;
66
- last_tick = current_tick;
67
- }
68
- await new Promise((resolve) => setTimeout(resolve, poll_interval));
69
- }
70
- catch (error) {
71
- console.warn(`Error while listening for ticks: ${error}`);
72
- await new Promise((resolve) => setTimeout(resolve, poll_interval * 2));
56
+ async *listen() {
57
+ for await (const data of this._client.stream("GET", "/api/v1/listen")) {
58
+ const tick = parseInt(data, 10);
59
+ if (!isNaN(tick) && tick !== this.tick) {
60
+ this.tick = tick;
61
+ yield tick;
73
62
  }
74
63
  }
75
64
  }
76
65
  async *nodes() {
77
- const data = await this._client._request("GET", "/api/v1/node");
66
+ const data = await this._client.request("GET", "/api/v1/node");
78
67
  for (const item of data) {
79
68
  yield new Node(item.node_id, item.owner_id, item.name, item.message, item.capacity, this._client);
80
69
  }
81
70
  }
82
71
  async create_node(name, message = "", capacity = 100) {
83
72
  const json_data = { name, message, capacity };
84
- const data = await this._client._request("POST", "/api/v1/node", undefined, json_data);
73
+ const data = await this._client.request("POST", "/api/v1/node", undefined, json_data);
85
74
  return new Node(data.node_id, data.owner_id, data.name, data.message, data.capacity, this._client);
86
75
  }
87
76
  }
@@ -96,7 +85,7 @@ class Node {
96
85
  this._client = client;
97
86
  }
98
87
  async recv() {
99
- const data = await this._client._request("GET", `/api/v1/node/${this.node_id}/recv`);
88
+ const data = await this._client.request("GET", `/api/v1/node/${this.node_id}/recv`);
100
89
  return data.map((item) => new Message(item.peer_id, item.round, item.message, item.score ?? null));
101
90
  }
102
91
  async send(replies) {
@@ -111,8 +100,8 @@ class Node {
111
100
  }
112
101
  return obj;
113
102
  });
114
- const data = await this._client._request("POST", `/api/v1/node/${this.node_id}/send`, undefined, json_data);
115
- return data.map((item) => new Status(item.peer_id, item.round, item.success));
103
+ const data = await this._client.request("POST", `/api/v1/node/${this.node_id}/send`, undefined, json_data);
104
+ return data.map((item) => new Message(item.peer_id, item.round, item.message, item.score ?? null));
116
105
  }
117
106
  async update(name, message, capacity) {
118
107
  const json_data = {};
@@ -125,7 +114,7 @@ class Node {
125
114
  if (Object.keys(json_data).length === 0) {
126
115
  return this;
127
116
  }
128
- const data = await this._client._request("PUT", `/api/v1/node/${this.node_id}`, undefined, json_data);
117
+ const data = await this._client.request("PUT", `/api/v1/node/${this.node_id}`, undefined, json_data);
129
118
  // Update local attributes
130
119
  if (data.name !== undefined)
131
120
  this.name = data.name;
@@ -136,7 +125,7 @@ class Node {
136
125
  return this;
137
126
  }
138
127
  async delete() {
139
- await this._client._request("DELETE", `/api/v1/node/${this.node_id}`);
128
+ await this._client.request("DELETE", `/api/v1/node/${this.node_id}`);
140
129
  }
141
130
  }
142
131
  exports.Node = Node;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashgrid/sdk",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "Hashgrid TypeScript/JavaScript SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/client.ts CHANGED
@@ -34,7 +34,7 @@ export class Hashgrid {
34
34
  return headers;
35
35
  }
36
36
 
37
- async _request(
37
+ async request(
38
38
  method: string,
39
39
  endpoint: string,
40
40
  params?: Record<string, any>,
@@ -148,13 +148,81 @@ export class Hashgrid {
148
148
  }
149
149
  }
150
150
 
151
+ async *stream(
152
+ method: string,
153
+ endpoint: string,
154
+ params?: Record<string, any>,
155
+ json_data?: any
156
+ ): AsyncGenerator<string> {
157
+ const base = this.base_url.endsWith("/") ? this.base_url.slice(0, -1) : this.base_url;
158
+ const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
159
+ let url = `${base}${path}`;
160
+
161
+ if (params) {
162
+ const searchParams = new URLSearchParams();
163
+ Object.entries(params).forEach(([key, value]) => {
164
+ searchParams.append(key, String(value));
165
+ });
166
+ url += `?${searchParams.toString()}`;
167
+ }
168
+
169
+ const headers = this._getHeaders();
170
+ headers["Accept"] = "text/event-stream";
171
+
172
+ const options: {
173
+ method: string;
174
+ headers: Record<string, string>;
175
+ body?: string;
176
+ } = {
177
+ method,
178
+ headers,
179
+ };
180
+
181
+ if (json_data !== undefined) {
182
+ options.body = JSON.stringify(json_data);
183
+ }
184
+
185
+ const response = await fetch(url, options);
186
+
187
+ if (!response.ok) {
188
+ throw new HashgridAPIError(`SSE connection failed: ${response.status}`);
189
+ }
190
+
191
+ const reader = response.body?.getReader();
192
+ const decoder = new TextDecoder();
193
+ let buffer = "";
194
+
195
+ if (!reader) {
196
+ throw new HashgridAPIError("Response body is not readable");
197
+ }
198
+
199
+ try {
200
+ while (true) {
201
+ const { done, value } = await reader.read();
202
+ if (done) break;
203
+
204
+ buffer += decoder.decode(value, { stream: true });
205
+ const lines = buffer.split("\n\n");
206
+ buffer = lines.pop() || "";
207
+
208
+ for (const line of lines) {
209
+ if (line.startsWith("data: ")) {
210
+ yield line.slice(6).trim();
211
+ }
212
+ }
213
+ }
214
+ } finally {
215
+ reader.releaseLock();
216
+ }
217
+ }
218
+
151
219
  static async connect(
152
220
  api_key?: string,
153
221
  base_url: string = "https://dna.hashgrid.ai",
154
222
  timeout: number = 30000
155
223
  ): Promise<Grid> {
156
224
  const client = new Hashgrid(api_key, base_url, timeout);
157
- const data = await client._request("GET", "/api/v1");
225
+ const data = await client.request("GET", "/api/v1");
158
226
  const grid = new Grid(data.name, data.tick, client);
159
227
  return grid;
160
228
  }
package/src/resources.ts CHANGED
@@ -100,30 +100,18 @@ export class Grid {
100
100
  this._client = client;
101
101
  }
102
102
 
103
- async *listen(poll_interval: number = 30000): AsyncGenerator<number> {
104
- let last_tick = -1;
105
- while (true) {
106
- try {
107
- const data = await this._client._request("GET", "/api/v1");
108
- this.name = data.name;
109
- this.tick = data.tick;
110
- const current_tick = this.tick;
111
-
112
- if (current_tick !== last_tick) {
113
- yield current_tick;
114
- last_tick = current_tick;
115
- }
116
-
117
- await new Promise((resolve) => setTimeout(resolve, poll_interval));
118
- } catch (error) {
119
- console.warn(`Error while listening for ticks: ${error}`);
120
- await new Promise((resolve) => setTimeout(resolve, poll_interval * 2));
103
+ async *listen(): AsyncGenerator<number> {
104
+ for await (const data of this._client.stream("GET", "/api/v1/listen")) {
105
+ const tick = parseInt(data, 10);
106
+ if (!isNaN(tick) && tick !== this.tick) {
107
+ this.tick = tick;
108
+ yield tick;
121
109
  }
122
110
  }
123
111
  }
124
112
 
125
113
  async *nodes(): AsyncGenerator<Node> {
126
- const data = await this._client._request("GET", "/api/v1/node");
114
+ const data = await this._client.request("GET", "/api/v1/node");
127
115
  for (const item of data) {
128
116
  yield new Node(
129
117
  item.node_id,
@@ -142,7 +130,7 @@ export class Grid {
142
130
  capacity: number = 100
143
131
  ): Promise<Node> {
144
132
  const json_data = { name, message, capacity };
145
- const data = await this._client._request("POST", "/api/v1/node", undefined, json_data);
133
+ const data = await this._client.request("POST", "/api/v1/node", undefined, json_data);
146
134
  return new Node(
147
135
  data.node_id,
148
136
  data.owner_id,
@@ -179,7 +167,7 @@ export class Node {
179
167
  }
180
168
 
181
169
  async recv(): Promise<Message[]> {
182
- const data = await this._client._request(
170
+ const data = await this._client.request(
183
171
  "GET",
184
172
  `/api/v1/node/${this.node_id}/recv`
185
173
  );
@@ -189,7 +177,7 @@ export class Node {
189
177
  );
190
178
  }
191
179
 
192
- async send(replies: Message[]): Promise<Status[]> {
180
+ async send(replies: Message[]): Promise<Message[]> {
193
181
  const json_data = replies.map((msg) => {
194
182
  const obj: any = {
195
183
  peer_id: msg.peer_id,
@@ -201,13 +189,16 @@ export class Node {
201
189
  }
202
190
  return obj;
203
191
  });
204
- const data = await this._client._request(
192
+ const data = await this._client.request(
205
193
  "POST",
206
194
  `/api/v1/node/${this.node_id}/send`,
207
195
  undefined,
208
196
  json_data
209
197
  );
210
- return data.map((item: any) => new Status(item.peer_id, item.round, item.success));
198
+ return data.map(
199
+ (item: any) =>
200
+ new Message(item.peer_id, item.round, item.message, item.score ?? null)
201
+ );
211
202
  }
212
203
 
213
204
  async update(
@@ -224,7 +215,7 @@ export class Node {
224
215
  return this;
225
216
  }
226
217
 
227
- const data = await this._client._request(
218
+ const data = await this._client.request(
228
219
  "PUT",
229
220
  `/api/v1/node/${this.node_id}`,
230
221
  undefined,
@@ -240,7 +231,7 @@ export class Node {
240
231
  }
241
232
 
242
233
  async delete(): Promise<void> {
243
- await this._client._request("DELETE", `/api/v1/node/${this.node_id}`);
234
+ await this._client.request("DELETE", `/api/v1/node/${this.node_id}`);
244
235
  }
245
236
  }
246
237