@dxos/edge-client 0.8.4-main.b97322e → 0.8.4-main.dedc0f3

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 (35) hide show
  1. package/dist/lib/browser/{chunk-LMP5TVOP.mjs → chunk-IKP53CBQ.mjs} +44 -11
  2. package/dist/lib/browser/{chunk-LMP5TVOP.mjs.map → chunk-IKP53CBQ.mjs.map} +3 -3
  3. package/dist/lib/browser/edge-ws-muxer.mjs +1 -1
  4. package/dist/lib/browser/index.mjs +392 -244
  5. package/dist/lib/browser/index.mjs.map +4 -4
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/testing/index.mjs +1 -1
  8. package/dist/lib/browser/testing/index.mjs.map +2 -2
  9. package/dist/lib/node-esm/{chunk-X7J46ISZ.mjs → chunk-DR5YNW5K.mjs} +44 -11
  10. package/dist/lib/node-esm/{chunk-X7J46ISZ.mjs.map → chunk-DR5YNW5K.mjs.map} +3 -3
  11. package/dist/lib/node-esm/edge-ws-muxer.mjs +1 -1
  12. package/dist/lib/node-esm/index.mjs +392 -244
  13. package/dist/lib/node-esm/index.mjs.map +4 -4
  14. package/dist/lib/node-esm/meta.json +1 -1
  15. package/dist/lib/node-esm/testing/index.mjs +1 -1
  16. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  17. package/dist/types/src/edge-client.d.ts +15 -15
  18. package/dist/types/src/edge-client.d.ts.map +1 -1
  19. package/dist/types/src/edge-http-client.d.ts +21 -2
  20. package/dist/types/src/edge-http-client.d.ts.map +1 -1
  21. package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
  22. package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
  23. package/dist/types/src/index.d.ts +4 -3
  24. package/dist/types/src/index.d.ts.map +1 -1
  25. package/dist/types/src/testing/test-utils.d.ts.map +1 -1
  26. package/dist/types/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +18 -15
  28. package/src/edge-client.ts +40 -40
  29. package/src/edge-http-client.ts +153 -20
  30. package/src/edge-ws-connection.ts +1 -1
  31. package/src/edge-ws-muxer.ts +1 -1
  32. package/src/http-client.test.ts +2 -1
  33. package/src/index.ts +4 -3
  34. package/src/testing/test-utils.ts +1 -1
  35. package/src/websocket.test.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/edge-client",
3
- "version": "0.8.4-main.b97322e",
3
+ "version": "0.8.4-main.dedc0f3",
4
4
  "description": "EDGE Client",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -10,16 +10,19 @@
10
10
  "type": "module",
