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

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/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@dxos/edge-client",
3
- "version": "0.8.4-main.ae835ea",
3
+ "version": "0.8.4-main.bc674ce",
4
4
  "description": "EDGE Client",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  ".": {
@@ -42,27 +46,28 @@
42
46
  "README.md"
43
47
  ],
44
48
  "dependencies": {
45
- "@effect/platform": "^0.92.1",
49
+ "@effect/platform": "0.93.6",
46
50
  "isomorphic-ws": "^5.0.0",
47
51
  "ws": "^8.14.2",
48
- "@dxos/credentials": "0.8.4-main.ae835ea",
49
- "@dxos/context": "0.8.4-main.ae835ea",
50
- "@dxos/debug": "0.8.4-main.ae835ea",
51
- "@dxos/invariant": "0.8.4-main.ae835ea",
52
- "@dxos/keyring": "0.8.4-main.ae835ea",
53
- "@dxos/keys": "0.8.4-main.ae835ea",
54
- "@dxos/log": "0.8.4-main.ae835ea",
55
- "@dxos/async": "0.8.4-main.ae835ea",
56
- "@dxos/node-std": "0.8.4-main.ae835ea",
57
- "@dxos/crypto": "0.8.4-main.ae835ea",
58
- "@dxos/protocols": "0.8.4-main.ae835ea",
59
- "@dxos/util": "0.8.4-main.ae835ea"
52
+ "@dxos/async": "0.8.4-main.bc674ce",
53
+ "@dxos/context": "0.8.4-main.bc674ce",
54
+ "@dxos/credentials": "0.8.4-main.bc674ce",
55
+ "@dxos/crypto": "0.8.4-main.bc674ce",
56
+ "@dxos/effect": "0.8.4-main.bc674ce",
57
+ "@dxos/invariant": "0.8.4-main.bc674ce",
58
+ "@dxos/keyring": "0.8.4-main.bc674ce",
59
+ "@dxos/log": "0.8.4-main.bc674ce",
60
+ "@dxos/keys": "0.8.4-main.bc674ce",
61
+ "@dxos/debug": "0.8.4-main.bc674ce",
62
+ "@dxos/node-std": "0.8.4-main.bc674ce",
63
+ "@dxos/protocols": "0.8.4-main.bc674ce",
64
+ "@dxos/util": "0.8.4-main.bc674ce"
60
65
  },
61
66
  "devDependencies": {
62
- "@dxos/test-utils": "0.8.4-main.ae835ea"
67
+ "@dxos/test-utils": "0.8.4-main.bc674ce"
63
68
  },
64
69
  "peerDependencies": {
65
- "effect": "^3.13.3"
70
+ "effect": "3.19.11"
66
71
  },
