@dxos/edge-client 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6

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