11
11
  "exports": {
12
12
  ".": {
13
+ "source": "./src/index.ts",
13
14
  "types": "./dist/types/src/index.d.ts",
14
15
  "browser": "./dist/lib/browser/index.mjs",
15
16
  "node": "./dist/lib/node-esm/index.mjs"
16
17
  },
17
18
  "./muxer": {
19
+ "source": "./src/edge-ws-muxer.ts",
18
20
  "types": "./dist/types/src/edge-ws-muxer.d.ts",
19
21
  "browser": "./dist/lib/browser/edge-ws-muxer.mjs",
20
22
  "node": "./dist/lib/node-esm/edge-ws-muxer.mjs"
21
23
  },
22
24
  "./testing": {
25
+ "source": "./src/testing/index.ts",
23
26
  "types": "./dist/types/src/testing/index.d.ts",
24
27
  "browser": "./dist/lib/browser/testing/index.mjs",
25
28
  "node": "./dist/lib/node-esm/testing/index.mjs"
@@ -39,24 +42,24 @@
39
42
  "README.md"
40
43
  ],
41
44
  "dependencies": {
42
- "@effect/platform": "^0.89.0",
45
+ "@effect/platform": "^0.90.2",
43
46
  "isomorphic-ws": "^5.0.0",
44
47
  "ws": "^8.14.2",
45
- "@dxos/async": "0.8.4-main.b97322e",
46
- "@dxos/context": "0.8.4-main.b97322e",
47
- "@dxos/credentials": "0.8.4-main.b97322e",
48
- "@dxos/crypto": "0.8.4-main.b97322e",
49
- "@dxos/debug": "0.8.4-main.b97322e",
50
- "@dxos/invariant": "0.8.4-main.b97322e",
51
- "@dxos/keys": "0.8.4-main.b97322e",
52
- "@dxos/keyring": "0.8.4-main.b97322e",
53
- "@dxos/log": "0.8.4-main.b97322e",
54
- "@dxos/protocols": "0.8.4-main.b97322e",
55
- "@dxos/node-std": "0.8.4-main.b97322e",
56
- "@dxos/util": "0.8.4-main.b97322e"
48
+ "@dxos/async": "0.8.4-main.dedc0f3",
49
+ "@dxos/context": "0.8.4-main.dedc0f3",
50
+ "@dxos/crypto": "0.8.4-main.dedc0f3",
51
+ "@dxos/credentials": "0.8.4-main.dedc0f3",
52
+ "@dxos/invariant": "0.8.4-main.dedc0f3",
53
+ "@dxos/debug": "0.8.4-main.dedc0f3",
54
+ "@dxos/keyring": "0.8.4-main.dedc0f3",
55
+ "@dxos/keys": "0.8.4-main.dedc0f3",
56
+ "@dxos/node-std": "0.8.4-main.dedc0f3",
57
+ "@dxos/log": "0.8.4-main.dedc0f3",
58
+ "@dxos/protocols": "0.8.4-main.dedc0f3",
59
+ "@dxos/util": "0.8.4-main.dedc0f3"
57
60
  },
58
61
  "devDependencies": {
59
- "@dxos/test-utils": "0.8.4-main.b97322e"
62
+ "@dxos/test-utils": "0.8.4-main.dedc0f3"
60
63
  },
61
64
  "peerDependencies": {
62
65
  "effect": "^3.13.3"
@@ -2,8 +2,8 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Trigger, scheduleMicroTask, TriggerState, PersistentLifecycle, Event } from '@dxos/async';
6
- import { Resource, type Lifecycle } from '@dxos/context';
5
+ import { Event, PersistentLifecycle, Trigger, TriggerState, scheduleMicroTask } from '@dxos/async';
6
+ import { type Lifecycle, Resource } from '@dxos/context';
7
7
  import { log, logInfo } from '@dxos/log';
8
8
  import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
9
9
  import { EdgeStatus } from '@dxos/protocols/proto/dxos/client/services';
@@ -20,6 +20,13 @@ const DEFAULT_TIMEOUT = 10_000;
20
20
  export type MessageListener = (message: Message) => void;
21
21
  export type ReconnectListener = () => void;
22
22
 
23
+ export type MessengerConfig = {
24
+ socketEndpoint: string;
25
+ timeout?: number;
26
+ protocol?: Protocol;
27
+ disableAuth?: boolean;
28
+ };
29
+
23
30
  export interface EdgeConnection extends Required<Lifecycle> {
24
31
  statusChanged: Event<EdgeStatus>;
25
32
  get info(): any;
@@ -28,18 +35,11 @@ export interface EdgeConnection extends Required<Lifecycle> {
28
35
  get isOpen(): boolean;
29
36
  get status(): EdgeStatus;
30
37
  setIdentity(identity: EdgeIdentity): void;
38
+ send(message: Message): Promise<void>;
31
39
  onMessage(listener: MessageListener): () => void;
32
40
  onReconnected(listener: ReconnectListener): () => void;
33
- send(message: Message): Promise<void>;
34
41
  }
35
42
 
36
- export type MessengerConfig = {
37
- socketEndpoint: string;
38
- timeout?: number;
39
- protocol?: Protocol;
40
- disableAuth?: boolean;
41
- };
42
-
43
43
  /**
44
44
  * Messenger client for EDGE:
45
45
  * - While open, uses PersistentLifecycle to keep an open EdgeWsConnection, reconnecting on failures.
@@ -80,7 +80,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
80
80
  };
81
81
  }
82
82
 
83
- get status(): EdgeStatus {
83
+ get status() {
84
84
  return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED
85
85
  ? EdgeStatus.CONNECTED
86
86
  : EdgeStatus.NOT_CONNECTED;
@@ -94,7 +94,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
94
94
  return this._identity.peerKey;
95
95
  }
96
96
 
97
- setIdentity(identity: EdgeIdentity): void {
97
+ setIdentity(identity: EdgeIdentity) {
98
98
  if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
99
99
  log('Edge identity changed', { identity, oldIdentity: this._identity });
100
100
  this._identity = identity;
@@ -103,12 +103,36 @@ export class EdgeClient extends Resource implements EdgeConnection {
103
103
  }
104
104
  }
105
105
 
106
- public onMessage(listener: MessageListener): () => void {
106
+ /**
107
+ * Send message.
108
+ * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
109
+ */
110
+ public async send(message: Message) {
111
+ if (this._ready.state !== TriggerState.RESOLVED) {
112
+ log('waiting for websocket');
113
+ await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });
114
+ }
115
+
116
+ if (!this._currentConnection) {
117
+ throw new EdgeConnectionClosedError();
118
+ }
119
+
120
+ if (
121
+ message.source &&
122
+ (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)
123
+ ) {
124
+ throw new EdgeIdentityChangedError();
125
+ }
126
+
127
+ this._currentConnection.send(message);
128
+ }
129
+
130
+ public onMessage(listener: MessageListener) {
107
131
  this._messageListeners.add(listener);
108
132
  return () => this._messageListeners.delete(listener);
109
133
  }