67
72
  "publishConfig": {
68
73
  "access": "public"
@@ -11,7 +11,7 @@ import { EdgeHttpClient } from './edge-http-client';
11
11
  const DEV_SERVER = 'https://edge.dxos.workers.dev';
12
12
 
13
13
  describe.skipIf(process.env.CI)('EdgeHttpClient', () => {
14
- it.only('should get status', async ({ expect }) => {
14
+ it.skip('should get status', async ({ expect }) => {
15
15
  const client = new EdgeHttpClient(DEV_SERVER);
16
16
  const identity = await createEphemeralEdgeIdentity();
17
17
  client.setIdentity(identity);
@@ -9,6 +9,7 @@ import * as Function from 'effect/Function';
9
9
 
10
10
  import { sleep } from '@dxos/async';
11
11
  import { Context } from '@dxos/context';
12
+ import { runAndForwardErrors } from '@dxos/effect';
12
13
  import { invariant } from '@dxos/invariant';
13
14
  import { type PublicKey, type SpaceId } from '@dxos/keys';
14
15
  import { log } from '@dxos/log';
@@ -18,8 +19,8 @@ import {
18
19
  type CreateSpaceRequest,
19
20
  type CreateSpaceResponseBody,
20
21
  EdgeAuthChallengeError,
21
- type EdgeBody,
22
22
  EdgeCallFailedError,
23
+ type EdgeFailure,
23
24
  type EdgeStatus,
24
25
  type ExecuteWorkflowResponseBody,
25
26
  type ExportBundleRequest,
@@ -77,14 +78,19 @@ type EdgeHttpRequestArgs = {
77
78
  json?: boolean;
78
79
 
79
80
  /**
80
- * Do not expect a standard EDGE JSON response with a `success` field.
81
- * @deprecated Use only for debugging.
81
+ * Force authentication.
82
+ * This should be used for requests with large bodies to avoid sending the body twice.
83
+ * The client will call /auth endpoint to generate the auth header.
82
84
  */
83
- rawResponse?: boolean;
85
+ auth?: boolean;
84
86
  };
85
87
 
86
- export type EdgeHttpGetArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry'>;
87
- export type EdgeHttpPostArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry' | 'body'>;
88
+ export type EdgeHttpGetArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry' | 'auth'>;
89
+ export type EdgeHttpPostArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry' | 'body' | 'auth'>;
90
+
91
+ export type GetCronTriggersResponse = {
92
+ cronIds: string[];
93
+ };
88
94
 
89
95
  export class EdgeHttpClient {
90
96
  private readonly _baseUrl: string;
@@ -117,7 +123,7 @@ export class EdgeHttpClient {
117
123
  //
118
124
 
119
125
  public async getStatus(args?: EdgeHttpGetArgs): Promise<EdgeStatus> {
120
- return this._call(new URL('/status', this.baseUrl), { ...args, method: 'GET' });
126
+ return this._call(new URL('/status', this.baseUrl), { ...args, method: 'GET', auth: true });
121
127
  }
122
128
 
123
129
  //
@@ -206,7 +212,8 @@ export class EdgeHttpClient {
206
212
  query: QueueQuery,
207
213
  args?: EdgeHttpGetArgs,
208
214
  ): Promise<QueryResult> {
209
- const { queueId } = query;
215
+ const queueId = query.queueIds?.[0];
216
+ invariant(queueId, 'queueId required');
210
217
  return this._call(
211
218
  createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {
212
219
  after: query.after,
@@ -268,6 +275,7 @@ export class EdgeHttpClient {
268
275
  formData.append('version', body.version);
269
276
  formData.append('ownerPublicKey', body.ownerPublicKey);
270
277
  formData.append('entryPoint', body.entryPoint);
278
+ body.runtime && formData.append('runtime', body.runtime);
271
279
  for (const [filename, content] of Object.entries(body.assets)) {
272
280
  formData.append(
273
281
  'assets',
@@ -318,7 +326,6 @@ export class EdgeHttpClient {
318
326
  ...args,
319
327
  body: input,
320
328
  method: 'POST',
321
- rawResponse: true,
322
329
  });
323
330
  }
324
331
 
@@ -343,8 +350,16 @@ export class EdgeHttpClient {
343
350
  // Triggers
344
351
  //
345
352
 
346
- public async getCronTriggers(spaceId: SpaceId) {
347
- return this._call(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), { method: 'GET' });
353
+ public async getCronTriggers(spaceId: SpaceId): Promise<GetCronTriggersResponse> {
354
+ return this._call<GetCronTriggersResponse>(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), {
355
+ method: 'GET',
356
+ });
357
+ }
358
+
359
+ public async forceRunCronTrigger(spaceId: SpaceId, triggerId: ObjectId) {
360
+ return this._call(new URL(`/test/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {
361
+ method: 'POST',
362
+ });
348
363
  }
349
364
 
350
365
  //
@@ -375,7 +390,7 @@ export class EdgeHttpClient {
375
390
  // Internal
376
391
  //
377
392
 
378
- private async _fetch<T>(url: URL, args: EdgeHttpRequestArgs): Promise<T> {
393
+ private async _fetch<T>(url: URL, _args: EdgeHttpRequestArgs): Promise<T> {
379
394
  return Function.pipe(
380
395
  HttpClient.get(url),
381
396
  withLogging,
@@ -383,7 +398,7 @@ export class EdgeHttpClient {
383
398
  Effect.provide(FetchHttpClient.layer),
384
399
  Effect.provide(HttpConfig.default),
385
400
  Effect.withSpan('EdgeHttpClient'),
386
- Effect.runPromise,
401
+ runAndForwardErrors,
387
402
  ) as T;
388
403
  }
389
404
 
@@ -394,18 +409,23 @@ export class EdgeHttpClient {
394
409
  log('fetch', { url, request: args.body });
395
410
 
396
411
  let handledAuth = false;
412
+ const tryCount = 1;
397
413
  while (true) {
398
414
  let processingError: EdgeCallFailedError | undefined = undefined;
399
415
  try {
416
+ if (!this._authHeader && args.auth) {
417
+ const response = await fetch(new URL(`/auth`, this.baseUrl));
418
+ if (response.status === 401) {
419
+ this._authHeader = await this._handleUnauthorized(response);
420
+ }
421
+ }
422
+
400
423
  const request = createRequest(args, this._authHeader);
424
+ log('call edge', { url, tryCount, authHeader: !!this._authHeader });
401
425
  const response = await fetch(url, request);
402
- const body: EdgeBody<T> | undefined =
403
- response.headers.get('Content-Type') === 'application/json' ? await response.clone().json() : undefined;
404
426
 
405
427
  if (response.ok) {
406
- if (args.rawResponse) {
407
- return body as any;
408
- }
428
+ const body = await response.clone().json();
409
429
  invariant(body, 'Expected body to be present');
410
430
  if (!('success' in body)) {
411
431
  return body;
@@ -419,10 +439,13 @@ export class EdgeHttpClient {
419
439
  continue;
420
440
  }
421
441
 
442
+ const body: EdgeFailure =
443
+ response.headers.get('Content-Type') === 'application/json' ? await response.clone().json() : undefined;
444
+
422
445
  invariant(!body?.success, 'Expected body to not be a failure response or undefined.');
423
446
 
424
- if (body?.errorData?.type === 'auth_challenge' && typeof body?.errorData?.challenge === 'string') {
425
- processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
447
+ if (body?.data?.type === 'auth_challenge' && typeof body?.data?.challenge === 'string') {
448
+ processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
426
449
  } else if (body?.success === false) {
427
450
  processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
428
451
  } else {
@@ -434,7 +457,7 @@ export class EdgeHttpClient {
434
457
  }
435
458
 
436
459
  if (processingError?.isRetryable && (await shouldRetry(requestContext, processingError.retryAfterMs))) {
437
- log('retrying edge request', { url, processingError });
460
+ log.verbose('retrying edge request', { url, processingError });
438
461
  } else {
439
462
  throw processingError!;
440
463
  }
@@ -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
- Effect.runPromise,
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
- Effect.runPromise,
53
+ runAndForwardErrors,
53
54
  );
54
55
  expect(result).toMatchObject({ success: true, data: { value: 100 } });
55
56
  }
@@ -61,7 +61,11 @@ export const withRetryConfig = (
61
61
  });
62
62
 
63
63
  export const withLogging = <A extends HttpClientResponse.HttpClientResponse, E, R>(effect: Effect.Effect<A, E, R>) =>
64
- effect.pipe(Effect.tap((res) => log.info('response', { status: res.status })));
64
+ effect.pipe(
65
+ Effect.tap((res) => {
66
+ log.info('response', { status: res.status });
67
+ }),
68
+ );
65
69
 
66
70
  /**
67
71
  *
@@ -16,13 +16,13 @@ import { toUint8Array } from '../protocol';
16
16
 
17
17
  export const DEFAULT_PORT = 8080;
18
18
 
19
- type TestEdgeWsServerParams = {
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?: TestEdgeWsServerParams) => {
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: TestEdgeWsServerParams | undefined) => {
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: TestEdgeWsServerParams | undefined) => {
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);