@dxos/edge-client 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29
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/dist/lib/{browser/chunk-IKP53CBQ.mjs → neutral/chunk-ZIQ5T3A7.mjs} +20 -83
- package/dist/lib/{browser/chunk-IKP53CBQ.mjs.map → neutral/chunk-ZIQ5T3A7.mjs.map} +2 -2
- package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs +1 -1
- package/dist/lib/{browser → neutral}/index.mjs +378 -439
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +6 -31
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/auth.d.ts.map +1 -1
- package/dist/types/src/edge-client.d.ts +5 -2
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +69 -30
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-identity.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts +20 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/http-client.d.ts +10 -7
- package/dist/types/src/http-client.d.ts.map +1 -1
- package/dist/types/src/protocol.d.ts +1 -1
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/testing/test-server.d.ts.map +1 -1
- package/dist/types/src/testing/test-utils.d.ts +3 -3
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +1 -1
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -31
- package/src/edge-client.test.ts +20 -15
- package/src/edge-client.ts +55 -8
- package/src/edge-http-client.test.ts +3 -2
- package/src/edge-http-client.ts +226 -78
- package/src/edge-ws-connection.ts +120 -6
- package/src/http-client.test.ts +9 -6
- package/src/http-client.ts +18 -8
- package/src/testing/test-utils.ts +7 -7
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DR5YNW5K.mjs +0 -332
- package/dist/lib/node-esm/chunk-DR5YNW5K.mjs.map +0 -7
- package/dist/lib/node-esm/edge-ws-muxer.mjs +0 -12
- package/dist/lib/node-esm/edge-ws-muxer.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -1251
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -186
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- /package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs.map +0 -0
package/src/edge-http-client.ts
CHANGED
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
|
|
6
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Function from 'effect/Function';
|
|
7
9
|
|
|
8
10
|
import { sleep } from '@dxos/async';
|
|
9
|
-
import { Context } from '@dxos/context';
|
|
11
|
+
import { Context, TRACE_SPAN_ATTRIBUTE, type TraceContextData } from '@dxos/context';
|
|
12
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
13
|
+
import { invariant } from '@dxos/invariant';
|
|
10
14
|
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
11
15
|
import { log } from '@dxos/log';
|
|
12
16
|
import {
|
|
@@ -14,14 +18,18 @@ import {
|
|
|
14
18
|
type CreateAgentResponseBody,
|
|
15
19
|
type CreateSpaceRequest,
|
|
16
20
|
type CreateSpaceResponseBody,
|
|
21
|
+
EDGE_CLIENT_TAG_HEADER,
|
|
17
22
|
EdgeAuthChallengeError,
|
|
18
23
|
EdgeCallFailedError,
|
|
19
|
-
type
|
|
24
|
+
type EdgeFailure,
|
|
20
25
|
type EdgeStatus,
|
|
21
26
|
type ExecuteWorkflowResponseBody,
|
|
22
27
|
type ExportBundleRequest,
|
|
23
28
|
type ExportBundleResponse,
|
|
29
|
+
type FeedProtocol,
|
|
24
30
|
type GetAgentStatusResponseBody,
|
|
31
|
+
type GetPluginVersionsResponseBody,
|
|
32
|
+
type GetPluginsResponseBody,
|
|
25
33
|
type GetNotarizationResponseBody,
|
|
26
34
|
type ImportBundleRequest,
|
|
27
35
|
type InitiateOAuthFlowRequest,
|
|
@@ -30,19 +38,32 @@ import {
|
|
|
30
38
|
type JoinSpaceResponseBody,
|
|
31
39
|
type ObjectId,
|
|
32
40
|
type PostNotarizationRequestBody,
|
|
33
|
-
type QueryResult,
|
|
34
|
-
type QueueQuery,
|
|
35
41
|
type RecoverIdentityRequest,
|
|
36
42
|
type RecoverIdentityResponseBody,
|
|
37
43
|
type UploadFunctionRequest,
|
|
38
44
|
type UploadFunctionResponseBody,
|
|
39
45
|
} from '@dxos/protocols';
|
|
46
|
+
import {
|
|
47
|
+
type QueryRequest as QueryRequestProto,
|
|
48
|
+
type QueryResponse as QueryResponseProto,
|
|
49
|
+
} from '@dxos/protocols/proto/dxos/echo/query';
|
|
40
50
|
import { createUrl } from '@dxos/util';
|
|
41
51
|
|
|
42
52
|
import { type EdgeIdentity, handleAuthChallenge } from './edge-identity';
|
|
43
53
|
import { HttpConfig, encodeAuthHeader, withLogging, withRetryConfig } from './http-client';
|
|
44
54
|
import { getEdgeUrlWithProtocol } from './utils';
|
|
45
55
|
|
|
56
|
+
/**
|
|
57
|
+
* HTTP wire shape returned by `/queue/.../query`. Unlike `FeedProtocol.QueryResult`
|
|
58
|
+
* (the RPC proto type, which transports object payloads as JSON strings), the edge
|
|
59
|
+
* HTTP endpoint embeds each object directly in the response JSON.
|
|
60
|
+
*/
|
|
61
|
+
export type EdgeQueryQueueResponse = {
|
|
62
|
+
objects?: unknown[];
|
|
63
|
+
nextCursor?: string;
|
|
64
|
+
prevCursor?: string;
|
|
65
|
+
};
|
|
66
|
+
|
|
46
67
|
const DEFAULT_RETRY_TIMEOUT = 1500;
|
|
47
68
|
const DEFAULT_RETRY_JITTER = 500;
|
|
48
69
|
const DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
@@ -65,7 +86,6 @@ export type RetryConfig = {
|
|
|
65
86
|
|
|
66
87
|
type EdgeHttpRequestArgs = {
|
|
67
88
|
method: string;
|
|
68
|
-
context?: Context;
|
|
69
89
|
retry?: RetryConfig;
|
|
70
90
|
body?: any;
|
|
71
91
|
/**
|
|
@@ -74,16 +94,30 @@ type EdgeHttpRequestArgs = {
|
|
|
74
94
|
json?: boolean;
|
|
75
95
|
|
|
76
96
|
/**
|
|
77
|
-
*
|
|
97
|
+
* Force authentication.
|
|
98
|
+
* This should be used for requests with large bodies to avoid sending the body twice.
|
|
99
|
+
* The client will call /auth endpoint to generate the auth header.
|
|
78
100
|
*/
|
|
79
|
-
|
|
101
|
+
auth?: boolean;
|
|
80
102
|
};
|
|
81
103
|
|
|
82
|
-
export type
|
|
83
|
-
|
|
104
|
+
export type EdgeHttpCallArgs = Pick<EdgeHttpRequestArgs, 'retry' | 'auth'>;
|
|
105
|
+
|
|
106
|
+
export type GetCronTriggersResponse = {
|
|
107
|
+
cronIds: string[];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type EdgeHttpClientOptions = {
|
|
111
|
+
/**
|
|
112
|
+
* Tag included in the {@link EDGE_CLIENT_TAG_HEADER} header on every request.
|
|
113
|
+
* Used on Edge to classify traffic for metering (e.g. `ci-e2e`).
|
|
114
|
+
*/
|
|
115
|
+
clientTag?: string;
|
|
116
|
+
};
|
|
84
117
|
|
|
85
118
|
export class EdgeHttpClient {
|
|
86
119
|
private readonly _baseUrl: string;
|
|
120
|
+
private readonly _clientTag: string | undefined;
|
|
87
121
|
|
|
88
122
|
private _edgeIdentity: EdgeIdentity | undefined;
|
|
89
123
|
|
|
@@ -92,8 +126,9 @@ export class EdgeHttpClient {
|
|
|
92
126
|
*/
|
|
93
127
|
private _authHeader: string | undefined;
|
|
94
128
|
|
|
95
|
-
constructor(baseUrl: string) {
|
|
129
|
+
constructor(baseUrl: string, options?: EdgeHttpClientOptions) {
|
|
96
130
|
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, 'http');
|
|
131
|
+
this._clientTag = options?.clientTag;
|
|
97
132
|
log('created', { url: this._baseUrl });
|
|
98
133
|
}
|
|
99
134
|
|
|
@@ -112,23 +147,28 @@ export class EdgeHttpClient {
|
|
|
112
147
|
// Status
|
|
113
148
|
//
|
|
114
149
|
|
|
115
|
-
public async getStatus(args?:
|
|
116
|
-
return this._call(new URL('/status', this.baseUrl), { ...args, method: 'GET' });
|
|
150
|
+
public async getStatus(ctx: Context, args?: EdgeHttpCallArgs): Promise<EdgeStatus> {
|
|
151
|
+
return this._call(ctx, new URL('/status', this.baseUrl), { ...args, method: 'GET', auth: true });
|
|
117
152
|
}
|
|
118
153
|
|
|
119
154
|
//
|
|
120
155
|
// Agents
|
|
121
156
|
//
|
|
122
157
|
|
|
123
|
-
public createAgent(
|
|
124
|
-
|
|
158
|
+
public createAgent(
|
|
159
|
+
ctx: Context,
|
|
160
|
+
body: CreateAgentRequestBody,
|
|
161
|
+
args?: EdgeHttpCallArgs,
|
|
162
|
+
): Promise<CreateAgentResponseBody> {
|
|
163
|
+
return this._call(ctx, new URL('/agents/create', this.baseUrl), { ...args, method: 'POST', body });
|
|
125
164
|
}
|
|
126
165
|
|
|
127
166
|
public getAgentStatus(
|
|
167
|
+
ctx: Context,
|
|
128
168
|
request: { ownerIdentityKey: PublicKey },
|
|
129
|
-
args?:
|
|
169
|
+
args?: EdgeHttpCallArgs,
|
|
130
170
|
): Promise<GetAgentStatusResponseBody> {
|
|
131
|
-
return this._call(new URL(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, this.baseUrl), {
|
|
171
|
+
return this._call(ctx, new URL(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, this.baseUrl), {
|
|
132
172
|
...args,
|
|
133
173
|
method: 'GET',
|
|
134
174
|
});
|
|
@@ -138,16 +178,21 @@ export class EdgeHttpClient {
|
|
|
138
178
|
// Credentials
|
|
139
179
|
//
|
|
140
180
|
|
|
141
|
-
public getCredentialsForNotarization(
|
|
142
|
-
|
|
181
|
+
public getCredentialsForNotarization(
|
|
182
|
+
ctx: Context,
|
|
183
|
+
spaceId: SpaceId,
|
|
184
|
+
args?: EdgeHttpCallArgs,
|
|
185
|
+
): Promise<GetNotarizationResponseBody> {
|
|
186
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, method: 'GET' });
|
|
143
187
|
}
|
|
144
188
|
|
|
145
189
|
public async notarizeCredentials(
|
|
190
|
+
ctx: Context,
|
|
146
191
|
spaceId: SpaceId,
|
|
147
192
|
body: PostNotarizationRequestBody,
|
|
148
|
-
args?:
|
|
193
|
+
args?: EdgeHttpCallArgs,
|
|
149
194
|
): Promise<void> {
|
|
150
|
-
await this._call(new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
195
|
+
await this._call(ctx, new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
151
196
|
}
|
|
152
197
|
|
|
153
198
|
//
|
|
@@ -155,10 +200,11 @@ export class EdgeHttpClient {
|
|
|
155
200
|
//
|
|
156
201
|
|
|
157
202
|
public async recoverIdentity(
|
|
203
|
+
ctx: Context,
|
|
158
204
|
body: RecoverIdentityRequest,
|
|
159
|
-
args?:
|
|
205
|
+
args?: EdgeHttpCallArgs,
|
|
160
206
|
): Promise<RecoverIdentityResponseBody> {
|
|
161
|
-
return this._call(new URL('/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });
|
|
207
|
+
return this._call(ctx, new URL('/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });
|
|
162
208
|
}
|
|
163
209
|
|
|
164
210
|
//
|
|
@@ -166,11 +212,12 @@ export class EdgeHttpClient {
|
|
|
166
212
|
//
|
|
167
213
|
|
|
168
214
|
public async joinSpaceByInvitation(
|
|
215
|
+
ctx: Context,
|
|
169
216
|
spaceId: SpaceId,
|
|
170
217
|
body: JoinSpaceRequest,
|
|
171
|
-
args?:
|
|
218
|
+
args?: EdgeHttpCallArgs,
|
|
172
219
|
): Promise<JoinSpaceResponseBody> {
|
|
173
|
-
return this._call(new URL(`/spaces/${spaceId}/join`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
220
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/join`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
174
221
|
}
|
|
175
222
|
|
|
176
223
|
//
|
|
@@ -178,18 +225,19 @@ export class EdgeHttpClient {
|
|
|
178
225
|
//
|
|
179
226
|
|
|
180
227
|
public async initiateOAuthFlow(
|
|
228
|
+
ctx: Context,
|
|
181
229
|
body: InitiateOAuthFlowRequest,
|
|
182
|
-
args?:
|
|
230
|
+
args?: EdgeHttpCallArgs,
|
|
183
231
|
): Promise<InitiateOAuthFlowResponse> {
|
|
184
|
-
return this._call(new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST' });
|
|
232
|
+
return this._call(ctx, new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST' });
|
|
185
233
|
}
|
|
186
234
|
|
|
187
235
|
//
|
|
188
236
|
// Spaces
|
|
189
237
|
//
|
|
190
238
|
|
|
191
|
-
async createSpace(body: CreateSpaceRequest, args?:
|
|
192
|
-
return this._call(new URL('/spaces/create', this.baseUrl), { ...args, body, method: 'POST' });
|
|
239
|
+
async createSpace(ctx: Context, body: CreateSpaceRequest, args?: EdgeHttpCallArgs): Promise<CreateSpaceResponseBody> {
|
|
240
|
+
return this._call(ctx, new URL('/spaces/create', this.baseUrl), { ...args, body, method: 'POST' });
|
|
193
241
|
}
|
|
194
242
|
|
|
195
243
|
//
|
|
@@ -197,13 +245,16 @@ export class EdgeHttpClient {
|
|
|
197
245
|
//
|
|
198
246
|
|
|
199
247
|
public async queryQueue(
|
|
248
|
+
ctx: Context,
|
|
200
249
|
subspaceTag: string,
|
|
201
250
|
spaceId: SpaceId,
|
|
202
|
-
query: QueueQuery,
|
|
203
|
-
args?:
|
|
204
|
-
): Promise<
|
|
205
|
-
const
|
|
251
|
+
query: FeedProtocol.QueueQuery,
|
|
252
|
+
args?: EdgeHttpCallArgs,
|
|
253
|
+
): Promise<EdgeQueryQueueResponse> {
|
|
254
|
+
const queueId = query.queueIds?.[0];
|
|
255
|
+
invariant(queueId, 'queueId required');
|
|
206
256
|
return this._call(
|
|
257
|
+
ctx,
|
|
207
258
|
createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {
|
|
208
259
|
after: query.after,
|
|
209
260
|
before: query.before,
|
|
@@ -219,13 +270,14 @@ export class EdgeHttpClient {
|
|
|
219
270
|
}
|
|
220
271
|
|
|
221
272
|
public async insertIntoQueue(
|
|
273
|
+
ctx: Context,
|
|
222
274
|
subspaceTag: string,
|
|
223
275
|
spaceId: SpaceId,
|
|
224
276
|
queueId: ObjectId,
|
|
225
277
|
objects: unknown[],
|
|
226
|
-
args?:
|
|
278
|
+
args?: EdgeHttpCallArgs,
|
|
227
279
|
): Promise<void> {
|
|
228
|
-
return this._call(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
280
|
+
return this._call(ctx, new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
229
281
|
...args,
|
|
230
282
|
body: { objects },
|
|
231
283
|
method: 'POST',
|
|
@@ -233,13 +285,15 @@ export class EdgeHttpClient {
|
|
|
233
285
|
}
|
|
234
286
|
|
|
235
287
|
public async deleteFromQueue(
|
|
288
|
+
ctx: Context,
|
|
236
289
|
subspaceTag: string,
|
|
237
290
|
spaceId: SpaceId,
|
|
238
291
|
queueId: ObjectId,
|
|
239
292
|
objectIds: ObjectId[],
|
|
240
|
-
args?:
|
|
293
|
+
args?: EdgeHttpCallArgs,
|
|
241
294
|
): Promise<void> {
|
|
242
295
|
return this._call(
|
|
296
|
+
ctx,
|
|
243
297
|
createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
244
298
|
ids: objectIds.join(','),
|
|
245
299
|
}),
|
|
@@ -255,15 +309,17 @@ export class EdgeHttpClient {
|
|
|
255
309
|
//
|
|
256
310
|
|
|
257
311
|
public async uploadFunction(
|
|
312
|
+
ctx: Context,
|
|
258
313
|
pathParts: { functionId?: string },
|
|
259
314
|
body: UploadFunctionRequest,
|
|
260
|
-
args?:
|
|
315
|
+
args?: EdgeHttpCallArgs,
|
|
261
316
|
): Promise<UploadFunctionResponseBody> {
|
|
262
317
|
const formData = new FormData();
|
|
263
318
|
formData.append('name', body.name ?? '');
|
|
264
319
|
formData.append('version', body.version);
|
|
265
320
|
formData.append('ownerPublicKey', body.ownerPublicKey);
|
|
266
321
|
formData.append('entryPoint', body.entryPoint);
|
|
322
|
+
body.runtime && formData.append('runtime', body.runtime);
|
|
267
323
|
for (const [filename, content] of Object.entries(body.assets)) {
|
|
268
324
|
formData.append(
|
|
269
325
|
'assets',
|
|
@@ -273,7 +329,7 @@ export class EdgeHttpClient {
|
|
|
273
329
|
}
|
|
274
330
|
|
|
275
331
|
const path = ['functions', ...(pathParts.functionId ? [pathParts.functionId] : [])].join('/');
|
|
276
|
-
return this._call(new URL(path, this.baseUrl), {
|
|
332
|
+
return this._call(ctx, new URL(path, this.baseUrl), {
|
|
277
333
|
...args,
|
|
278
334
|
body: formData,
|
|
279
335
|
method: 'PUT',
|
|
@@ -281,11 +337,12 @@ export class EdgeHttpClient {
|
|
|
281
337
|
});
|
|
282
338
|
}
|
|
283
339
|
|
|
284
|
-
public async listFunctions(args?:
|
|
285
|
-
return this._call(new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
|
|
340
|
+
public async listFunctions(ctx: Context, args?: EdgeHttpCallArgs): Promise<any> {
|
|
341
|
+
return this._call(ctx, new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
|
|
286
342
|
}
|
|
287
343
|
|
|
288
344
|
public async invokeFunction(
|
|
345
|
+
ctx: Context,
|
|
289
346
|
params: {
|
|
290
347
|
functionId: string;
|
|
291
348
|
version?: string;
|
|
@@ -294,7 +351,7 @@ export class EdgeHttpClient {
|
|
|
294
351
|
subrequestsLimit?: number;
|
|
295
352
|
},
|
|
296
353
|
input: unknown,
|
|
297
|
-
args?:
|
|
354
|
+
args?: EdgeHttpCallArgs,
|
|
298
355
|
): Promise<any> {
|
|
299
356
|
const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
|
|
300
357
|
if (params.version) {
|
|
@@ -310,11 +367,10 @@ export class EdgeHttpClient {
|
|
|
310
367
|
url.searchParams.set('subrequestsLimit', params.subrequestsLimit.toString());
|
|
311
368
|
}
|
|
312
369
|
|
|
313
|
-
return this._call(url, {
|
|
370
|
+
return this._call(ctx, url, {
|
|
314
371
|
...args,
|
|
315
372
|
body: input,
|
|
316
373
|
method: 'POST',
|
|
317
|
-
rawResponse: true,
|
|
318
374
|
});
|
|
319
375
|
}
|
|
320
376
|
|
|
@@ -323,12 +379,13 @@ export class EdgeHttpClient {
|
|
|
323
379
|
//
|
|
324
380
|
|
|
325
381
|
public async executeWorkflow(
|
|
382
|
+
ctx: Context,
|
|
326
383
|
spaceId: SpaceId,
|
|
327
384
|
graphId: ObjectId,
|
|
328
385
|
input: any,
|
|
329
|
-
args?:
|
|
386
|
+
args?: EdgeHttpCallArgs,
|
|
330
387
|
): Promise<ExecuteWorkflowResponseBody> {
|
|
331
|
-
return this._call(new URL(`/workflows/${spaceId}/${graphId}`, this.baseUrl), {
|
|
388
|
+
return this._call(ctx, new URL(`/workflows/${spaceId}/${graphId}`, this.baseUrl), {
|
|
332
389
|
...args,
|
|
333
390
|
body: input,
|
|
334
391
|
method: 'POST',
|
|
@@ -339,8 +396,62 @@ export class EdgeHttpClient {
|
|
|
339
396
|
// Triggers
|
|
340
397
|
//
|
|
341
398
|
|
|
342
|
-
public async getCronTriggers(spaceId: SpaceId) {
|
|
343
|
-
return this._call(new URL(`/
|
|
399
|
+
public async getCronTriggers(ctx: Context, spaceId: SpaceId): Promise<GetCronTriggersResponse> {
|
|
400
|
+
return this._call<GetCronTriggersResponse>(ctx, new URL(`/functions/${spaceId}/triggers/crons`, this.baseUrl), {
|
|
401
|
+
method: 'GET',
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
public async forceRunCronTrigger(ctx: Context, spaceId: SpaceId, triggerId: ObjectId) {
|
|
406
|
+
return this._call(ctx, new URL(`/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {
|
|
407
|
+
method: 'POST',
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
//
|
|
412
|
+
// Query
|
|
413
|
+
//
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Execute a QueryAST query against a space.
|
|
417
|
+
*/
|
|
418
|
+
public async execQuery(
|
|
419
|
+
ctx: Context,
|
|
420
|
+
spaceId: SpaceId,
|
|
421
|
+
body: QueryRequestProto,
|
|
422
|
+
args?: EdgeHttpCallArgs,
|
|
423
|
+
): Promise<QueryResponseProto> {
|
|
424
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/exec-query`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
//
|
|
428
|
+
// Registry
|
|
429
|
+
//
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Fetches the hydrated plugin directory from the Edge registry service.
|
|
433
|
+
* Unauthenticated; safe to call without an identity.
|
|
434
|
+
*/
|
|
435
|
+
public async getRegistryPlugins(ctx: Context, args?: EdgeHttpCallArgs): Promise<GetPluginsResponseBody> {
|
|
436
|
+
return this._call(ctx, new URL('/registry/plugins', this.baseUrl), { ...args, method: 'GET' });
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Fetches the available release versions for a given plugin repo. `repo` is the
|
|
441
|
+
* GitHub `owner/name` form; this method takes care of URL-encoding before issuing
|
|
442
|
+
* the request. Unauthenticated; same surface area as {@link getRegistryPlugins}.
|
|
443
|
+
*
|
|
444
|
+
* Versions are returned newest first, suitable for direct rendering in a picker.
|
|
445
|
+
*/
|
|
446
|
+
public async getRegistryPluginVersions(
|
|
447
|
+
ctx: Context,
|
|
448
|
+
repo: string,
|
|
449
|
+
args?: EdgeHttpCallArgs,
|
|
450
|
+
): Promise<GetPluginVersionsResponseBody> {
|
|
451
|
+
return this._call(ctx, new URL(`/registry/plugins/${encodeURIComponent(repo)}/versions`, this.baseUrl), {
|
|
452
|
+
...args,
|
|
453
|
+
method: 'GET',
|
|
454
|
+
});
|
|
344
455
|
}
|
|
345
456
|
|
|
346
457
|
//
|
|
@@ -348,19 +459,21 @@ export class EdgeHttpClient {
|
|
|
348
459
|
//
|
|
349
460
|
|
|
350
461
|
public async importBundle(
|
|
351
|
-
|
|
462
|
+
ctx: Context,
|
|
463
|
+
spaceId: SpaceId,
|
|
352
464
|
body: ImportBundleRequest,
|
|
353
|
-
args?:
|
|
465
|
+
args?: EdgeHttpCallArgs,
|
|
354
466
|
): Promise<void> {
|
|
355
|
-
return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), { ...args, body, method: 'PUT' });
|
|
467
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/import`, this.baseUrl), { ...args, body, method: 'PUT' });
|
|
356
468
|
}
|
|
357
469
|
|
|
358
470
|
public async exportBundle(
|
|
471
|
+
ctx: Context,
|
|
359
472
|
spaceId: SpaceId,
|
|
360
473
|
body: ExportBundleRequest,
|
|
361
|
-
args?:
|
|
474
|
+
args?: EdgeHttpCallArgs,
|
|
362
475
|
): Promise<ExportBundleResponse> {
|
|
363
|
-
return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
476
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
364
477
|
...args,
|
|
365
478
|
body,
|
|
366
479
|
method: 'POST',
|
|
@@ -371,66 +484,75 @@ export class EdgeHttpClient {
|
|
|
371
484
|
// Internal
|
|
372
485
|
//
|
|
373
486
|
|
|
374
|
-
private async _fetch<T>(url: URL,
|
|
375
|
-
return pipe(
|
|
487
|
+
private async _fetch<T>(url: URL, _args: EdgeHttpRequestArgs): Promise<T> {
|
|
488
|
+
return Function.pipe(
|
|
376
489
|
HttpClient.get(url),
|
|
377
490
|
withLogging,
|
|
378
491
|
withRetryConfig,
|
|
379
492
|
Effect.provide(FetchHttpClient.layer),
|
|
380
493
|
Effect.provide(HttpConfig.default),
|
|
381
494
|
Effect.withSpan('EdgeHttpClient'),
|
|
382
|
-
|
|
495
|
+
runAndForwardErrors,
|
|
383
496
|
) as T;
|
|
384
497
|
}
|
|
385
498
|
|
|
386
499
|
// TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
|
|
387
|
-
private async _call<T>(url: URL, args: EdgeHttpRequestArgs): Promise<T> {
|
|
500
|
+
private async _call<T>(ctx: Context, url: URL, args: EdgeHttpRequestArgs): Promise<T> {
|
|
388
501
|
const shouldRetry = createRetryHandler(args);
|
|
389
|
-
const requestContext = args.context ?? new Context();
|
|
390
502
|
log('fetch', { url, request: args.body });
|
|
391
503
|
|
|
504
|
+
const traceHeaders = getTraceHeaders(ctx);
|
|
505
|
+
|
|
392
506
|
let handledAuth = false;
|
|
507
|
+
const tryCount = 1;
|
|
393
508
|
while (true) {
|
|
394
509
|
let processingError: EdgeCallFailedError | undefined = undefined;
|
|
395
|
-
let retryAfterHeaderValue: number = Number.NaN;
|
|
396
510
|
try {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const body = (await response.json()) as EdgeHttpResponse<T>;
|
|
402
|
-
|
|
403
|
-
if (args.rawResponse) {
|
|
404
|
-
return body as any;
|
|
511
|
+
if (!this._authHeader && args.auth) {
|
|
512
|
+
const response = await fetch(new URL(`/auth`, this.baseUrl));
|
|
513
|
+
if (response.status === 401) {
|
|
514
|
+
this._authHeader = await this._handleUnauthorized(response);
|
|
405
515
|
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const request = createRequest(args, this._authHeader, traceHeaders, this._clientTag);
|
|
519
|
+
log('call edge', { url, tryCount, authHeader: !!this._authHeader });
|
|
520
|
+
const response = await fetch(url, request);
|
|
406
521
|
|
|
522
|
+
if (response.ok) {
|
|
523
|
+
const body = await response.clone().json();
|
|
524
|
+
invariant(body, 'Expected body to be present');
|
|
407
525
|
if (!('success' in body)) {
|
|
408
526
|
return body;
|
|
409
527
|
}
|
|
410
|
-
|
|
411
528
|
if (body.success) {
|
|
412
529
|
return body.data;
|
|
413
530
|
}
|
|
414
|
-
|
|
415
|
-
log.warn('unsuccessful edge response', { url, body });
|
|
416
|
-
if (body.errorData?.type === 'auth_challenge' && typeof body.errorData?.challenge === 'string') {
|
|
417
|
-
processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
|
|
418
|
-
} else if (body.errorData) {
|
|
419
|
-
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
420
|
-
}
|
|
421
531
|
} else if (response.status === 401 && !handledAuth) {
|
|
422
532
|
this._authHeader = await this._handleUnauthorized(response);
|
|
423
533
|
handledAuth = true;
|
|
424
534
|
continue;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const body: EdgeFailure =
|
|
538
|
+
response.headers.get('Content-Type') === 'application/json' ? await response.clone().json() : undefined;
|
|
539
|
+
|
|
540
|
+
invariant(!body?.success, 'Expected body to not be a failure response or undefined.');
|
|
541
|
+
|
|
542
|
+
if (body?.data?.type === 'auth_challenge' && typeof body?.data?.challenge === 'string') {
|
|
543
|
+
processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
|
|
544
|
+
} else if (body?.success === false) {
|
|
545
|
+
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
425
546
|
} else {
|
|
547
|
+
invariant(!response.ok, 'Expected response to not be ok.');
|
|
426
548
|
processingError = await EdgeCallFailedError.fromHttpFailure(response);
|
|
427
549
|
}
|
|
428
550
|
} catch (error: any) {
|
|
429
551
|
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
430
552
|
}
|
|
431
553
|
|
|
432
|
-
if (processingError?.isRetryable && (await shouldRetry(
|
|
433
|
-
log('retrying edge request', { url, processingError });
|
|
554
|
+
if (processingError?.isRetryable && (await shouldRetry(ctx, processingError.retryAfterMs))) {
|
|
555
|
+
log.verbose('retrying edge request', { url, processingError });
|
|
434
556
|
} else {
|
|
435
557
|
throw processingError!;
|
|
436
558
|
}
|
|
@@ -451,6 +573,8 @@ export class EdgeHttpClient {
|
|
|
451
573
|
const createRequest = (
|
|
452
574
|
{ method, body, json = true }: EdgeHttpRequestArgs,
|
|
453
575
|
authHeader: string | undefined,
|
|
576
|
+
traceHeaders?: Record<string, string>,
|
|
577
|
+
clientTag?: string,
|
|
454
578
|
): RequestInit => {
|
|
455
579
|
let requestBody: BodyInit | undefined;
|
|
456
580
|
const headers: HeadersInit = {};
|
|
@@ -470,6 +594,14 @@ const createRequest = (
|
|
|
470
594
|
headers['Authorization'] = authHeader;
|
|
471
595
|
}
|
|
472
596
|
|
|
597
|
+
if (traceHeaders) {
|
|
598
|
+
Object.assign(headers, traceHeaders);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (clientTag) {
|
|
602
|
+
headers[EDGE_CLIENT_TAG_HEADER] = clientTag;
|
|
603
|
+
}
|
|
604
|
+
|
|
473
605
|
return {
|
|
474
606
|
method,
|
|
475
607
|
body: requestBody,
|
|
@@ -477,6 +609,22 @@ const createRequest = (
|
|
|
477
609
|
};
|
|
478
610
|
};
|
|
479
611
|
|
|
612
|
+
/**
|
|
613
|
+
* Extract W3C Trace Context headers (traceparent/tracestate) from a DXOS Context.
|
|
614
|
+
*/
|
|
615
|
+
const getTraceHeaders = (ctx: Context): Record<string, string> | undefined => {
|
|
616
|
+
const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE) as TraceContextData | undefined;
|
|
617
|
+
if (!traceCtx) {
|
|
618
|
+
return undefined;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const headers: Record<string, string> = { traceparent: traceCtx.traceparent };
|
|
622
|
+
if (traceCtx.tracestate) {
|
|
623
|
+
headers.tracestate = traceCtx.tracestate;
|
|
624
|
+
}
|
|
625
|
+
return headers;
|
|
626
|
+
};
|
|
627
|
+
|
|
480
628
|
/**
|
|
481
629
|
* @deprecated
|
|
482
630
|
*/
|
|
@@ -489,7 +637,7 @@ const createRetryHandler = ({ retry }: EdgeHttpRequestArgs) => {
|
|
|
489
637
|
const maxRetries = retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
490
638
|
const baseTimeout = retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
491
639
|
const jitter = retry.jitter ?? DEFAULT_RETRY_JITTER;
|
|
492
|
-
return async (ctx: Context, retryAfter
|
|
640
|
+
return async (ctx: Context, retryAfter?: number) => {
|
|
493
641
|
if (++retries > maxRetries || ctx.disposed) {
|
|
494
642
|
return false;
|
|
495
643
|
}
|