@otonoma/paranet-client 2.11.0-rc.18

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.
Files changed (75) hide show
  1. package/README.md +2 -0
  2. package/dist/client.d.ts +168 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +497 -0
  5. package/dist/graphql.d.ts +8 -0
  6. package/dist/graphql.d.ts.map +1 -0
  7. package/dist/graphql.js +113 -0
  8. package/dist/index.d.ts +12 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +11 -0
  11. package/dist/pncp.d.ts +129 -0
  12. package/dist/pncp.d.ts.map +1 -0
  13. package/dist/pncp.js +100 -0
  14. package/dist/proto/broker.grpc.client.d.ts +154 -0
  15. package/dist/proto/broker.grpc.client.d.ts.map +1 -0
  16. package/dist/proto/broker.grpc.client.js +102 -0
  17. package/dist/proto/broker.grpc.d.ts +6 -0
  18. package/dist/proto/broker.grpc.d.ts.map +1 -0
  19. package/dist/proto/broker.grpc.js +32 -0
  20. package/dist/proto/broker_api.d.ts +324 -0
  21. package/dist/proto/broker_api.d.ts.map +1 -0
  22. package/dist/proto/broker_api.js +566 -0
  23. package/dist/proto/google/protobuf/descriptor.d.ts +2492 -0
  24. package/dist/proto/google/protobuf/descriptor.d.ts.map +1 -0
  25. package/dist/proto/google/protobuf/descriptor.js +3250 -0
  26. package/dist/proto/google/protobuf/timestamp.d.ts +157 -0
  27. package/dist/proto/google/protobuf/timestamp.d.ts.map +1 -0
  28. package/dist/proto/google/protobuf/timestamp.js +132 -0
  29. package/dist/proto/grpc/health/v1/health.client.d.ts +79 -0
  30. package/dist/proto/grpc/health/v1/health.client.d.ts.map +1 -0
  31. package/dist/proto/grpc/health/v1/health.client.js +46 -0
  32. package/dist/proto/grpc/health/v1/health.d.ts +74 -0
  33. package/dist/proto/grpc/health/v1/health.d.ts.map +1 -0
  34. package/dist/proto/grpc/health/v1/health.js +152 -0
  35. package/dist/proto/identifiers.d.ts +82 -0
  36. package/dist/proto/identifiers.d.ts.map +1 -0
  37. package/dist/proto/identifiers.js +132 -0
  38. package/dist/proto/mediums.d.ts +71 -0
  39. package/dist/proto/mediums.d.ts.map +1 -0
  40. package/dist/proto/mediums.js +120 -0
  41. package/dist/proto/observation.d.ts +287 -0
  42. package/dist/proto/observation.d.ts.map +1 -0
  43. package/dist/proto/observation.js +443 -0
  44. package/dist/proto/otonoma/common/value.d.ts +127 -0
  45. package/dist/proto/otonoma/common/value.d.ts.map +1 -0
  46. package/dist/proto/otonoma/common/value.js +248 -0
  47. package/dist/proto/pncp.d.ts +607 -0
  48. package/dist/proto/pncp.d.ts.map +1 -0
  49. package/dist/proto/pncp.js +936 -0
  50. package/dist/schema/paranet.d.ts +245 -0
  51. package/dist/schema/paranet.d.ts.map +1 -0
  52. package/dist/schema/paranet.js +7 -0
  53. package/dist/util.d.ts +5 -0
  54. package/dist/util.d.ts.map +1 -0
  55. package/dist/util.js +120 -0
  56. package/package.json +39 -0
  57. package/src/client.ts +677 -0
  58. package/src/graphql.ts +103 -0
  59. package/src/index.ts +14 -0
  60. package/src/pncp.ts +236 -0
  61. package/src/proto/broker.grpc.client.ts +193 -0
  62. package/src/proto/broker.grpc.ts +32 -0
  63. package/src/proto/broker_api.ts +778 -0
  64. package/src/proto/google/protobuf/descriptor.ts +4860 -0
  65. package/src/proto/google/protobuf/timestamp.ts +288 -0
  66. package/src/proto/grpc/health/v1/health.client.ts +106 -0
  67. package/src/proto/grpc/health/v1/health.ts +174 -0
  68. package/src/proto/identifiers.ts +176 -0
  69. package/src/proto/mediums.ts +168 -0
  70. package/src/proto/observation.ts +636 -0
  71. package/src/proto/otonoma/common/value.ts +334 -0
  72. package/src/proto/pncp.ts +1333 -0
  73. package/src/schema/paranet.ts +257 -0
  74. package/src/util.ts +129 -0
  75. package/tsconfig.json +27 -0