110
134
 
111
- public onReconnected(listener: () => void): () => void {
135
+ public onReconnected(listener: () => void) {
112
136
  this._reconnectListeners.add(listener);
113
137
  if (this._ready.state === TriggerState.RESOLVED) {
114
138
  // Microtask so that listener is always called asynchronously, no matter the state of the ready trigger
@@ -123,6 +147,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
123
147
  }
124
148
  });
125
149
  }
150
+
126
151
  return () => this._reconnectListeners.delete(listener);
127
152
  }
128
153
 
@@ -200,7 +225,6 @@ export class EdgeClient extends Resource implements EdgeConnection {
200
225
  // Race with restartRequired so that restart is not blocked by _connect execution.
201
226
  // Wait on ready to attempt a reconnect if it times out.
202
227
  await Promise.race([this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT }), restartRequired]);
203
-
204
228
  return connection;
205
229
  }
206
230
 
@@ -237,30 +261,6 @@ export class EdgeClient extends Resource implements EdgeConnection {
237
261
  }
238
262
  }
239
263
 
240
- /**
241
- * Send message.
242
- * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
243
- */
244
- public async send(message: Message): Promise<void> {
245
- if (this._ready.state !== TriggerState.RESOLVED) {
246
- log('waiting for websocket to become ready');
247
- await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });
248
- }
249
-
250
- if (!this._currentConnection) {
251
- throw new EdgeConnectionClosedError();
252
- }
253
-
254
- if (
255
- message.source &&
256
- (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)
257
- ) {
258
- throw new EdgeIdentityChangedError();
259
- }
260
-
261
- this._currentConnection.send(message);
262
- }
263
-
264
264
  private async _createAuthHeader(path: string): Promise<string | undefined> {
265
265
  const httpUrl = new URL(path, this._baseHttpUrl);
266
266
  httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), 'http');
@@ -277,7 +277,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
277
277
  }
278
278
 
279
279
  const encodePresentationWsAuthHeader = (encodedPresentation: Uint8Array): string => {
280
- // = and / characters are not allowed in the WebSocket subprotocol header.
280
+ // '=' and '/' characters are not allowed in the WebSocket subprotocol header.
281
281
  const encodedToken = Buffer.from(encodedPresentation).toString('base64').replace(/=*$/, '').replaceAll('/', '|');
282
282
  return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
283
283
  };
@@ -10,39 +10,43 @@ import { Context } from '@dxos/context';
10
10
  import { type PublicKey, type SpaceId } from '@dxos/keys';
11
11
  import { log } from '@dxos/log';
12
12
  import {
13
- type CreateAgentResponseBody,
14
13
  type CreateAgentRequestBody,
14
+ type CreateAgentResponseBody,
15
15
  type CreateSpaceRequest,
16
16
  type CreateSpaceResponseBody,
17
17
  EdgeAuthChallengeError,
18
18
  EdgeCallFailedError,
19
19
  type EdgeHttpResponse,
20
+ type EdgeStatus,
20
21
  type ExecuteWorkflowResponseBody,
22
+ type ExportBundleRequest,
23
+ type ExportBundleResponse,
21
24
  type GetAgentStatusResponseBody,
22
25
  type GetNotarizationResponseBody,
26
+ type ImportBundleRequest,
23
27
  type InitiateOAuthFlowRequest,
24
28
  type InitiateOAuthFlowResponse,
25
29
  type JoinSpaceRequest,
26
30
  type JoinSpaceResponseBody,
27
- type RecoverIdentityRequest,
28
- type RecoverIdentityResponseBody,
29
31
  type ObjectId,
30
32
  type PostNotarizationRequestBody,
31
- type QueueQuery,
32
33
  type QueryResult,
34
+ type QueueQuery,
35
+ type RecoverIdentityRequest,
36
+ type RecoverIdentityResponseBody,
33
37
  type UploadFunctionRequest,
34
38
  type UploadFunctionResponseBody,
35
- type EdgeStatus,
36
39
  } from '@dxos/protocols';
