@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/dist/lib/browser/index.mjs +68 -35
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs.map +1 -1
- package/dist/lib/node-esm/index.mjs +68 -35
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +11 -6
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/http-client.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 +22 -17
- package/src/edge-http-client.test.ts +1 -1
- package/src/edge-http-client.ts +44 -21
- package/src/http-client.test.ts +3 -2
- package/src/http-client.ts +5 -1
- package/src/testing/test-utils.ts +4 -4
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/edge-client",
|
|
3
|
-
"version": "0.8.4-main.
|
|
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":
|
|
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": "
|
|
49
|
+
"@effect/platform": "0.93.6",
|
|
46
50
|
"isomorphic-ws": "^5.0.0",
|
|
47
51
|
"ws": "^8.14.2",
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/context": "0.8.4-main.
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
59
|
-
"@dxos/
|
|
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.
|
|
67
|
+
"@dxos/test-utils": "0.8.4-main.bc674ce"
|
|
63
68
|
},
|
|
64
69
|
"peerDependencies": {
|
|
65
|
-
"effect": "
|
|
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.
|
|
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);
|
package/src/edge-http-client.ts
CHANGED
|
@@ -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
|
-
*
|
|
81
|
-
*
|
|
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
|
-
|
|
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
|
|
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), {
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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?.
|
|
425
|
-
processingError = new EdgeAuthChallengeError(body.
|
|
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
|
}
|
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
|
}
|
package/src/http-client.ts
CHANGED
|
@@ -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(
|
|
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
|
|
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);
|