@dxos/edge-client 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0
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 → neutral}/index.mjs +155 -96
- 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.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 +42 -33
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts +1 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/src/testing/test-utils.d.ts +2 -2
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -24
- package/src/edge-client.test.ts +16 -11
- package/src/edge-client.ts +32 -3
- package/src/edge-http-client.test.ts +2 -1
- package/src/edge-http-client.ts +152 -64
- package/src/edge-ws-connection.ts +2 -1
- package/src/http-client.test.ts +3 -2
- package/src/testing/test-utils.ts +4 -4
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/chunk-JTBFRYNM.mjs +0 -303
- package/dist/lib/node-esm/chunk-JTBFRYNM.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 -1363
- 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}/chunk-VESGVCLQ.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-VESGVCLQ.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs +0 -0
- /package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/testing/index.mjs +0 -0
package/src/edge-http-client.ts
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
|
|
6
6
|
import * as HttpClient from '@effect/platform/HttpClient';
|
|
7
|
+
import { type Context as OtelContext, propagation } from '@opentelemetry/api';
|
|
7
8
|
import * as Effect from 'effect/Effect';
|
|
8
9
|
import * as Function from 'effect/Function';
|
|
9
10
|
|
|
10
11
|
import { sleep } from '@dxos/async';
|
|
11
12
|
import { Context } from '@dxos/context';
|
|
13
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
12
14
|
import { invariant } from '@dxos/invariant';
|
|
13
15
|
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
14
16
|
import { log } from '@dxos/log';
|
|
@@ -17,12 +19,15 @@ import {
|
|
|
17
19
|
type CreateAgentResponseBody,
|
|
18
20
|
type CreateSpaceRequest,
|
|
19
21
|
type CreateSpaceResponseBody,
|
|
22
|
+
EDGE_CLIENT_TAG_HEADER,
|
|
20
23
|
EdgeAuthChallengeError,
|
|
21
24
|
EdgeCallFailedError,
|
|
25
|
+
type EdgeFailure,
|
|
22
26
|
type EdgeStatus,
|
|
23
27
|
type ExecuteWorkflowResponseBody,
|
|
24
28
|
type ExportBundleRequest,
|
|
25
29
|
type ExportBundleResponse,
|
|
30
|
+
type FeedProtocol,
|
|
26
31
|
type GetAgentStatusResponseBody,
|
|
27
32
|
type GetNotarizationResponseBody,
|
|
28
33
|
type ImportBundleRequest,
|
|
@@ -32,13 +37,16 @@ import {
|
|
|
32
37
|
type JoinSpaceResponseBody,
|
|
33
38
|
type ObjectId,
|
|
34
39
|
type PostNotarizationRequestBody,
|
|
35
|
-
type QueryResult,
|
|
36
|
-
type QueueQuery,
|
|
37
40
|
type RecoverIdentityRequest,
|
|
38
41
|
type RecoverIdentityResponseBody,
|
|
39
42
|
type UploadFunctionRequest,
|
|
40
43
|
type UploadFunctionResponseBody,
|
|
41
44
|
} from '@dxos/protocols';
|
|
45
|
+
import {
|
|
46
|
+
type QueryRequest as QueryRequestProto,
|
|
47
|
+
type QueryResponse as QueryResponseProto,
|
|
48
|
+
} from '@dxos/protocols/proto/dxos/echo/query';
|
|
49
|
+
import { TRACE_PROCESSOR, TRACE_SPAN_ATTRIBUTE } from '@dxos/tracing';
|
|
42
50
|
import { createUrl } from '@dxos/util';
|
|
43
51
|
|
|
44
52
|
import { type EdgeIdentity, handleAuthChallenge } from './edge-identity';
|
|
@@ -67,7 +75,6 @@ export type RetryConfig = {
|
|
|
67
75
|
|
|
68
76
|
type EdgeHttpRequestArgs = {
|
|
69
77
|
method: string;
|
|
70
|
-
context?: Context;
|
|
71
78
|
retry?: RetryConfig;
|
|
72
79
|
body?: any;
|
|
73
80
|
/**
|
|
@@ -75,12 +82,6 @@ type EdgeHttpRequestArgs = {
|
|
|
75
82
|
*/
|
|
76
83
|
json?: boolean;
|
|
77
84
|
|
|
78
|
-
/**
|
|
79
|
-
* Do not expect a standard EDGE JSON response with a `success` field.
|
|
80
|
-
* @deprecated Use only for debugging.
|
|
81
|
-
*/
|
|
82
|
-
rawResponse?: boolean;
|
|
83
|
-
|
|
84
85
|
/**
|
|
85
86
|
* Force authentication.
|
|
86
87
|
* This should be used for requests with large bodies to avoid sending the body twice.
|
|
@@ -89,11 +90,23 @@ type EdgeHttpRequestArgs = {
|
|
|
89
90
|
auth?: boolean;
|
|
90
91
|
};
|
|
91
92
|
|
|
92
|
-
export type
|
|
93
|
-
|
|
93
|
+
export type EdgeHttpCallArgs = Pick<EdgeHttpRequestArgs, 'retry' | 'auth'>;
|
|
94
|
+
|
|
95
|
+
export type GetCronTriggersResponse = {
|
|
96
|
+
cronIds: string[];
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type EdgeHttpClientOptions = {
|
|
100
|
+
/**
|
|
101
|
+
* Tag included in the {@link EDGE_CLIENT_TAG_HEADER} header on every request.
|
|
102
|
+
* Used on Edge to classify traffic for metering (e.g. `ci-e2e`).
|
|
103
|
+
*/
|
|
104
|
+
clientTag?: string;
|
|
105
|
+
};
|
|
94
106
|
|
|
95
107
|
export class EdgeHttpClient {
|
|
96
108
|
private readonly _baseUrl: string;
|
|
109
|
+
private readonly _clientTag: string | undefined;
|
|
97
110
|
|
|
98
111
|
private _edgeIdentity: EdgeIdentity | undefined;
|
|
99
112
|
|
|
@@ -102,8 +115,9 @@ export class EdgeHttpClient {
|
|
|
102
115
|
*/
|
|
103
116
|
private _authHeader: string | undefined;
|
|
104
117
|
|
|
105
|
-
constructor(baseUrl: string) {
|
|
118
|
+
constructor(baseUrl: string, options?: EdgeHttpClientOptions) {
|
|
106
119
|
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, 'http');
|
|
120
|
+
this._clientTag = options?.clientTag;
|
|
107
121
|
log('created', { url: this._baseUrl });
|
|
108
122
|
}
|
|
109
123
|
|
|
@@ -122,23 +136,28 @@ export class EdgeHttpClient {
|
|
|
122
136
|
// Status
|
|
123
137
|
//
|
|
124
138
|
|
|
125
|
-
public async getStatus(args?:
|
|
126
|
-
return this._call(new URL('/status', this.baseUrl), { ...args, method: 'GET' });
|
|
139
|
+
public async getStatus(ctx: Context, args?: EdgeHttpCallArgs): Promise<EdgeStatus> {
|
|
140
|
+
return this._call(ctx, new URL('/status', this.baseUrl), { ...args, method: 'GET', auth: true });
|
|
127
141
|
}
|
|
128
142
|
|
|
129
143
|
//
|
|
130
144
|
// Agents
|
|
131
145
|
//
|
|
132
146
|
|
|
133
|
-
public createAgent(
|
|
134
|
-
|
|
147
|
+
public createAgent(
|
|
148
|
+
ctx: Context,
|
|
149
|
+
body: CreateAgentRequestBody,
|
|
150
|
+
args?: EdgeHttpCallArgs,
|
|
151
|
+
): Promise<CreateAgentResponseBody> {
|
|
152
|
+
return this._call(ctx, new URL('/agents/create', this.baseUrl), { ...args, method: 'POST', body });
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
public getAgentStatus(
|
|
156
|
+
ctx: Context,
|
|
138
157
|
request: { ownerIdentityKey: PublicKey },
|
|
139
|
-
args?:
|
|
158
|
+
args?: EdgeHttpCallArgs,
|
|
140
159
|
): Promise<GetAgentStatusResponseBody> {
|
|
141
|
-
return this._call(new URL(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, this.baseUrl), {
|
|
160
|
+
return this._call(ctx, new URL(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, this.baseUrl), {
|
|
142
161
|
...args,
|
|
143
162
|
method: 'GET',
|
|
144
163
|
});
|
|
@@ -148,16 +167,21 @@ export class EdgeHttpClient {
|
|
|
148
167
|
// Credentials
|
|
149
168
|
//
|
|
150
169
|
|
|
151
|
-
public getCredentialsForNotarization(
|
|
152
|
-
|
|
170
|
+
public getCredentialsForNotarization(
|
|
171
|
+
ctx: Context,
|
|
172
|
+
spaceId: SpaceId,
|
|
173
|
+
args?: EdgeHttpCallArgs,
|
|
174
|
+
): Promise<GetNotarizationResponseBody> {
|
|
175
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, method: 'GET' });
|
|
153
176
|
}
|
|
154
177
|
|
|
155
178
|
public async notarizeCredentials(
|
|
179
|
+
ctx: Context,
|
|
156
180
|
spaceId: SpaceId,
|
|
157
181
|
body: PostNotarizationRequestBody,
|
|
158
|
-
args?:
|
|
182
|
+
args?: EdgeHttpCallArgs,
|
|
159
183
|
): Promise<void> {
|
|
160
|
-
await this._call(new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
184
|
+
await this._call(ctx, new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
161
185
|
}
|
|
162
186
|
|
|
163
187
|
//
|
|
@@ -165,10 +189,11 @@ export class EdgeHttpClient {
|
|
|
165
189
|
//
|
|
166
190
|
|
|
167
191
|
public async recoverIdentity(
|
|
192
|
+
ctx: Context,
|
|
168
193
|
body: RecoverIdentityRequest,
|
|
169
|
-
args?:
|
|
194
|
+
args?: EdgeHttpCallArgs,
|
|
170
195
|
): Promise<RecoverIdentityResponseBody> {
|
|
171
|
-
return this._call(new URL('/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });
|
|
196
|
+
return this._call(ctx, new URL('/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });
|
|
172
197
|
}
|
|
173
198
|
|
|
174
199
|
//
|
|
@@ -176,11 +201,12 @@ export class EdgeHttpClient {
|
|
|
176
201
|
//
|
|
177
202
|
|
|
178
203
|
public async joinSpaceByInvitation(
|
|
204
|
+
ctx: Context,
|
|
179
205
|
spaceId: SpaceId,
|
|
180
206
|
body: JoinSpaceRequest,
|
|
181
|
-
args?:
|
|
207
|
+
args?: EdgeHttpCallArgs,
|
|
182
208
|
): Promise<JoinSpaceResponseBody> {
|
|
183
|
-
return this._call(new URL(`/spaces/${spaceId}/join`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
209
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/join`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
184
210
|
}
|
|
185
211
|
|
|
186
212
|
//
|
|
@@ -188,18 +214,19 @@ export class EdgeHttpClient {
|
|
|
188
214
|
//
|
|
189
215
|
|
|
190
216
|
public async initiateOAuthFlow(
|
|
217
|
+
ctx: Context,
|
|
191
218
|
body: InitiateOAuthFlowRequest,
|
|
192
|
-
args?:
|
|
219
|
+
args?: EdgeHttpCallArgs,
|
|
193
220
|
): Promise<InitiateOAuthFlowResponse> {
|
|
194
|
-
return this._call(new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST' });
|
|
221
|
+
return this._call(ctx, new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST' });
|
|
195
222
|
}
|
|
196
223
|
|
|
197
224
|
//
|
|
198
225
|
// Spaces
|
|
199
226
|
//
|
|
200
227
|
|
|
201
|
-
async createSpace(body: CreateSpaceRequest, args?:
|
|
202
|
-
return this._call(new URL('/spaces/create', this.baseUrl), { ...args, body, method: 'POST' });
|
|
228
|
+
async createSpace(ctx: Context, body: CreateSpaceRequest, args?: EdgeHttpCallArgs): Promise<CreateSpaceResponseBody> {
|
|
229
|
+
return this._call(ctx, new URL('/spaces/create', this.baseUrl), { ...args, body, method: 'POST' });
|
|
203
230
|
}
|
|
204
231
|
|
|
205
232
|
//
|
|
@@ -207,13 +234,16 @@ export class EdgeHttpClient {
|
|
|
207
234
|
//
|
|
208
235
|
|
|
209
236
|
public async queryQueue(
|
|
237
|
+
ctx: Context,
|
|
210
238
|
subspaceTag: string,
|
|
211
239
|
spaceId: SpaceId,
|
|
212
|
-
query: QueueQuery,
|
|
213
|
-
args?:
|
|
214
|
-
): Promise<QueryResult> {
|
|
215
|
-
const
|
|
240
|
+
query: FeedProtocol.QueueQuery,
|
|
241
|
+
args?: EdgeHttpCallArgs,
|
|
242
|
+
): Promise<FeedProtocol.QueryResult> {
|
|
243
|
+
const queueId = query.queueIds?.[0];
|
|
244
|
+
invariant(queueId, 'queueId required');
|
|
216
245
|
return this._call(
|
|
246
|
+
ctx,
|
|
217
247
|
createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {
|
|
218
248
|
after: query.after,
|
|
219
249
|
before: query.before,
|
|
@@ -229,13 +259,14 @@ export class EdgeHttpClient {
|
|
|
229
259
|
}
|
|
230
260
|
|
|
231
261
|
public async insertIntoQueue(
|
|
262
|
+
ctx: Context,
|
|
232
263
|
subspaceTag: string,
|
|
233
264
|
spaceId: SpaceId,
|
|
234
265
|
queueId: ObjectId,
|
|
235
266
|
objects: unknown[],
|
|
236
|
-
args?:
|
|
267
|
+
args?: EdgeHttpCallArgs,
|
|
237
268
|
): Promise<void> {
|
|
238
|
-
return this._call(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
269
|
+
return this._call(ctx, new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
239
270
|
...args,
|
|
240
271
|
body: { objects },
|
|
241
272
|
method: 'POST',
|
|
@@ -243,13 +274,15 @@ export class EdgeHttpClient {
|
|
|
243
274
|
}
|
|
244
275
|
|
|
245
276
|
public async deleteFromQueue(
|
|
277
|
+
ctx: Context,
|
|
246
278
|
subspaceTag: string,
|
|
247
279
|
spaceId: SpaceId,
|
|
248
280
|
queueId: ObjectId,
|
|
249
281
|
objectIds: ObjectId[],
|
|
250
|
-
args?:
|
|
282
|
+
args?: EdgeHttpCallArgs,
|
|
251
283
|
): Promise<void> {
|
|
252
284
|
return this._call(
|
|
285
|
+
ctx,
|
|
253
286
|
createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
254
287
|
ids: objectIds.join(','),
|
|
255
288
|
}),
|
|
@@ -265,9 +298,10 @@ export class EdgeHttpClient {
|
|
|
265
298
|
//
|
|
266
299
|
|
|
267
300
|
public async uploadFunction(
|
|
301
|
+
ctx: Context,
|
|
268
302
|
pathParts: { functionId?: string },
|
|
269
303
|
body: UploadFunctionRequest,
|
|
270
|
-
args?:
|
|
304
|
+
args?: EdgeHttpCallArgs,
|
|
271
305
|
): Promise<UploadFunctionResponseBody> {
|
|
272
306
|
const formData = new FormData();
|
|
273
307
|
formData.append('name', body.name ?? '');
|
|
@@ -284,7 +318,7 @@ export class EdgeHttpClient {
|
|
|
284
318
|
}
|
|
285
319
|
|
|
286
320
|
const path = ['functions', ...(pathParts.functionId ? [pathParts.functionId] : [])].join('/');
|
|
287
|
-
return this._call(new URL(path, this.baseUrl), {
|
|
321
|
+
return this._call(ctx, new URL(path, this.baseUrl), {
|
|
288
322
|
...args,
|
|
289
323
|
body: formData,
|
|
290
324
|
method: 'PUT',
|
|
@@ -292,11 +326,12 @@ export class EdgeHttpClient {
|
|
|
292
326
|
});
|
|
293
327
|
}
|
|
294
328
|
|
|
295
|
-
public async listFunctions(args?:
|
|
296
|
-
return this._call(new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
|
|
329
|
+
public async listFunctions(ctx: Context, args?: EdgeHttpCallArgs): Promise<any> {
|
|
330
|
+
return this._call(ctx, new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
|
|
297
331
|
}
|
|
298
332
|
|
|
299
333
|
public async invokeFunction(
|
|
334
|
+
ctx: Context,
|
|
300
335
|
params: {
|
|
301
336
|
functionId: string;
|
|
302
337
|
version?: string;
|
|
@@ -305,7 +340,7 @@ export class EdgeHttpClient {
|
|
|
305
340
|
subrequestsLimit?: number;
|
|
306
341
|
},
|
|
307
342
|
input: unknown,
|
|
308
|
-
args?:
|
|
343
|
+
args?: EdgeHttpCallArgs,
|
|
309
344
|
): Promise<any> {
|
|
310
345
|
const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
|
|
311
346
|
if (params.version) {
|
|
@@ -321,11 +356,10 @@ export class EdgeHttpClient {
|
|
|
321
356
|
url.searchParams.set('subrequestsLimit', params.subrequestsLimit.toString());
|
|
322
357
|
}
|
|
323
358
|
|
|
324
|
-
return this._call(url, {
|
|
359
|
+
return this._call(ctx, url, {
|
|
325
360
|
...args,
|
|
326
361
|
body: input,
|
|
327
362
|
method: 'POST',
|
|
328
|
-
rawResponse: true,
|
|
329
363
|
});
|
|
330
364
|
}
|
|
331
365
|
|
|
@@ -334,12 +368,13 @@ export class EdgeHttpClient {
|
|
|
334
368
|
//
|
|
335
369
|
|
|
336
370
|
public async executeWorkflow(
|
|
371
|
+
ctx: Context,
|
|
337
372
|
spaceId: SpaceId,
|
|
338
373
|
graphId: ObjectId,
|
|
339
374
|
input: any,
|
|
340
|
-
args?:
|
|
375
|
+
args?: EdgeHttpCallArgs,
|
|
341
376
|
): Promise<ExecuteWorkflowResponseBody> {
|
|
342
|
-
return this._call(new URL(`/workflows/${spaceId}/${graphId}`, this.baseUrl), {
|
|
377
|
+
return this._call(ctx, new URL(`/workflows/${spaceId}/${graphId}`, this.baseUrl), {
|
|
343
378
|
...args,
|
|
344
379
|
body: input,
|
|
345
380
|
method: 'POST',
|
|
@@ -350,8 +385,32 @@ export class EdgeHttpClient {
|
|
|
350
385
|
// Triggers
|
|
351
386
|
//
|
|
352
387
|
|
|
353
|
-
public async getCronTriggers(spaceId: SpaceId) {
|
|
354
|
-
return this._call(new URL(`/
|
|
388
|
+
public async getCronTriggers(ctx: Context, spaceId: SpaceId): Promise<GetCronTriggersResponse> {
|
|
389
|
+
return this._call<GetCronTriggersResponse>(ctx, new URL(`/functions/${spaceId}/triggers/crons`, this.baseUrl), {
|
|
390
|
+
method: 'GET',
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
public async forceRunCronTrigger(ctx: Context, spaceId: SpaceId, triggerId: ObjectId) {
|
|
395
|
+
return this._call(ctx, new URL(`/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {
|
|
396
|
+
method: 'POST',
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
//
|
|
401
|
+
// Query
|
|
402
|
+
//
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Execute a QueryAST query against a space.
|
|
406
|
+
*/
|
|
407
|
+
public async execQuery(
|
|
408
|
+
ctx: Context,
|
|
409
|
+
spaceId: SpaceId,
|
|
410
|
+
body: QueryRequestProto,
|
|
411
|
+
args?: EdgeHttpCallArgs,
|
|
412
|
+
): Promise<QueryResponseProto> {
|
|
413
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/exec-query`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
355
414
|
}
|
|
356
415
|
|
|
357
416
|
//
|
|
@@ -359,19 +418,21 @@ export class EdgeHttpClient {
|
|
|
359
418
|
//
|
|
360
419
|
|
|
361
420
|
public async importBundle(
|
|
362
|
-
|
|
421
|
+
ctx: Context,
|
|
422
|
+
spaceId: SpaceId,
|
|
363
423
|
body: ImportBundleRequest,
|
|
364
|
-
args?:
|
|
424
|
+
args?: EdgeHttpCallArgs,
|
|
365
425
|
): Promise<void> {
|
|
366
|
-
return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), { ...args, body, method: 'PUT' });
|
|
426
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/import`, this.baseUrl), { ...args, body, method: 'PUT' });
|
|
367
427
|
}
|
|
368
428
|
|
|
369
429
|
public async exportBundle(
|
|
430
|
+
ctx: Context,
|
|
370
431
|
spaceId: SpaceId,
|
|
371
432
|
body: ExportBundleRequest,
|
|
372
|
-
args?:
|
|
433
|
+
args?: EdgeHttpCallArgs,
|
|
373
434
|
): Promise<ExportBundleResponse> {
|
|
374
|
-
return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
435
|
+
return this._call(ctx, new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
375
436
|
...args,
|
|
376
437
|
body,
|
|
377
438
|
method: 'POST',
|
|
@@ -390,16 +451,17 @@ export class EdgeHttpClient {
|
|
|
390
451
|
Effect.provide(FetchHttpClient.layer),
|
|
391
452
|
Effect.provide(HttpConfig.default),
|
|
392
453
|
Effect.withSpan('EdgeHttpClient'),
|
|
393
|
-
|
|
454
|
+
runAndForwardErrors,
|
|
394
455
|
) as T;
|
|
395
456
|
}
|
|
396
457
|
|
|
397
458
|
// TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
|
|
398
|
-
private async _call<T>(url: URL, args: EdgeHttpRequestArgs): Promise<T> {
|
|
459
|
+
private async _call<T>(ctx: Context, url: URL, args: EdgeHttpRequestArgs): Promise<T> {
|
|
399
460
|
const shouldRetry = createRetryHandler(args);
|
|
400
|
-
const requestContext = args.context ?? Context.default();
|
|
401
461
|
log('fetch', { url, request: args.body });
|
|
402
462
|
|
|
463
|
+
const traceHeaders = getTraceHeaders(ctx);
|
|
464
|
+
|
|
403
465
|
let handledAuth = false;
|
|
404
466
|
const tryCount = 1;
|
|
405
467
|
while (true) {
|
|
@@ -412,15 +474,12 @@ export class EdgeHttpClient {
|
|
|
412
474
|
}
|
|
413
475
|
}
|
|
414
476
|
|
|
415
|
-
const request = createRequest(args, this._authHeader);
|
|
477
|
+
const request = createRequest(args, this._authHeader, traceHeaders, this._clientTag);
|
|
416
478
|
log('call edge', { url, tryCount, authHeader: !!this._authHeader });
|
|
417
479
|
const response = await fetch(url, request);
|
|
418
480
|
|
|
419
481
|
if (response.ok) {
|
|
420
482
|
const body = await response.clone().json();
|
|
421
|
-
if (args.rawResponse) {
|
|
422
|
-
return body as any;
|
|
423
|
-
}
|
|
424
483
|
invariant(body, 'Expected body to be present');
|
|
425
484
|
if (!('success' in body)) {
|
|
426
485
|
return body;
|
|
@@ -434,13 +493,13 @@ export class EdgeHttpClient {
|
|
|
434
493
|
continue;
|
|
435
494
|
}
|
|
436
495
|
|
|
437
|
-
const body =
|
|
496
|
+
const body: EdgeFailure =
|
|
438
497
|
response.headers.get('Content-Type') === 'application/json' ? await response.clone().json() : undefined;
|
|
439
498
|
|
|
440
499
|
invariant(!body?.success, 'Expected body to not be a failure response or undefined.');
|
|
441
500
|
|
|
442
|
-
if (body?.
|
|
443
|
-
processingError = new EdgeAuthChallengeError(body.
|
|
501
|
+
if (body?.data?.type === 'auth_challenge' && typeof body?.data?.challenge === 'string') {
|
|
502
|
+
processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
|
|
444
503
|
} else if (body?.success === false) {
|
|
445
504
|
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
446
505
|
} else {
|
|
@@ -451,7 +510,7 @@ export class EdgeHttpClient {
|
|
|
451
510
|
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
452
511
|
}
|
|
453
512
|
|
|
454
|
-
if (processingError?.isRetryable && (await shouldRetry(
|
|
513
|
+
if (processingError?.isRetryable && (await shouldRetry(ctx, processingError.retryAfterMs))) {
|
|
455
514
|
log.verbose('retrying edge request', { url, processingError });
|
|
456
515
|
} else {
|
|
457
516
|
throw processingError!;
|
|
@@ -473,6 +532,8 @@ export class EdgeHttpClient {
|
|
|
473
532
|
const createRequest = (
|
|
474
533
|
{ method, body, json = true }: EdgeHttpRequestArgs,
|
|
475
534
|
authHeader: string | undefined,
|
|
535
|
+
traceHeaders?: Record<string, string>,
|
|
536
|
+
clientTag?: string,
|
|
476
537
|
): RequestInit => {
|
|
477
538
|
let requestBody: BodyInit | undefined;
|
|
478
539
|
const headers: HeadersInit = {};
|
|
@@ -492,6 +553,14 @@ const createRequest = (
|
|
|
492
553
|
headers['Authorization'] = authHeader;
|
|
493
554
|
}
|
|
494
555
|
|
|
556
|
+
if (traceHeaders) {
|
|
557
|
+
Object.assign(headers, traceHeaders);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (clientTag) {
|
|
561
|
+
headers[EDGE_CLIENT_TAG_HEADER] = clientTag;
|
|
562
|
+
}
|
|
563
|
+
|
|
495
564
|
return {
|
|
496
565
|
method,
|
|
497
566
|
body: requestBody,
|
|
@@ -499,6 +568,25 @@ const createRequest = (
|
|
|
499
568
|
};
|
|
500
569
|
};
|
|
501
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Extract W3C Trace Context headers (traceparent/tracestate) from a DXOS Context.
|
|
573
|
+
*/
|
|
574
|
+
const getTraceHeaders = (ctx: Context): Record<string, string> | undefined => {
|
|
575
|
+
const spanId = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE);
|
|
576
|
+
const otlpContext =
|
|
577
|
+
typeof spanId === 'number'
|
|
578
|
+
? (TRACE_PROCESSOR.remoteTracing.getSpanContext(spanId) as OtelContext | undefined)
|
|
579
|
+
: undefined;
|
|
580
|
+
|
|
581
|
+
if (!otlpContext) {
|
|
582
|
+
return undefined;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const headers: Record<string, string> = {};
|
|
586
|
+
propagation.inject(otlpContext, headers);
|
|
587
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
588
|
+
};
|
|
589
|
+
|
|
502
590
|
/**
|
|
503
591
|
* @deprecated
|
|
504
592
|
*/
|
|
@@ -50,7 +50,7 @@ export class EdgeWsConnection extends Resource {
|
|
|
50
50
|
|
|
51
51
|
constructor(
|
|
52
52
|
private readonly _identity: EdgeIdentity,
|
|
53
|
-
private readonly _connectionInfo: { url: URL; protocolHeader?: string },
|
|
53
|
+
private readonly _connectionInfo: { url: URL; protocolHeader?: string; headers?: Record<string, string> },
|
|
54
54
|
private readonly _callbacks: EdgeWsConnectionCallbacks,
|
|
55
55
|
) {
|
|
56
56
|
super();
|
|
@@ -121,6 +121,7 @@ export class EdgeWsConnection extends Resource {
|
|
|
121
121
|
this._connectionInfo.protocolHeader
|
|
122
122
|
? [...baseProtocols, this._connectionInfo.protocolHeader]
|
|
123
123
|
: [...baseProtocols],
|
|
124
|
+
this._connectionInfo.headers ? { headers: this._connectionInfo.headers } : undefined,
|
|
124
125
|
);
|
|
125
126
|
const muxer = new WebSocketMuxer(this._ws);
|
|
126
127
|
this._wsMuxer = muxer;
|
package/src/http-client.test.ts
CHANGED
|
@@ -8,6 +8,7 @@ import * as Effect from 'effect/Effect';
|
|
|
8
8
|
import * as Function from 'effect/Function';
|
|
9
9
|
import { afterEach, beforeEach, describe, it } from 'vitest';
|
|
10
10
|
|
|
11
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
11
12
|
import { invariant } from '@dxos/invariant';
|
|
12
13
|
|
|
13
14
|
import { HttpConfig, withLogging, withRetry, withRetryConfig } from './http-client';
|
|
@@ -36,7 +37,7 @@ describe('HttpClient', () => {
|
|
|
36
37
|
withRetry(HttpClient.get(server.url)),
|
|
37
38
|
Effect.provide(FetchHttpClient.layer),
|
|
38
39
|
Effect.withSpan('EdgeHttpClient'),
|
|
39
|
-
|
|
40
|
+
runAndForwardErrors,
|
|
40
41
|
);
|
|
41
42
|
expect(result).toMatchObject({ success: true, data: { value: 100 } });
|
|
42
43
|
}
|
|
@@ -49,7 +50,7 @@ describe('HttpClient', () => {
|
|
|
49
50
|
Effect.provide(FetchHttpClient.layer),
|
|
50
51
|
Effect.provide(HttpConfig.default), // TODO(burdon): Swap out to mock.
|
|
51
52
|
Effect.withSpan('EdgeHttpClient'), // TODO(burdon): OTEL.
|
|
52
|
-
|
|
53
|
+
runAndForwardErrors,
|
|
53
54
|
);
|
|
54
55
|
expect(result).toMatchObject({ success: true, data: { value: 100 } });
|
|
55
56
|
}
|
|
@@ -16,13 +16,13 @@ import { toUint8Array } from '../protocol';
|
|
|
16
16
|
|
|
17
17
|
export const DEFAULT_PORT = 8080;
|
|
18
18
|
|
|
19
|
-
type
|
|
19
|
+
type TestEdgeWsServerProps = {
|
|
20
20
|
admitConnection?: Trigger;
|
|
21
21
|
payloadDecoder?: (payload: Uint8Array) => any;
|
|
22
22
|
messageHandler?: (payload: any) => Promise<Uint8Array | undefined>;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
export const createTestEdgeWsServer = async (port = DEFAULT_PORT, params?:
|
|
25
|
+
export const createTestEdgeWsServer = async (port = DEFAULT_PORT, params?: TestEdgeWsServerProps) => {
|
|
26
26
|
const wsServer = new WebSocket.Server({
|
|
27
27
|
port,
|
|
28
28
|
verifyClient: createConnectionDelayHandler(params),
|
|
@@ -86,7 +86,7 @@ export const createTestEdgeWsServer = async (port = DEFAULT_PORT, params?: TestE
|
|
|
86
86
|
};
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
const createConnectionDelayHandler = (params:
|
|
89
|
+
const createConnectionDelayHandler = (params: TestEdgeWsServerProps | undefined) => {
|
|
90
90
|
return (_: any, callback: (admit: boolean) => void) => {
|
|
91
91
|
if (params?.admitConnection) {
|
|
92
92
|
log('delaying edge connection admission');
|
|
@@ -116,7 +116,7 @@ const createResponseSender = (connection: () => WebSocketMuxer) => {
|
|
|
116
116
|
};
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
-
const decodePayload = async (request: Message, params:
|
|
119
|
+
const decodePayload = async (request: Message, params: TestEdgeWsServerProps | undefined) => {
|
|
120
120
|
const requestPayload = params?.payloadDecoder
|
|
121
121
|
? params.payloadDecoder(request.payload!.value!)
|
|
122
122
|
: protocol.getPayload(request, TextMessageSchema);
|