@hashgrid/sdk 0.2.8 → 0.3.1
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 +0 -3
- package/dist/client.d.ts +6 -5
- package/dist/client.js +61 -12
- package/dist/exceptions.d.ts +5 -5
- package/dist/exceptions.js +8 -8
- package/dist/resources.d.ts +21 -21
- package/dist/resources.js +43 -54
- package/package.json +1 -1
- package/src/client.ts +85 -18
- package/src/exceptions.ts +9 -10
- package/src/resources.ts +72 -82
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
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/** Main Hashgrid client class. */
|
|
2
2
|
import { Grid } from "./resources";
|
|
3
3
|
export declare class Hashgrid {
|
|
4
|
-
private
|
|
5
|
-
private
|
|
4
|
+
private apiKey?;
|
|
5
|
+
private baseUrl;
|
|
6
6
|
private timeout;
|
|
7
|
-
constructor(
|
|
7
|
+
constructor(apiKey?: string, baseUrl?: string, timeout?: number);
|
|
8
8
|
private _getHeaders;
|
|
9
|
-
|
|
9
|
+
request(method: string, endpoint: string, params?: Record<string, any>, jsonData?: any): Promise<any>;
|
|
10
10
|
_handleResponse(response: Response): Promise<any>;
|
|
11
|
-
|
|
11
|
+
stream(method: string, endpoint: string, params?: Record<string, any>, jsonData?: any): AsyncGenerator<string>;
|
|
12
|
+
static connect(apiKey?: string, baseUrl?: string, timeout?: number): Promise<Grid>;
|
|
12
13
|
}
|
package/dist/client.js
CHANGED
|
@@ -5,9 +5,9 @@ exports.Hashgrid = void 0;
|
|
|
5
5
|
const exceptions_1 = require("./exceptions");
|
|
6
6
|
const resources_1 = require("./resources");
|
|
7
7
|
class Hashgrid {
|
|
8
|
-
constructor(
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
8
|
+
constructor(apiKey, baseUrl = "https://dna.hashgrid.ai", timeout = 30000) {
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
11
11
|
this.timeout = timeout;
|
|
12
12
|
}
|
|
13
13
|
_getHeaders() {
|
|
@@ -15,18 +15,18 @@ class Hashgrid {
|
|
|
15
15
|
"Content-Type": "application/json",
|
|
16
16
|
Accept: "application/json",
|
|
17
17
|
};
|
|
18
|
-
if (this.
|
|
19
|
-
headers["Authorization"] = `Bearer ${this.
|
|
18
|
+
if (this.apiKey) {
|
|
19
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
20
20
|
}
|
|
21
21
|
return headers;
|
|
22
22
|
}
|
|
23
|
-
async
|
|
23
|
+
async request(method, endpoint, params, jsonData) {
|
|
24
24
|
let url;
|
|
25
25
|
if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
|
|
26
26
|
url = endpoint;
|
|
27
27
|
}
|
|
28
28
|
else {
|
|
29
|
-
const base = this.
|
|
29
|
+
const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
30
30
|
const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
31
31
|
url = `${base}${path}`;
|
|
32
32
|
}
|
|
@@ -45,8 +45,8 @@ class Hashgrid {
|
|
|
45
45
|
headers,
|
|
46
46
|
signal: controller.signal,
|
|
47
47
|
};
|
|
48
|
-
if (
|
|
49
|
-
options.body = JSON.stringify(
|
|
48
|
+
if (jsonData !== undefined) {
|
|
49
|
+
options.body = JSON.stringify(jsonData);
|
|
50
50
|
}
|
|
51
51
|
try {
|
|
52
52
|
const response = await fetch(url, options);
|
|
@@ -104,9 +104,58 @@ class Hashgrid {
|
|
|
104
104
|
throw new exceptions_1.HashgridAPIError(`Response handling failed: ${error}`);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const
|
|
107
|
+
async *stream(method, endpoint, params, jsonData) {
|
|
108
|
+
const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
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 (jsonData !== undefined) {
|
|
125
|
+
options.body = JSON.stringify(jsonData);
|
|
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
|
+
}
|
|
156
|
+
static async connect(apiKey, baseUrl = "https://dna.hashgrid.ai", timeout = 30000) {
|
|
157
|
+
const client = new Hashgrid(apiKey, baseUrl, timeout);
|
|
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
|
}
|
package/dist/exceptions.d.ts
CHANGED
|
@@ -3,16 +3,16 @@ export declare class HashgridError extends Error {
|
|
|
3
3
|
constructor(message: string);
|
|
4
4
|
}
|
|
5
5
|
export declare class HashgridAPIError extends HashgridError {
|
|
6
|
-
|
|
6
|
+
statusCode?: number;
|
|
7
7
|
response?: Response;
|
|
8
|
-
constructor(message: string,
|
|
8
|
+
constructor(message: string, statusCode?: number, response?: Response);
|
|
9
9
|
}
|
|
10
10
|
export declare class HashgridAuthenticationError extends HashgridAPIError {
|
|
11
|
-
constructor(message: string,
|
|
11
|
+
constructor(message: string, statusCode?: number, response?: Response);
|
|
12
12
|
}
|
|
13
13
|
export declare class HashgridNotFoundError extends HashgridAPIError {
|
|
14
|
-
constructor(message: string,
|
|
14
|
+
constructor(message: string, statusCode?: number, response?: Response);
|
|
15
15
|
}
|
|
16
16
|
export declare class HashgridValidationError extends HashgridAPIError {
|
|
17
|
-
constructor(message: string,
|
|
17
|
+
constructor(message: string, statusCode?: number, response?: Response);
|
|
18
18
|
}
|
package/dist/exceptions.js
CHANGED
|
@@ -11,34 +11,34 @@ class HashgridError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
exports.HashgridError = HashgridError;
|
|
13
13
|
class HashgridAPIError extends HashgridError {
|
|
14
|
-
constructor(message,
|
|
14
|
+
constructor(message, statusCode, response) {
|
|
15
15
|
super(message);
|
|
16
16
|
this.name = "HashgridAPIError";
|
|
17
|
-
this.
|
|
17
|
+
this.statusCode = statusCode;
|
|
18
18
|
this.response = response;
|
|
19
19
|
Object.setPrototypeOf(this, HashgridAPIError.prototype);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
exports.HashgridAPIError = HashgridAPIError;
|
|
23
23
|
class HashgridAuthenticationError extends HashgridAPIError {
|
|
24
|
-
constructor(message,
|
|
25
|
-
super(message,
|
|
24
|
+
constructor(message, statusCode, response) {
|
|
25
|
+
super(message, statusCode, response);
|
|
26
26
|
this.name = "HashgridAuthenticationError";
|
|
27
27
|
Object.setPrototypeOf(this, HashgridAuthenticationError.prototype);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
exports.HashgridAuthenticationError = HashgridAuthenticationError;
|
|
31
31
|
class HashgridNotFoundError extends HashgridAPIError {
|
|
32
|
-
constructor(message,
|
|
33
|
-
super(message,
|
|
32
|
+
constructor(message, statusCode, response) {
|
|
33
|
+
super(message, statusCode, response);
|
|
34
34
|
this.name = "HashgridNotFoundError";
|
|
35
35
|
Object.setPrototypeOf(this, HashgridNotFoundError.prototype);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
exports.HashgridNotFoundError = HashgridNotFoundError;
|
|
39
39
|
class HashgridValidationError extends HashgridAPIError {
|
|
40
|
-
constructor(message,
|
|
41
|
-
super(message,
|
|
40
|
+
constructor(message, statusCode, response) {
|
|
41
|
+
super(message, statusCode, response);
|
|
42
42
|
this.name = "HashgridValidationError";
|
|
43
43
|
Object.setPrototypeOf(this, HashgridValidationError.prototype);
|
|
44
44
|
}
|
package/dist/resources.d.ts
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
/** Hashgrid API resources. */
|
|
2
2
|
import { Hashgrid } from "./client";
|
|
3
3
|
export declare class User {
|
|
4
|
-
|
|
4
|
+
userId: string;
|
|
5
5
|
name: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
constructor(
|
|
6
|
+
isSuperuser: boolean;
|
|
7
|
+
quotaId: string;
|
|
8
|
+
constructor(userId: string, name: string, isSuperuser: boolean, quotaId: string);
|
|
9
9
|
}
|
|
10
10
|
export declare class Quota {
|
|
11
|
-
|
|
11
|
+
quotaId: string;
|
|
12
12
|
name: string;
|
|
13
13
|
capacity: number;
|
|
14
|
-
constructor(
|
|
14
|
+
constructor(quotaId: string, name: string, capacity: number);
|
|
15
15
|
}
|
|
16
16
|
export declare class Edge {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
nodeId: string;
|
|
18
|
+
peerId: string;
|
|
19
|
+
recvMessage: string;
|
|
20
|
+
sendMessage: string | null;
|
|
21
21
|
score: number | null;
|
|
22
22
|
round: number;
|
|
23
|
-
constructor(
|
|
23
|
+
constructor(nodeId: string, peerId: string, recvMessage: string, sendMessage: string | null, score: number | null, round: number);
|
|
24
24
|
}
|
|
25
25
|
export declare class Message {
|
|
26
|
-
|
|
26
|
+
peerId: string;
|
|
27
27
|
round: number;
|
|
28
28
|
message: string;
|
|
29
29
|
score: number | null;
|
|
30
|
-
constructor(
|
|
30
|
+
constructor(peerId: string, round: number, message?: string, score?: number | null);
|
|
31
31
|
}
|
|
32
32
|
export declare class Status {
|
|
33
|
-
|
|
33
|
+
peerId: string;
|
|
34
34
|
round: number;
|
|
35
35
|
success: boolean;
|
|
36
|
-
constructor(
|
|
36
|
+
constructor(peerId: string, round: number, success: boolean);
|
|
37
37
|
}
|
|
38
38
|
export declare class Grid {
|
|
39
39
|
name: string;
|
|
40
40
|
tick: number;
|
|
41
41
|
private _client;
|
|
42
42
|
constructor(name: string, tick: number, client: Hashgrid);
|
|
43
|
-
listen(
|
|
43
|
+
listen(): AsyncGenerator<number>;
|
|
44
44
|
nodes(): AsyncGenerator<Node>;
|
|
45
|
-
|
|
45
|
+
createNode(name: string, message?: string, capacity?: number): Promise<Node>;
|
|
46
46
|
}
|
|
47
47
|
export declare class Node {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
nodeId: string;
|
|
49
|
+
ownerId: string;
|
|
50
50
|
name: string;
|
|
51
51
|
message: string;
|
|
52
52
|
capacity: number;
|
|
53
53
|
private _client;
|
|
54
|
-
constructor(
|
|
54
|
+
constructor(nodeId: string, ownerId: string, name: string, message: string, capacity: number, client: Hashgrid);
|
|
55
55
|
recv(): Promise<Message[]>;
|
|
56
|
-
send(replies: Message[]): Promise<
|
|
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
|
@@ -3,36 +3,36 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.Node = exports.Grid = exports.Status = exports.Message = exports.Edge = exports.Quota = exports.User = void 0;
|
|
5
5
|
class User {
|
|
6
|
-
constructor(
|
|
7
|
-
this.
|
|
6
|
+
constructor(userId, name, isSuperuser, quotaId) {
|
|
7
|
+
this.userId = userId;
|
|
8
8
|
this.name = name;
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
9
|
+
this.isSuperuser = isSuperuser;
|
|
10
|
+
this.quotaId = quotaId;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
exports.User = User;
|
|
14
14
|
class Quota {
|
|
15
|
-
constructor(
|
|
16
|
-
this.
|
|
15
|
+
constructor(quotaId, name, capacity) {
|
|
16
|
+
this.quotaId = quotaId;
|
|
17
17
|
this.name = name;
|
|
18
18
|
this.capacity = capacity;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
exports.Quota = Quota;
|
|
22
22
|
class Edge {
|
|
23
|
-
constructor(
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
23
|
+
constructor(nodeId, peerId, recvMessage, sendMessage, score, round) {
|
|
24
|
+
this.nodeId = nodeId;
|
|
25
|
+
this.peerId = peerId;
|
|
26
|
+
this.recvMessage = recvMessage;
|
|
27
|
+
this.sendMessage = sendMessage;
|
|
28
28
|
this.score = score;
|
|
29
29
|
this.round = round;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
exports.Edge = Edge;
|
|
33
33
|
class Message {
|
|
34
|
-
constructor(
|
|
35
|
-
this.
|
|
34
|
+
constructor(peerId, round, message = "", score = null) {
|
|
35
|
+
this.peerId = peerId;
|
|
36
36
|
this.round = round;
|
|
37
37
|
this.message = message;
|
|
38
38
|
this.score = score;
|
|
@@ -40,8 +40,8 @@ class Message {
|
|
|
40
40
|
}
|
|
41
41
|
exports.Message = Message;
|
|
42
42
|
class Status {
|
|
43
|
-
constructor(
|
|
44
|
-
this.
|
|
43
|
+
constructor(peerId, round, success) {
|
|
44
|
+
this.peerId = peerId;
|
|
45
45
|
this.round = round;
|
|
46
46
|
this.success = success;
|
|
47
47
|
}
|
|
@@ -53,56 +53,45 @@ class Grid {
|
|
|
53
53
|
this.tick = tick;
|
|
54
54
|
this._client = client;
|
|
55
55
|
}
|
|
56
|
-
async *listen(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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.
|
|
66
|
+
const data = await this._client.request("GET", "/api/v1/node");
|
|
78
67
|
for (const item of data) {
|
|
79
|
-
yield new Node(item.node_id, item.owner_id, item.name, item.message, item.capacity, this._client);
|
|
68
|
+
yield new Node(item.node_id || item.nodeId, item.owner_id || item.ownerId, item.name, item.message, item.capacity, this._client);
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
|
-
async
|
|
83
|
-
const
|
|
84
|
-
const data = await this._client.
|
|
85
|
-
return new Node(data.node_id, data.owner_id, data.name, data.message, data.capacity, this._client);
|
|
71
|
+
async createNode(name, message = "", capacity = 100) {
|
|
72
|
+
const jsonData = { name, message, capacity };
|
|
73
|
+
const data = await this._client.request("POST", "/api/v1/node", undefined, jsonData);
|
|
74
|
+
return new Node(data.node_id || data.nodeId, data.owner_id || data.ownerId, data.name, data.message, data.capacity, this._client);
|
|
86
75
|
}
|
|
87
76
|
}
|
|
88
77
|
exports.Grid = Grid;
|
|
89
78
|
class Node {
|
|
90
|
-
constructor(
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
79
|
+
constructor(nodeId, ownerId, name, message, capacity, client) {
|
|
80
|
+
this.nodeId = nodeId;
|
|
81
|
+
this.ownerId = ownerId;
|
|
93
82
|
this.name = name;
|
|
94
83
|
this.message = message;
|
|
95
84
|
this.capacity = capacity;
|
|
96
85
|
this._client = client;
|
|
97
86
|
}
|
|
98
87
|
async recv() {
|
|
99
|
-
const data = await this._client.
|
|
100
|
-
return data.map((item) => new Message(item.peer_id, item.round, item.message, item.score ?? null));
|
|
88
|
+
const data = await this._client.request("GET", `/api/v1/node/${this.nodeId}/recv`);
|
|
89
|
+
return data.map((item) => new Message(item.peer_id || item.peerId, item.round, item.message, item.score ?? null));
|
|
101
90
|
}
|
|
102
91
|
async send(replies) {
|
|
103
|
-
const
|
|
92
|
+
const jsonData = replies.map((msg) => {
|
|
104
93
|
const obj = {
|
|
105
|
-
peer_id: msg.
|
|
94
|
+
peer_id: msg.peerId,
|
|
106
95
|
message: msg.message,
|
|
107
96
|
round: msg.round,
|
|
108
97
|
};
|
|
@@ -111,21 +100,21 @@ class Node {
|
|
|
111
100
|
}
|
|
112
101
|
return obj;
|
|
113
102
|
});
|
|
114
|
-
const data = await this._client.
|
|
115
|
-
return data.map((item) => new
|
|
103
|
+
const data = await this._client.request("POST", `/api/v1/node/${this.nodeId}/send`, undefined, jsonData);
|
|
104
|
+
return data.map((item) => new Message(item.peer_id || item.peerId, item.round, item.message, item.score ?? null));
|
|
116
105
|
}
|
|
117
106
|
async update(name, message, capacity) {
|
|
118
|
-
const
|
|
107
|
+
const jsonData = {};
|
|
119
108
|
if (name !== undefined)
|
|
120
|
-
|
|
109
|
+
jsonData.name = name;
|
|
121
110
|
if (message !== undefined)
|
|
122
|
-
|
|
111
|
+
jsonData.message = message;
|
|
123
112
|
if (capacity !== undefined)
|
|
124
|
-
|
|
125
|
-
if (Object.keys(
|
|
113
|
+
jsonData.capacity = capacity;
|
|
114
|
+
if (Object.keys(jsonData).length === 0) {
|
|
126
115
|
return this;
|
|
127
116
|
}
|
|
128
|
-
const data = await this._client.
|
|
117
|
+
const data = await this._client.request("PUT", `/api/v1/node/${this.nodeId}`, undefined, jsonData);
|
|
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.
|
|
128
|
+
await this._client.request("DELETE", `/api/v1/node/${this.nodeId}`);
|
|
140
129
|
}
|
|
141
130
|
}
|
|
142
131
|
exports.Node = Node;
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -9,17 +9,17 @@ import {
|
|
|
9
9
|
import { Grid } from "./resources";
|
|
10
10
|
|
|
11
11
|
export class Hashgrid {
|
|
12
|
-
private
|
|
13
|
-
private
|
|
12
|
+
private apiKey?: string;
|
|
13
|
+
private baseUrl: string;
|
|
14
14
|
private timeout: number;
|
|
15
15
|
|
|
16
16
|
constructor(
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
apiKey?: string,
|
|
18
|
+
baseUrl: string = "https://dna.hashgrid.ai",
|
|
19
19
|
timeout: number = 30000
|
|
20
20
|
) {
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
21
|
+
this.apiKey = apiKey;
|
|
22
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
23
23
|
this.timeout = timeout;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -28,23 +28,23 @@ export class Hashgrid {
|
|
|
28
28
|
"Content-Type": "application/json",
|
|
29
29
|
Accept: "application/json",
|
|
30
30
|
};
|
|
31
|
-
if (this.
|
|
32
|
-
headers["Authorization"] = `Bearer ${this.
|
|
31
|
+
if (this.apiKey) {
|
|
32
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
33
33
|
}
|
|
34
34
|
return headers;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
async
|
|
37
|
+
async request(
|
|
38
38
|
method: string,
|
|
39
39
|
endpoint: string,
|
|
40
40
|
params?: Record<string, any>,
|
|
41
|
-
|
|
41
|
+
jsonData?: any
|
|
42
42
|
): Promise<any> {
|
|
43
43
|
let url: string;
|
|
44
44
|
if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
|
|
45
45
|
url = endpoint;
|
|
46
46
|
} else {
|
|
47
|
-
const base = this.
|
|
47
|
+
const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
48
48
|
const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
49
49
|
url = `${base}${path}`;
|
|
50
50
|
}
|
|
@@ -72,8 +72,8 @@ export class Hashgrid {
|
|
|
72
72
|
signal: controller.signal,
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
-
if (
|
|
76
|
-
options.body = JSON.stringify(
|
|
75
|
+
if (jsonData !== undefined) {
|
|
76
|
+
options.body = JSON.stringify(jsonData);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
try {
|
|
@@ -148,15 +148,82 @@ export class Hashgrid {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
async *stream(
|
|
152
|
+
method: string,
|
|
153
|
+
endpoint: string,
|
|
154
|
+
params?: Record<string, any>,
|
|
155
|
+
jsonData?: any
|
|
156
|
+
): AsyncGenerator<string> {
|
|
157
|
+
const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
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 (jsonData !== undefined) {
|
|
182
|
+
options.body = JSON.stringify(jsonData);
|
|
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
|
-
|
|
153
|
-
|
|
220
|
+
apiKey?: string,
|
|
221
|
+
baseUrl: string = "https://dna.hashgrid.ai",
|
|
154
222
|
timeout: number = 30000
|
|
155
223
|
): Promise<Grid> {
|
|
156
|
-
const client = new Hashgrid(
|
|
157
|
-
const data = await client.
|
|
224
|
+
const client = new Hashgrid(apiKey, baseUrl, timeout);
|
|
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
|
}
|
|
161
229
|
}
|
|
162
|
-
|
package/src/exceptions.ts
CHANGED
|
@@ -9,39 +9,38 @@ export class HashgridError extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export class HashgridAPIError extends HashgridError {
|
|
12
|
-
|
|
12
|
+
statusCode?: number;
|
|
13
13
|
response?: Response;
|
|
14
14
|
|
|
15
|
-
constructor(message: string,
|
|
15
|
+
constructor(message: string, statusCode?: number, response?: Response) {
|
|
16
16
|
super(message);
|
|
17
17
|
this.name = "HashgridAPIError";
|
|
18
|
-
this.
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
19
|
this.response = response;
|
|
20
20
|
Object.setPrototypeOf(this, HashgridAPIError.prototype);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export class HashgridAuthenticationError extends HashgridAPIError {
|
|
25
|
-
constructor(message: string,
|
|
26
|
-
super(message,
|
|
25
|
+
constructor(message: string, statusCode?: number, response?: Response) {
|
|
26
|
+
super(message, statusCode, response);
|
|
27
27
|
this.name = "HashgridAuthenticationError";
|
|
28
28
|
Object.setPrototypeOf(this, HashgridAuthenticationError.prototype);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export class HashgridNotFoundError extends HashgridAPIError {
|
|
33
|
-
constructor(message: string,
|
|
34
|
-
super(message,
|
|
33
|
+
constructor(message: string, statusCode?: number, response?: Response) {
|
|
34
|
+
super(message, statusCode, response);
|
|
35
35
|
this.name = "HashgridNotFoundError";
|
|
36
36
|
Object.setPrototypeOf(this, HashgridNotFoundError.prototype);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export class HashgridValidationError extends HashgridAPIError {
|
|
41
|
-
constructor(message: string,
|
|
42
|
-
super(message,
|
|
41
|
+
constructor(message: string, statusCode?: number, response?: Response) {
|
|
42
|
+
super(message, statusCode, response);
|
|
43
43
|
this.name = "HashgridValidationError";
|
|
44
44
|
Object.setPrototypeOf(this, HashgridValidationError.prototype);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
|
package/src/resources.ts
CHANGED
|
@@ -3,74 +3,74 @@
|
|
|
3
3
|
import { Hashgrid } from "./client";
|
|
4
4
|
|
|
5
5
|
export class User {
|
|
6
|
-
|
|
6
|
+
userId: string;
|
|
7
7
|
name: string;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
isSuperuser: boolean;
|
|
9
|
+
quotaId: string;
|
|
10
10
|
|
|
11
11
|
constructor(
|
|
12
|
-
|
|
12
|
+
userId: string,
|
|
13
13
|
name: string,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
isSuperuser: boolean,
|
|
15
|
+
quotaId: string
|
|
16
16
|
) {
|
|
17
|
-
this.
|
|
17
|
+
this.userId = userId;
|
|
18
18
|
this.name = name;
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
19
|
+
this.isSuperuser = isSuperuser;
|
|
20
|
+
this.quotaId = quotaId;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export class Quota {
|
|
25
|
-
|
|
25
|
+
quotaId: string;
|
|
26
26
|
name: string;
|
|
27
27
|
capacity: number;
|
|
28
28
|
|
|
29
|
-
constructor(
|
|
30
|
-
this.
|
|
29
|
+
constructor(quotaId: string, name: string, capacity: number) {
|
|
30
|
+
this.quotaId = quotaId;
|
|
31
31
|
this.name = name;
|
|
32
32
|
this.capacity = capacity;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export class Edge {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
nodeId: string;
|
|
38
|
+
peerId: string;
|
|
39
|
+
recvMessage: string;
|
|
40
|
+
sendMessage: string | null;
|
|
41
41
|
score: number | null;
|
|
42
42
|
round: number;
|
|
43
43
|
|
|
44
44
|
constructor(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
nodeId: string,
|
|
46
|
+
peerId: string,
|
|
47
|
+
recvMessage: string,
|
|
48
|
+
sendMessage: string | null,
|
|
49
49
|
score: number | null,
|
|
50
50
|
round: number
|
|
51
51
|
) {
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
52
|
+
this.nodeId = nodeId;
|
|
53
|
+
this.peerId = peerId;
|
|
54
|
+
this.recvMessage = recvMessage;
|
|
55
|
+
this.sendMessage = sendMessage;
|
|
56
56
|
this.score = score;
|
|
57
57
|
this.round = round;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export class Message {
|
|
62
|
-
|
|
62
|
+
peerId: string;
|
|
63
63
|
round: number;
|
|
64
64
|
message: string;
|
|
65
65
|
score: number | null;
|
|
66
66
|
|
|
67
67
|
constructor(
|
|
68
|
-
|
|
68
|
+
peerId: string,
|
|
69
69
|
round: number,
|
|
70
70
|
message: string = "",
|
|
71
71
|
score: number | null = null
|
|
72
72
|
) {
|
|
73
|
-
this.
|
|
73
|
+
this.peerId = peerId;
|
|
74
74
|
this.round = round;
|
|
75
75
|
this.message = message;
|
|
76
76
|
this.score = score;
|
|
@@ -78,12 +78,12 @@ export class Message {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
export class Status {
|
|
81
|
-
|
|
81
|
+
peerId: string;
|
|
82
82
|
round: number;
|
|
83
83
|
success: boolean;
|
|
84
84
|
|
|
85
|
-
constructor(
|
|
86
|
-
this.
|
|
85
|
+
constructor(peerId: string, round: number, success: boolean) {
|
|
86
|
+
this.peerId = peerId;
|
|
87
87
|
this.round = round;
|
|
88
88
|
this.success = success;
|
|
89
89
|
}
|
|
@@ -100,34 +100,22 @@ export class Grid {
|
|
|
100
100
|
this._client = client;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
async *listen(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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.
|
|
114
|
+
const data = await this._client.request("GET", "/api/v1/node");
|
|
127
115
|
for (const item of data) {
|
|
128
116
|
yield new Node(
|
|
129
|
-
item.node_id,
|
|
130
|
-
item.owner_id,
|
|
117
|
+
item.node_id || item.nodeId,
|
|
118
|
+
item.owner_id || item.ownerId,
|
|
131
119
|
item.name,
|
|
132
120
|
item.message,
|
|
133
121
|
item.capacity,
|
|
@@ -136,16 +124,16 @@ export class Grid {
|
|
|
136
124
|
}
|
|
137
125
|
}
|
|
138
126
|
|
|
139
|
-
async
|
|
127
|
+
async createNode(
|
|
140
128
|
name: string,
|
|
141
129
|
message: string = "",
|
|
142
130
|
capacity: number = 100
|
|
143
131
|
): Promise<Node> {
|
|
144
|
-
const
|
|
145
|
-
const data = await this._client.
|
|
132
|
+
const jsonData = { name, message, capacity };
|
|
133
|
+
const data = await this._client.request("POST", "/api/v1/node", undefined, jsonData);
|
|
146
134
|
return new Node(
|
|
147
|
-
data.node_id,
|
|
148
|
-
data.owner_id,
|
|
135
|
+
data.node_id || data.nodeId,
|
|
136
|
+
data.owner_id || data.ownerId,
|
|
149
137
|
data.name,
|
|
150
138
|
data.message,
|
|
151
139
|
data.capacity,
|
|
@@ -155,23 +143,23 @@ export class Grid {
|
|
|
155
143
|
}
|
|
156
144
|
|
|
157
145
|
export class Node {
|
|
158
|
-
|
|
159
|
-
|
|
146
|
+
nodeId: string;
|
|
147
|
+
ownerId: string;
|
|
160
148
|
name: string;
|
|
161
149
|
message: string;
|
|
162
150
|
capacity: number;
|
|
163
151
|
private _client: Hashgrid;
|
|
164
152
|
|
|
165
153
|
constructor(
|
|
166
|
-
|
|
167
|
-
|
|
154
|
+
nodeId: string,
|
|
155
|
+
ownerId: string,
|
|
168
156
|
name: string,
|
|
169
157
|
message: string,
|
|
170
158
|
capacity: number,
|
|
171
159
|
client: Hashgrid
|
|
172
160
|
) {
|
|
173
|
-
this.
|
|
174
|
-
this.
|
|
161
|
+
this.nodeId = nodeId;
|
|
162
|
+
this.ownerId = ownerId;
|
|
175
163
|
this.name = name;
|
|
176
164
|
this.message = message;
|
|
177
165
|
this.capacity = capacity;
|
|
@@ -179,20 +167,20 @@ export class Node {
|
|
|
179
167
|
}
|
|
180
168
|
|
|
181
169
|
async recv(): Promise<Message[]> {
|
|
182
|
-
const data = await this._client.
|
|
170
|
+
const data = await this._client.request(
|
|
183
171
|
"GET",
|
|
184
|
-
`/api/v1/node/${this.
|
|
172
|
+
`/api/v1/node/${this.nodeId}/recv`
|
|
185
173
|
);
|
|
186
174
|
return data.map(
|
|
187
175
|
(item: any) =>
|
|
188
|
-
new Message(item.peer_id, item.round, item.message, item.score ?? null)
|
|
176
|
+
new Message(item.peer_id || item.peerId, item.round, item.message, item.score ?? null)
|
|
189
177
|
);
|
|
190
178
|
}
|
|
191
179
|
|
|
192
|
-
async send(replies: Message[]): Promise<
|
|
193
|
-
const
|
|
180
|
+
async send(replies: Message[]): Promise<Message[]> {
|
|
181
|
+
const jsonData = replies.map((msg) => {
|
|
194
182
|
const obj: any = {
|
|
195
|
-
peer_id: msg.
|
|
183
|
+
peer_id: msg.peerId,
|
|
196
184
|
message: msg.message,
|
|
197
185
|
round: msg.round,
|
|
198
186
|
};
|
|
@@ -201,13 +189,16 @@ export class Node {
|
|
|
201
189
|
}
|
|
202
190
|
return obj;
|
|
203
191
|
});
|
|
204
|
-
const data = await this._client.
|
|
192
|
+
const data = await this._client.request(
|
|
205
193
|
"POST",
|
|
206
|
-
`/api/v1/node/${this.
|
|
194
|
+
`/api/v1/node/${this.nodeId}/send`,
|
|
207
195
|
undefined,
|
|
208
|
-
|
|
196
|
+
jsonData
|
|
197
|
+
);
|
|
198
|
+
return data.map(
|
|
199
|
+
(item: any) =>
|
|
200
|
+
new Message(item.peer_id || item.peerId, item.round, item.message, item.score ?? null)
|
|
209
201
|
);
|
|
210
|
-
return data.map((item: any) => new Status(item.peer_id, item.round, item.success));
|
|
211
202
|
}
|
|
212
203
|
|
|
213
204
|
async update(
|
|
@@ -215,20 +206,20 @@ export class Node {
|
|
|
215
206
|
message?: string,
|
|
216
207
|
capacity?: number
|
|
217
208
|
): Promise<Node> {
|
|
218
|
-
const
|
|
219
|
-
if (name !== undefined)
|
|
220
|
-
if (message !== undefined)
|
|
221
|
-
if (capacity !== undefined)
|
|
209
|
+
const jsonData: any = {};
|
|
210
|
+
if (name !== undefined) jsonData.name = name;
|
|
211
|
+
if (message !== undefined) jsonData.message = message;
|
|
212
|
+
if (capacity !== undefined) jsonData.capacity = capacity;
|
|
222
213
|
|
|
223
|
-
if (Object.keys(
|
|
214
|
+
if (Object.keys(jsonData).length === 0) {
|
|
224
215
|
return this;
|
|
225
216
|
}
|
|
226
217
|
|
|
227
|
-
const data = await this._client.
|
|
218
|
+
const data = await this._client.request(
|
|
228
219
|
"PUT",
|
|
229
|
-
`/api/v1/node/${this.
|
|
220
|
+
`/api/v1/node/${this.nodeId}`,
|
|
230
221
|
undefined,
|
|
231
|
-
|
|
222
|
+
jsonData
|
|
232
223
|
);
|
|
233
224
|
|
|
234
225
|
// Update local attributes
|
|
@@ -240,7 +231,6 @@ export class Node {
|
|
|
240
231
|
}
|
|
241
232
|
|
|
242
233
|
async delete(): Promise<void> {
|
|
243
|
-
await this._client.
|
|
234
|
+
await this._client.request("DELETE", `/api/v1/node/${this.nodeId}`);
|
|
244
235
|
}
|
|
245
236
|
}
|
|
246
|
-
|