37
40
  import { createUrl } from '@dxos/util';
38
41
 
39
42
  import { type EdgeIdentity, handleAuthChallenge } from './edge-identity';
40
- import { encodeAuthHeader, HttpConfig, withLogging, withRetryConfig } from './http-client';
43
+ import { HttpConfig, encodeAuthHeader, withLogging, withRetryConfig } from './http-client';
41
44
  import { getEdgeUrlWithProtocol } from './utils';
42
45
 
43
46
  const DEFAULT_RETRY_TIMEOUT = 1500;
44
47
  const DEFAULT_RETRY_JITTER = 500;
45
48
  const DEFAULT_MAX_RETRIES_COUNT = 3;
49
+ const WARNING_BODY_SIZE = 10 * 1024 * 1024; // 10MB
46
50
 
47
51
  export type RetryConfig = {
48
52
  /**
@@ -64,6 +68,15 @@ type EdgeHttpRequestArgs = {
64
68
  context?: Context;
65
69
  retry?: RetryConfig;
66
70
  body?: any;
71
+ /**
72
+ * @default true
73
+ */
74
+ json?: boolean;
75
+
76
+ /**
77
+ * Do not expect a standard EDGE JSON response with a `success` field.
78
+ */
79
+ rawResponse?: boolean;
67
80
  };
68
81
 
69
82
  export type EdgeHttpGetArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry'>;
@@ -164,10 +177,6 @@ export class EdgeHttpClient {
164
177
  // OAuth and credentials
165
178
  //
166
179
 
167
- public async listFunctions(args?: EdgeHttpGetArgs): Promise<any> {
168
- return this._call(new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
169
- }
170
-
171
180
  public async initiateOAuthFlow(
172
181
  body: InitiateOAuthFlowRequest,
173
182
  args?: EdgeHttpGetArgs,
@@ -250,8 +259,63 @@ export class EdgeHttpClient {
250
259
  body: UploadFunctionRequest,
251
260
  args?: EdgeHttpGetArgs,
252
261
  ): Promise<UploadFunctionResponseBody> {
262
+ const formData = new FormData();
263
+ formData.append('name', body.name ?? '');
264
+ formData.append('version', body.version);
265
+ formData.append('ownerPublicKey', body.ownerPublicKey);
266
+ formData.append('entryPoint', body.entryPoint);
267
+ for (const [filename, content] of Object.entries(body.assets)) {
268
+ formData.append(
269
+ 'assets',
270
+ new Blob([content as Uint8Array<ArrayBuffer>], { type: getFileMimeType(filename) }),
271
+ filename,
272
+ );
273
+ }
274
+
253
275
  const path = ['functions', ...(pathParts.functionId ? [pathParts.functionId] : [])].join('/');
254
- return this._call(new URL(path, this.baseUrl), { ...args, body, method: 'PUT' });
276
+ return this._call(new URL(path, this.baseUrl), {
277
+ ...args,
278
+ body: formData,
279
+ method: 'PUT',
280
+ json: false,
281
+ });
282
+ }
283
+
284
+ public async listFunctions(args?: EdgeHttpGetArgs): Promise<any> {
285
+ return this._call(new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
286
+ }
287
+
288
+ public async invokeFunction(
289
+ params: {
290
+ functionId: string;
291
+ version?: string;
292
+ spaceId?: SpaceId;
293
+ cpuTimeLimit?: number;
294
+ subrequestsLimit?: number;
295
+ },
296
+ input: unknown,
297
+ args?: EdgeHttpGetArgs,
298
+ ): Promise<any> {
299
+ const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
300
+ if (params.version) {
301
+ url.searchParams.set('version', params.version);
302
+ }
303
+ if (params.spaceId) {
304
+ url.searchParams.set('spaceId', params.spaceId.toString());
305
+ }
306
+ if (params.cpuTimeLimit) {
307
+ url.searchParams.set('cpuTimeLimit', params.cpuTimeLimit.toString());
308
+ }
309
+ if (params.subrequestsLimit) {
310
+ url.searchParams.set('subrequestsLimit', params.subrequestsLimit.toString());
311
+ }
312
+
313
+ return this._call(url, {
314
+ ...args,
315
+ body: input,
316
+ method: 'POST',
317
+ rawResponse: true,
318
+ });
255
319
  }
256
320
 
257
321
  //
@@ -271,6 +335,38 @@ export class EdgeHttpClient {
271
335
  });
272
336
  }
273
337
 
338
+ //
339
+ // Triggers
340
+ //
341
+
342
+ public async getCronTriggers(spaceId: SpaceId) {
343
+ return this._call(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), { method: 'GET' });
344
+ }
345
+
346
+ //
347
+ // Import/Export space.
348
+ //
349
+
350
+ public async importBundle(
351
+ spaceId: SpaceId, //
352
+ body: ImportBundleRequest,
353
+ args?: EdgeHttpGetArgs,
354
+ ): Promise<void> {
355
+ return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), { ...args, body, method: 'PUT' });
356
+ }
357
+
358
+ public async exportBundle(
359
+ spaceId: SpaceId,
360
+ body: ExportBundleRequest,
361
+ args?: EdgeHttpGetArgs,
362
+ ): Promise<ExportBundleResponse> {
363
+ return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
364
+ ...args,
365
+ body,
366
+ method: 'POST',
367
+ });
368
+ }
369
+
274
370
  //