package/src/client.ts ADDED
@@ -0,0 +1,677 @@
1
+ // Wrapper client around the paranet broker and data service.
2
+
3
+ import { BrokerClient } from "./proto/broker.grpc.client";
4
+ import { ConversationId, ConversationMembership } from "./proto/identifiers";
5
+ import {
6
+ PncpRequest,
7
+ PncpMessage,
8
+ PncpCallback,
9
+ SkillMatchStrategy,
10
+ PncpMessageKind,
11
+ } from "./proto/pncp";
12
+ import { LoginRequest, MessageResponse, PncpCallbackRequest, RefreshTokenRequest } from "./proto/broker_api";
13
+ import { jwtDecode } from "jwt-decode";
14
+ import { ObservationCallback, ObservationMessageWrapper, ObservationRequest } from "./proto/observation";
15
+
16
+ import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
17
+ import { RpcError, RpcMetadata, RpcOptions, ServerStreamingCall, UnaryCall } from "@protobuf-ts/runtime-rpc";
18
+ import { fromJsonValue } from "./util";
19
+ import { Value } from "./proto/otonoma/common/value";
20
+ import { MessageRequest, PncpMessageObject, SkillRequest } from "./pncp";
21
+ import { HealthClient } from "./proto/grpc/health/v1/health.client";
22
+ import { HealthCheckRequest, HealthCheckResponse_ServingStatus } from "./proto/grpc/health/v1/health";
23
+
24
+ export interface ParanetClientInit {
25
+ endpoint: string;
26
+ actorId: string;
27
+ }
28
+
29
+ export interface PncpClientInit {
30
+ endpoint: string;
31
+ actorId: string;
32
+ actorVersion?: string;
33
+ token?: string;
34
+ refreshToken?: string;
35
+ }
36
+
37
+ export class PncpClient {
38
+ transport: GrpcWebFetchTransport;
39
+ inner: BrokerClient;
40
+ health: HealthClient;
41
+ endpoint: string;
42
+ actorId: string;
43
+ actorVersion?: string;
44
+ token?: string;
45
+ refreshToken?: string;
46
+ tokenExp?: Date;
47
+ tokenCb?: (token: string, refresh: string) => void;
48
+
49
+ constructor(init: PncpClientInit) {
50
+ this.actorId = init.actorId;
51
+ this.endpoint = init.endpoint;
52
+ this.actorVersion = init.actorVersion;
53
+ // TODO: Consider passing in transport config.
54
+ this.transport = new GrpcWebFetchTransport({
55
+ baseUrl: init.endpoint,
56
+ format: "binary",
57
+ // TODO: Consider other methods here.
58
+ });
59
+ this.inner = new BrokerClient(this.transport);
60
+ this.health = new HealthClient(this.transport);
61
+ }
62
+
63
+ public async healthCheck(service: string = ""): Promise<boolean> {
64
+ const req: HealthCheckRequest = { service };
65
+ const response = this.health.check(req);
66
+ await response.status;
67
+ const data = await response.response;
68
+ return data.status === HealthCheckResponse_ServingStatus.SERVING;
69
+ }
70
+
71
+ public async pncpRequest(req: SkillRequest): Promise<MessageResponse> {
72
+ let version = req.actorVersion || this.actorVersion;
73
+ if (!version) {
74
+ throw new Error("Invalid request, must know the actor version");
75
+ }
76
+
77
+ let pncpReq: PncpRequest = {
78
+ body: {
79
+ subject: req.subject,
80
+ action: req.action,
81
+ body: Value.fromJson(req.body),
82
+ },
83
+ author: {
84
+ id: this.actorId,
85
+ version: version,
86
+ },
87
+ callback: req.callback ? fromJsonValue(req.callback) : undefined,
88
+ parentId: req.parentConversationId,
89
+ versionReq: req.versionReq,
90
+ targetActorId: req.targetId,
91
+ matchStrategy: req.matchStrategy || SkillMatchStrategy.BEST,
92
+ mustMatch: req.mustMatch || true,
93
+ useLock: req.useLock || false,
94
+ allowOpenMatch: req.allowOpenMatch || false,
95
+ };
96
+ return await this.directPncpRequest(pncpReq);
97
+ }
98
+
99
+ public async directPncpRequest(req: PncpRequest): Promise<MessageResponse> {
100
+ let response = await this.issue(req, BrokerClient.prototype.skillRequest);
101
+ return response;
102
+ }
103
+
104
+ public async pncpMessage(data: MessageRequest): Promise<MessageResponse> {
105
+ const [id, tag] = data.conversation.split("@");
106
+ const membership = tag === "requester" ? ConversationMembership.REQUESTER : tag === "fulfiller" ? ConversationMembership.FULFILLER : ConversationMembership.OBSERVER;
107
+ let pncpId: ConversationId = { id, membership };
108
+ return await this.pncpMessageObject({ id: pncpId, body: data.message });
109
+
110
+ }
111
+
112
+ public async pncpMessageObject(data: PncpMessageObject): Promise<MessageResponse> {
113
+ let extra: any;
114
+ switch (data.body.type) {
115
+ case PncpMessageKind.PNCP_QUESTION: {
116
+ extra = {
117
+ oneofKind: "question",
118
+ question: {
119
+ id: data.body.value.id,
120
+ callback: data.body.value.callback ? fromJsonValue(data.body.value.callback) : undefined,
121
+ }
122
+ };
123
+ break;
124
+ }
125
+ case PncpMessageKind.PNCP_ANSWER: {
126
+ extra = {
127
+ oneofKind: "answer",
128
+ answer: {
129
+ replyTo: data.body.value.reply_to,
130
+ }
131
+ };
132
+ break;
133
+ }
134
+ default: extra = { oneofKind: undefined };
135
+ }
136
+ let pncpMsg: PncpMessage = {
137
+ id: data.id,
138
+ kind: data.body.type,
139
+ body: fromJsonValue(data.body.value.data),
140
+ extra,
141
+ };
142
+
143
+ return await this.directPncpMessage(pncpMsg);
144
+
145
+ }
146
+
147
+ public async directPncpMessage(data: PncpMessage): Promise<MessageResponse> {
148
+ let response = await this.issue(data, BrokerClient.prototype.pncpMessageRequest);
149
+ return response;
150
+ }
151
+
152
+ // Creates a new pncp callback listener.
153
+ public pncpListener(): PncpListener {
154
+ return new PncpListener(this);
155
+ }
156
+
157
+ public observerListener(init: ObservationRequest): ParanetObserverListener {
158
+ return new ParanetObserverListener(this, init);
159
+ }
160
+
161
+ public async login(creds: LoginCredentials): Promise<TokenResponse> {
162
+ let secret: any;
163
+ switch (creds.kind) {
164
+ case "token":
165
+ secret = { oneofKind: "token", token: creds.secret }; break;
166
+ case "password": secret = { oneofKind: "password", password: creds.secret }; break;
167
+ default:
168
+ throw new Error("Invalid creds type");
169
+ }
170
+ let req: LoginRequest = {
171
+ actorId: this.actorId,
172
+ secret,
173
+ };
174
+
175
+ let response = await this.issue(req, BrokerClient.prototype.login);
176
+ let token = response.token;
177
+ let refresh_token = response.refreshToken;
178
+ this.setTokens(token, refresh_token);
179
+ return { refresh_token, access_token: token };
180
+ }
181
+
182
+ public async refreshAuth(): Promise<TokenResponse> {
183
+ if (!this.token || !this.refreshToken) {
184
+ throw new Error("Cannot refresh auth, no tokens set.");
185
+ }
186
+ let req: RefreshTokenRequest = {
187
+ token: this.token,
188
+ refreshToken: this.refreshToken,
189
+ };
190
+ let response = await this.issue(req, BrokerClient.prototype.refreshToken);
191
+ let token = response.token;
192
+ let refreshToken = response.refreshToken;
193
+ this.setTokens(token, refreshToken);
194
+ return { refresh_token: refreshToken, access_token: token };
195
+ }
196
+
197
+ public logout() {
198
+ this.setTokens();
199
+ }
200
+
201
+ public setTokens(token?: string, refresh?: string) {
202
+ this.token = token;
203
+ this.refreshToken = refresh;
204
+ if (token && refresh) {
205
+ let decoded = jwtDecode(token);
206
+ this.tokenExp = decoded.exp ? new Date(decoded.exp * 1000) : undefined;
207
+ if (this.tokenCb) {
208
+ this.tokenCb(token, refresh);
209
+ }
210
+ }
211
+ }
212
+
213
+ public setTokenCb(cb: (token: string, refresh: string) => void) {
214
+ this.tokenCb = cb;
215
+ }
216
+
217
+ public hasTokens(): boolean {
218
+ return this.token != undefined;
219
+ }
220
+
221
+ public getTokens(): { token?: string; refresh?: string } {
222
+ return { token: this.token, refresh: this.refreshToken };
223
+ }
224
+
225
+ public isTokenValid(): boolean {
226
+ if (!this.tokenExp) {
227
+ return false;
228
+ }
229
+ let now = new Date();
230
+ // Add one minute to now. This will make it so the token "expires" sooner than it really
231
+ // does, but allow a more clear indication that it is time to refresh.
232
+ now.setTime(now.getTime() + 1000 * 60);
233
+ return now < this.tokenExp;
234
+ }
235
+
236
+ getMetadata(): RpcMetadata {
237
+ return {
238
+ 'x-actor-id': this.actorId,
239
+ ...(this.token && { 'authorization': `Bearer ${this.token}` })
240
+ };
241
+ }
242
+
243
+ async issue<R extends object, T extends object>(req: R, f: (this: BrokerClient, req: R, metadata: RpcOptions) => UnaryCall<R, T>): Promise<T> {
244
+ let md = this.getMetadata();
245
+ let response = f.bind(this.inner)(req, {
246
+ meta: md,
247
+ });
248
+ // Rejects if it failed.
249
+ await response.status;
250
+ let data = await response.response;
251
+ return data;
252
+ }
253
+ }
254
+
255
+ export abstract class ParanetListener<Treq extends object, Tres extends object, TresIn extends object = Tres> {
256
+ client: PncpClient;
257
+ start?: (client: PncpClient) => void;
258
+ msg?: (msg: Tres, client: PncpClient) => void;
259
+ error?: (msg: string, fatal: boolean) => void;
260
+ disconnected?: () => void;
261
+ reconnected?: () => void;
262
+ shouldAutoReconnect: boolean = true;
263
+
264
+ streamState?: {
265
+ stream: ServerStreamingCall<Treq, TresIn>;
266
+ abort: AbortController;
267
+ };
268
+
269
+ protected constructor(client: PncpClient) {
270
+ this.client = client;
271
+ }
272
+
273
+ protected abstract getRequest(): RpcStreamRequest<Treq, TresIn>;
274
+ protected abstract getRes(t: TresIn): Tres | null;
275
+
276
+ public onmessage(f: (msg: Tres, client: PncpClient) => void): this {
277
+ this.msg = f;
278
+ return this;
279
+ }
280
+
281
+ public onstart(f: (client: PncpClient) => void): this {
282
+ this.start = f;
283
+ return this;
284
+ }
285
+
286
+ public onerror(f: (msg: string, isFatal: boolean) => void): this {
287
+ this.error = f;
288
+ return this;
289
+ }
290
+
291
+ public ondisconnect(f: () => void): this {
292
+ this.disconnected = f;
293
+ return this;
294
+ }
295
+
296
+ public onreconnect(f: () => void): this {
297
+ this.reconnected = f;
298
+ return this;
299
+ }
300
+
301
+ public autoReconnect(v: boolean): this {
302
+ this.shouldAutoReconnect = v;
303
+ return this;
304
+ }
305
+
306
+
307
+ public connect() {
308
+ this.makeDecoratedStream(false);
309
+ }
310
+
311
+ public disconnect() {
312
+ if (this.streamState) {
313
+ this.streamState.abort.abort("disconnect called.");
314
+ this.streamState = undefined;
315
+ }
316
+ }
317
+
318
+ makeDecoratedStream(isReconnect: boolean, timeout = 100) {
319
+ if (this.streamState) {
320
+ this.disconnect();
321
+ }
322
+
323
+ // Set it up as a new one when we create a new stream.
324
+ let abort = new AbortController();
325
+
326
+ let { method, body } = this.getRequest();
327
+ let stream = method.bind(this.client.inner)(body, {
328
+ meta: this.client.getMetadata(),
329
+ abort: abort.signal
330
+ });
331
+ this.streamState = { stream, abort };
332
+ // Wait for the headers, indicating success.
333
+ // Note: This doesn't seem to work on firefox for some reason.
334
+ // Seems to be working fine in chrome though.
335
+ stream.headers.then((_headers) => {
336
+ this.start?.(this.client);
337
+ if (isReconnect) {
338
+ this.reconnected?.();
339
+ }
340
+ }).catch((err: RpcError) => {
341
+ // We need this catch here because of retry looping.
342
+ // We don't need to call this.error on this because it will come through on the onError still.
343
+ });
344
+
345
+ // Handle the message stream finally.
346
+ stream.responses.onMessage((msg) => {
347
+ let unwrapped = this.getRes(msg);
348
+ if (unwrapped) {
349
+ this.msg?.(unwrapped, this.client);
350
+ }
351
+ });
352
+
353
+ stream.responses.onError(async (err) => {
354
+ // Check if we aborted to know if we should retry or not.
355
+ this.disconnected?.();
356
+ if (!abort.signal.aborted) {
357
+ if (err.name == "RpcError") {
358
+ let rpcErr = err as RpcError;
359
+ if (rpcErr.code == "UNAUTHENTICATED") {
360
+ try {
361
+ await this.client.refreshAuth();
362
+ } catch (e) {
363
+ this.error?.(err.message, true);
364
+ abort.abort("Unauthenticated");
365
+ // Exit early in this case.
366
+ return;
367
+ }
368
+ }
369
+ }
370
+ this.error?.(err.message, false);
371
+ if (this.shouldAutoReconnect) {
372
+ // Increase timeout, saturated at 10000ms.
373
+ timeout = 2 * timeout + 1;
374
+ if (timeout > 10000)
375
+ timeout = 10000;
376
+ setTimeout(() => this.makeDecoratedStream(true, timeout), timeout);
377
+ }
378
+ }
379
+
380
+ });
381
+ // Semantically, this should never happen for a paranet connection, however, it might
382
+ // at some point in the future, so we should handle this case appropriately.
383
+ stream.responses.onComplete(() => {
384
+ this.disconnected?.();
385
+ });
386
+ }
387
+ }
388
+
389
+ export class PncpListener extends ParanetListener<PncpCallbackRequest, PncpCallback> {
390
+ constructor(client: PncpClient) {
391
+ super(client);
392
+ }
393
+
394
+ protected getRequest(): RpcStreamRequest<PncpCallbackRequest, PncpCallback> {
395
+ return {
396
+ method: BrokerClient.prototype.pncpCallbackStream,
397
+ body: {
398
+ disableSkillForwarding: false,
399
+ }
400
+ };
401
+ }
402
+
403
+ protected getRes(t: PncpCallback): PncpCallback | null {
404
+ if (t.body.oneofKind == undefined) return null;
405
+ return t;
406
+ }
407
+ }
408
+
409
+ interface RpcStreamRequest<TReq extends object, TRes extends object> {
410
+ method: (this: BrokerClient, body: TReq, meta: RpcOptions) => ServerStreamingCall<TReq, TRes>;
411
+ body: TReq;
412
+ }
413
+
414
+ export class ParanetObserverListener extends ParanetListener<ObservationRequest, ObservationCallback, ObservationMessageWrapper> {
415
+ init: ObservationRequest;
416
+ constructor(client: PncpClient, init: ObservationRequest) {
417
+ super(client);
418
+ this.init = init;
419
+ }
420
+
421
+ protected getRequest(): RpcStreamRequest<ObservationRequest, ObservationMessageWrapper> {
422
+ return {
423
+ method: BrokerClient.prototype.createObserverStream,
424
+ body: this.init,
425
+ };
426
+ }
427
+
428
+ protected getRes(t: ObservationMessageWrapper): ObservationCallback | null {
429
+ if (t.body) return t.body;
430
+ return null;
431
+ }
432
+ }
433
+
434
+ export abstract class BaseClient {
435
+ actorId: string;
436
+ endpoint: string;
437
+ // Access token, provided on login calls.
438
+ protected token?: string;
439
+ protected refreshToken?: string;
440
+ protected tokenExp?: Date;
441
+ protected tokenCb?: (token: string, refresh: string) => void;
442
+
443
+ constructor(init: ParanetClientInit) {
444
+ this.endpoint = cleanUrl(init.endpoint);
445
+ this.actorId = init.actorId;
446
+ }
447
+
448
+ public getEndpoint(): string {
449
+ return this.endpoint;
450
+ }
451
+
452
+ // Logs in this client and stores the token on the client.
453
+ public async login(secret: ActorSecret): Promise<TokenResponse> {
454
+ let id = this.actorId.split('@')[0];
455
+ console.log(secret);
456
+ let response: TokenResponse = await this.postJson(
457
+ "login",
458
+ {
459
+ id,
460
+ ...secret,
461
+ }
462
+ );
463
+
464
+ this.setTokens(response.access_token, response.refresh_token);
465
+ return response;
466
+ }
467
+
468
+ public async refreshAuth(): Promise<TokenResponse> {
469
+ let tokens: TokenResponse = await this.postJson(
470
+ "token/refresh",
471
+ {
472
+ token: this.token,
473
+ refresh_token: this.refreshToken,
474
+ }
475
+ );
476
+ this.setTokens(tokens.access_token, tokens.refresh_token);
477
+ return tokens;
478
+ }
479
+
480
+ public logout() {
481
+ this.setTokens();
482
+ }
483
+
484
+ public setTokens(token?: string, refresh?: string) {
485
+ this.token = token;
486
+ this.refreshToken = refresh;
487
+ if (token && refresh) {
488
+ let decoded = jwtDecode(token);
489
+ this.tokenExp = decoded.exp ? new Date(decoded.exp * 1000) : undefined;
490
+ if (this.tokenCb) {
491
+ this.tokenCb(token, refresh);
492
+ }
493
+ }
494
+ }
495
+
496
+ public setTokenCb(cb: (token: string, refresh: string) => void) {
497
+ this.tokenCb = cb;
498
+ }
499
+
500
+ public hasTokens(): boolean {
501
+ return this.token != undefined;
502
+ }
503
+
504
+ public getTokens(): { token?: string; refresh?: string } {
505
+ return { token: this.token, refresh: this.refreshToken };
506
+ }
507
+
508
+ public isTokenValid(): boolean {
509
+ if (!this.tokenExp) {
510
+ return false;
511
+ }
512
+ let now = new Date();
513
+ // Add one minute to now. This will make it so the token "expires" sooner than it really
514
+ // does, but allow a more clear indication that it is time to refresh.
515
+ now.setTime(now.getTime() + 1000 * 60);
516
+ return now < this.tokenExp;
517
+ }
518
+
519
+ protected async postJson<T>(path: string, body: any): Promise<T> {
520
+ let response = await this.post_(path, JSON.stringify(body), { "content-type": "application/json" });
521
+ return await response.json();
522
+ }
523
+
524
+ protected async post_(path: string, body: any, headers?: Record<string, string>): Promise<Response> {
525
+ let response = await this.fetch(path, "POST", body, headers);
526
+ return response;
527
+ }
528
+
529
+ protected async get<T>(path: string): Promise<T> {
530
+ let response = await this.fetch(path, "GET");
531
+ return await response.json();
532
+ }
533
+
534
+ protected async fetch(path: string, method: string, body?: any, headers?: Record<string, string>): Promise<Response> {
535
+ let url = `${this.endpoint}/${path}`;
536
+ const mkFetch = () => fetch(url, {
537
+ method,
538
+ body,
539
+ headers: {
540
+ ...(headers || {}),
541
+ ...this.getHeaders(),
542
+ },
543
+ });
544
+ let response = await mkFetch();
545
+ if (response.status == 401) {
546
+ await this.refreshAuth();
547
+ response = await mkFetch();
548
+ }
549
+ if (response.status != 200) {
550
+ let text = await response.text();
551
+ throw new Error(
552
+ `Failed to read from endpoint "${url}" (${response.status}): ${text} `
553
+ );
554
+ }
555
+
556
+ return response;
557
+ }
558
+
559
+ getHeaders(): any {
560
+ return {
561
+ "X-ACTOR-ID": this.actorId,
562
+ ...(this.token && { Authorization: `Bearer ${this.token}` }),
563
+ };
564
+ }
565
+ }
566
+
567
+ export class ParanetServiceClient extends BaseClient {
568
+ constructor(init: ParanetClientInit) {
569
+ super(init);
570
+ }
571
+
572
+ public serviceEndpoint(): string {
573
+ return this.endpoint;
574
+ }
575
+
576
+ // TODO fill in a specific type.
577
+ // TODO: At a higher level than this we need a schema manager which properly parses
578
+ // schemas and caches them.
579
+ public async schemaFetch(path: string): Promise<any> {
580
+ return await this.get(`schema/${path}`);
581
+ }
582
+
583
+ public async documentFetch({ name, id }: DocumentArgs): Promise<Response> {
584
+ if (name) {
585
+ return await this.fetch(`document/fetch/name/${name}`, "GET");
586
+ }
587
+
588
+ if (id) {
589
+ return await this.fetch(`document/fetch/id/${id}`, "GET");
590
+ }
591
+
592
+ throw new Error("Must provide either a name or id to fetch a document.");
593
+ }
594
+
595
+ public async documentUpload(data: File | any, args?: DocumentArgs): Promise<DocumentInfo> {
596
+ const formData = new FormData();
597
+ formData.append("file", data);
598
+ let response: Response | null = null;
599
+ if (args?.name) {
600
+ const url = `document/upload/name/${args.name}`;
601
+ response = await this.post_(url, formData);
602
+ }
603
+ else if (args?.id) {
604
+ const url = `document/upload/id/${args.id}`;
605
+ response = await this.post_(url, formData);
606
+ } else {
607
+ response = await this.post_("document/upload", formData);
608
+ }
609
+
610
+ return await response.json();
611
+ }
612
+
613
+ public async graphql(query: string, inputs?: Record<string, any>): Promise<any> {
614
+ let response: any = await this.postJson(
615
+ "graphql",
616
+ {
617
+ query,
618
+ variables: inputs,
619
+ }
620
+ );
621
+
622
+ return response;
623
+ }
624
+ }
625
+
626
+ export class SysClient extends BaseClient {
627
+ constructor(init: ParanetClientInit) {
628
+ super(init);
629
+ }
630
+
631
+ public async nodeInfo(): Promise<NodeInfo> {
632
+ return await this.get('node-info');
633
+ }
634
+ }
635
+
636
+ export interface LoginCredentials {
637
+ secret: string;
638
+ kind: "password" | "token" | "jwt";
639
+ }
640
+
641
+ export type ActorSecret = { password: string } | { token: string } | { jwt: string };
642
+
643
+ export type TokenResponse = {
644
+ access_token: string;
645
+ refresh_token: string;
646
+ };
647
+
648
+ // Some util functions.
649
+ function cleanUrl(url: string): string {
650
+ if (url.endsWith("/")) {
651
+ url = url.slice(0, url.length - 1);
652
+ }
653
+ return url;
654
+ }
655
+
656
+ export interface DocumentArgs {
657
+ name?: string;
658
+ id?: string;
659
+ }
660
+
661
+ export interface DocumentInfo {
662
+ id: string;
663
+ contentType: string;
664
+ }
665
+
666
+ export interface NodeInfo {
667
+ id: string;
668
+ name: string;
669
+ versions: {
670
+ broker: string;
671
+ expected_paraflow: string;
672
+ expected_paracord: string;
673
+ para_version: string;
674
+ platform_version: string;
675
+ platform_channel: string;
676
+ }
677
+ }