275
371
  // Internal
276
372
  //
@@ -295,7 +391,7 @@ export class EdgeHttpClient {
295
391
 
296
392
  let handledAuth = false;
297
393
  while (true) {
298
- let processingError: EdgeCallFailedError;
394
+ let processingError: EdgeCallFailedError | undefined = undefined;
299
395
  let retryAfterHeaderValue: number = Number.NaN;
300
396
  try {
301
397
  const request = createRequest(args, this._authHeader);
@@ -303,6 +399,15 @@ export class EdgeHttpClient {
303
399
  retryAfterHeaderValue = Number(response.headers.get('Retry-After'));
304
400
  if (response.ok) {
305
401
  const body = (await response.json()) as EdgeHttpResponse<T>;
402
+
403
+ if (args.rawResponse) {
404
+ return body as any;
405
+ }
406
+
407
+ if (!('success' in body)) {
408
+ return body;
409
+ }
410
+
306
411
  if (body.success) {
307
412
  return body.data;
308
413
  }
@@ -310,7 +415,7 @@ export class EdgeHttpClient {
310
415
  log.warn('unsuccessful edge response', { url, body });
311
416
  if (body.errorData?.type === 'auth_challenge' && typeof body.errorData?.challenge === 'string') {
312
417
  processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
313
- } else {
418
+ } else if (body.errorData) {
314
419
  processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
315
420
  }
316
421
  } else if (response.status === 401 && !handledAuth) {
@@ -318,16 +423,16 @@ export class EdgeHttpClient {
318
423
  handledAuth = true;
319
424
  continue;
320
425
  } else {
321
- processingError = EdgeCallFailedError.fromHttpFailure(response);
426
+ processingError = await EdgeCallFailedError.fromHttpFailure(response);
322
427
  }
323
428
  } catch (error: any) {
324
429
  processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
325
430
  }
326
431
 
327
- if (processingError.isRetryable && (await shouldRetry(requestContext, retryAfterHeaderValue))) {
432
+ if (processingError?.isRetryable && (await shouldRetry(requestContext, retryAfterHeaderValue))) {
328
433
  log('retrying edge request', { url, processingError });
329
434
  } else {
330
- throw processingError;
435
+ throw processingError!;
331
436
  }
332
437
  }
333
438
  }
@@ -335,7 +440,7 @@ export class EdgeHttpClient {
335
440
  private async _handleUnauthorized(response: Response): Promise<string> {
336
441
  if (!this._edgeIdentity) {
337
442
  log.warn('unauthorized response received before identity was set');
338
- throw EdgeCallFailedError.fromHttpFailure(response);
443
+ throw await EdgeCallFailedError.fromHttpFailure(response);
339
444
  }
340
445
 
341
446
  const challenge = await handleAuthChallenge(response, this._edgeIdentity);
@@ -343,11 +448,32 @@ export class EdgeHttpClient {
343
448
  }
344
449
  }
345
450
 
346
- const createRequest = ({ method, body }: EdgeHttpRequestArgs, authHeader: string | undefined): RequestInit => {
451
+ const createRequest = (
452
+ { method, body, json = true }: EdgeHttpRequestArgs,
453
+ authHeader: string | undefined,
454
+ ): RequestInit => {
455
+ let requestBody: BodyInit | undefined;
456
+ const headers: HeadersInit = {};
457
+
458
+ if (json) {
459
+ requestBody = body && JSON.stringify(body);
460
+ headers['Content-Type'] = 'application/json';
461
+ } else {
462
+ requestBody = body;
463
+ }
464
+
465
+ if (typeof requestBody === 'string' && requestBody.length > WARNING_BODY_SIZE) {
466
+ log.warn('Request with large body', { bodySize: requestBody.length });
467
+ }
468
+
469
+ if (authHeader) {
470
+ headers['Authorization'] = authHeader;
471
+ }
472
+
347
473
  return {
348
474
  method,
349
- body: body && JSON.stringify(body),
350
- headers: authHeader ? { Authorization: authHeader } : undefined,
475
+ body: requestBody,
476
+ headers,
351
477
  };
352
478
  };
353
479
 
@@ -378,3 +504,10 @@ const createRetryHandler = ({ retry }: EdgeHttpRequestArgs) => {
378
504
  return true;
379
505
  };
380
506
  };
507
+
508
+ const getFileMimeType = (filename: string) =>
509
+ ['.js', '.mjs'].some((codeExtension) => filename.endsWith(codeExtension))
510
+ ? 'application/javascript+module'
511
+ : filename.endsWith('.wasm')
512
+ ? 'application/wasm'
513
+ : 'application/octet-stream';
@@ -10,7 +10,7 @@ import { invariant } from '@dxos/invariant';
10
10
  import { log, logInfo } from '@dxos/log';
11
11
  import { EdgeWebsocketProtocol } from '@dxos/protocols';
12
12
  import { buf } from '@dxos/protocols/buf';
13
- import { MessageSchema, type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
13
+ import { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
14
14
 
15
15
  import { protocol } from './defs';
16
16
  import { type EdgeIdentity } from './edge-identity';
@@ -5,7 +5,7 @@
5
5
  import { Trigger } from '@dxos/async';
6
6
  import { log } from '@dxos/log';
7
7
  import { buf } from '@dxos/protocols/buf';
8
- import { MessageSchema, type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
8
+ import { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
9
9
 
10
10
  import { protocol } from './defs';
11
11
 
@@ -23,8 +23,9 @@ describe('HttpClient', () => {
23
23
  server = undefined;
24
24
  });
25
25
 
26
- // TODO(burdon): Auth headers.
26
+ // TODO(burdon): Auth headers/API key for admin.
27
27
  // TODO(burdon): Add request/response schema type checking.
28
+ // TODO(burdon): Test swarm.
28
29
  it.skipIf(process.env.CI)('should retry', async ({ expect }) => {
29
30
  invariant(server);
30
31
 
package/src/index.ts CHANGED
@@ -4,11 +4,12 @@
4
4
 
5
5
  export * from '@dxos/protocols/buf/dxos/edge/messenger_pb';
6
6
 
7
- export * from './edge-client';
7
+ export * from './auth';
8
8
  export * from './defs';
9
- export * from './protocol';
9
+ export * from './edge-client';
10
10
  export * from './errors';
11
- export * from './auth';
11
+ export * from './protocol';
12
12
  export * from './edge-http-client';
13
13
  export * from './edge-identity';
14
14
  export * from './edge-ws-muxer';
15
+ export * from './http-client';
@@ -8,7 +8,7 @@ import { Trigger } from '@dxos/async';
8
8
  import { log } from '@dxos/log';
9
9
  import { EdgeWebsocketProtocol } from '@dxos/protocols';
10
10
  import { buf } from '@dxos/protocols/buf';
11
- import { MessageSchema, TextMessageSchema, type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
11
+ import { type Message, MessageSchema, TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
12
12
 
13
13
  import { protocol } from '../defs';
14
14
  import { WebSocketMuxer } from '../edge-ws-muxer';
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import WebSocket from 'isomorphic-ws';
6
- import { describe, expect, test, onTestFinished } from 'vitest';
6
+ import { describe, expect, onTestFinished, test } from 'vitest';
7
7
 
8
8
  import { Trigger, TriggerState } from '@dxos/async';
9
9