@dxos/edge-client 0.8.3 → 0.8.4-main.28f8d3d
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/{chunk-VHS3XEIX.mjs → chunk-SUXH7FH6.mjs} +15 -8
- package/dist/lib/browser/{chunk-VHS3XEIX.mjs.map → chunk-SUXH7FH6.mjs.map} +3 -3
- package/dist/lib/browser/edge-ws-muxer.mjs +1 -1
- package/dist/lib/browser/index.mjs +239 -149
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +61 -16
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/{chunk-HGQUUFIJ.mjs → chunk-R6K4IIBW.mjs} +15 -8
- package/dist/lib/node-esm/{chunk-HGQUUFIJ.mjs.map → chunk-R6K4IIBW.mjs.map} +3 -3
- package/dist/lib/node-esm/edge-ws-muxer.mjs +1 -1
- package/dist/lib/node-esm/index.mjs +239 -149
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +61 -16
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/edge-client.d.ts +1 -1
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +33 -30
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.test.d.ts +2 -0
- package/dist/types/src/edge-http-client.test.d.ts.map +1 -0
- 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/edge-ws-muxer.d.ts.map +1 -1
- package/dist/types/src/http-client.d.ts +22 -0
- package/dist/types/src/http-client.d.ts.map +1 -0
- package/dist/types/src/http-client.test.d.ts +2 -0
- package/dist/types/src/http-client.test.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-server.d.ts +9 -0
- package/dist/types/src/testing/test-server.d.ts.map +1 -0
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -14
- package/src/edge-client.ts +2 -2
- package/src/edge-http-client.test.ts +22 -0
- package/src/edge-http-client.ts +192 -135
- package/src/edge-ws-connection.ts +11 -3
- package/src/edge-ws-muxer.ts +1 -1
- package/src/http-client.test.ts +54 -0
- package/src/http-client.ts +67 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/test-server.ts +45 -0
- package/src/testing/test-utils.ts +2 -2
- package/src/websocket.test.ts +1 -1
- package/dist/lib/node/chunk-XNHBUTNB.cjs +0 -317
- package/dist/lib/node/chunk-XNHBUTNB.cjs.map +0 -7
- package/dist/lib/node/edge-ws-muxer.cjs +0 -33
- package/dist/lib/node/edge-ws-muxer.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -1060
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/testing/index.cjs +0 -169
- package/dist/lib/node/testing/index.cjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/edge-client",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-main.28f8d3d",
|
|
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,23 +42,27 @@
|
|
|
39
42
|
"README.md"
|
|
40
43
|
],
|
|
41
44
|
"dependencies": {
|
|
45
|
+
"@effect/platform": "^0.90.2",
|
|
42
46
|
"isomorphic-ws": "^5.0.0",
|
|
43
47
|
"ws": "^8.14.2",
|
|
44
|
-
"@dxos/async": "0.8.
|
|
45
|
-
"@dxos/context": "0.8.
|
|
46
|
-
"@dxos/credentials": "0.8.
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/keyring": "0.8.
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/protocols": "0.8.
|
|
55
|
-
"@dxos/util": "0.8.
|
|
48
|
+
"@dxos/async": "0.8.4-main.28f8d3d",
|
|
49
|
+
"@dxos/context": "0.8.4-main.28f8d3d",
|
|
50
|
+
"@dxos/credentials": "0.8.4-main.28f8d3d",
|
|
51
|
+
"@dxos/crypto": "0.8.4-main.28f8d3d",
|
|
52
|
+
"@dxos/debug": "0.8.4-main.28f8d3d",
|
|
53
|
+
"@dxos/invariant": "0.8.4-main.28f8d3d",
|
|
54
|
+
"@dxos/keyring": "0.8.4-main.28f8d3d",
|
|
55
|
+
"@dxos/keys": "0.8.4-main.28f8d3d",
|
|
56
|
+
"@dxos/node-std": "0.8.4-main.28f8d3d",
|
|
57
|
+
"@dxos/log": "0.8.4-main.28f8d3d",
|
|
58
|
+
"@dxos/protocols": "0.8.4-main.28f8d3d",
|
|
59
|
+
"@dxos/util": "0.8.4-main.28f8d3d"
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
58
|
-
"@dxos/test-utils": "0.8.
|
|
62
|
+
"@dxos/test-utils": "0.8.4-main.28f8d3d"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"effect": "^3.13.3"
|
|
59
66
|
},
|
|
60
67
|
"publishConfig": {
|
|
61
68
|
"access": "public"
|
package/src/edge-client.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { createEphemeralEdgeIdentity } from './auth';
|
|
8
|
+
import { EdgeHttpClient } from './edge-http-client';
|
|
9
|
+
|
|
10
|
+
// TODO(burdon): Factor out config.
|
|
11
|
+
const DEV_SERVER = 'https://edge.dxos.workers.dev';
|
|
12
|
+
|
|
13
|
+
describe.skipIf(process.env.CI)('EdgeHttpClient', () => {
|
|
14
|
+
it.only('should get status', async ({ expect }) => {
|
|
15
|
+
const client = new EdgeHttpClient(DEV_SERVER);
|
|
16
|
+
const identity = await createEphemeralEdgeIdentity();
|
|
17
|
+
client.setIdentity(identity);
|
|
18
|
+
|
|
19
|
+
const result = await client.getStatus();
|
|
20
|
+
expect(result).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
});
|
package/src/edge-http-client.ts
CHANGED
|
@@ -2,42 +2,73 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { FetchHttpClient, HttpClient } from '@effect/platform';
|
|
6
|
+
import { Effect, pipe } from 'effect';
|
|
7
|
+
|
|
5
8
|
import { sleep } from '@dxos/async';
|
|
6
9
|
import { Context } from '@dxos/context';
|
|
7
10
|
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
8
11
|
import { log } from '@dxos/log';
|
|
9
12
|
import {
|
|
13
|
+
type CreateAgentRequestBody,
|
|
14
|
+
type CreateAgentResponseBody,
|
|
15
|
+
type CreateSpaceRequest,
|
|
16
|
+
type CreateSpaceResponseBody,
|
|
17
|
+
EdgeAuthChallengeError,
|
|
10
18
|
EdgeCallFailedError,
|
|
11
19
|
type EdgeHttpResponse,
|
|
20
|
+
type EdgeStatus,
|
|
21
|
+
type ExecuteWorkflowResponseBody,
|
|
22
|
+
type GetAgentStatusResponseBody,
|
|
12
23
|
type GetNotarizationResponseBody,
|
|
13
|
-
type
|
|
24
|
+
type InitiateOAuthFlowRequest,
|
|
25
|
+
type InitiateOAuthFlowResponse,
|
|
14
26
|
type JoinSpaceRequest,
|
|
15
27
|
type JoinSpaceResponseBody,
|
|
16
|
-
|
|
17
|
-
type
|
|
18
|
-
type
|
|
19
|
-
type
|
|
28
|
+
type ObjectId,
|
|
29
|
+
type PostNotarizationRequestBody,
|
|
30
|
+
type QueryResult,
|
|
31
|
+
type QueueQuery,
|
|
20
32
|
type RecoverIdentityRequest,
|
|
21
33
|
type RecoverIdentityResponseBody,
|
|
22
34
|
type UploadFunctionRequest,
|
|
23
35
|
type UploadFunctionResponseBody,
|
|
24
|
-
type ObjectId,
|
|
25
|
-
type ExecuteWorkflowResponseBody,
|
|
26
|
-
type QueueQuery,
|
|
27
|
-
type QueryResult,
|
|
28
|
-
type InitiateOAuthFlowRequest,
|
|
29
|
-
type InitiateOAuthFlowResponse,
|
|
30
|
-
type CreateSpaceRequest,
|
|
31
|
-
type CreateSpaceResponseBody,
|
|
32
36
|
} from '@dxos/protocols';
|
|
37
|
+
import { createUrl } from '@dxos/util';
|
|
33
38
|
|
|
34
39
|
import { type EdgeIdentity, handleAuthChallenge } from './edge-identity';
|
|
40
|
+
import { HttpConfig, encodeAuthHeader, withLogging, withRetryConfig } from './http-client';
|
|
35
41
|
import { getEdgeUrlWithProtocol } from './utils';
|
|
36
42
|
|
|
37
43
|
const DEFAULT_RETRY_TIMEOUT = 1500;
|
|
38
44
|
const DEFAULT_RETRY_JITTER = 500;
|
|
39
45
|
const DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
40
46
|
|
|
47
|
+
export type RetryConfig = {
|
|
48
|
+
/**
|
|
49
|
+
* A number of call retries, not counting the initial request.
|
|
50
|
+
*/
|
|
51
|
+
count: number;
|
|
52
|
+
/**
|
|
53
|
+
* Delay before retries in ms.
|
|
54
|
+
*/
|
|
55
|
+
timeout?: number;
|
|
56
|
+
/**
|
|
57
|
+
* A random amount of time before retrying to help prevent large bursts of requests.
|
|
58
|
+
*/
|
|
59
|
+
jitter?: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type EdgeHttpRequestArgs = {
|
|
63
|
+
method: string;
|
|
64
|
+
context?: Context;
|
|
65
|
+
retry?: RetryConfig;
|
|
66
|
+
body?: any;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type EdgeHttpGetArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry'>;
|
|
70
|
+
export type EdgeHttpPostArgs = Pick<EdgeHttpRequestArgs, 'context' | 'retry' | 'body'>;
|
|
71
|
+
|
|
41
72
|
export class EdgeHttpClient {
|
|
42
73
|
private readonly _baseUrl: string;
|
|
43
74
|
|
|
@@ -64,19 +95,38 @@ export class EdgeHttpClient {
|
|
|
64
95
|
}
|
|
65
96
|
}
|
|
66
97
|
|
|
98
|
+
//
|
|
99
|
+
// Status
|
|
100
|
+
//
|
|
101
|
+
|
|
102
|
+
public async getStatus(args?: EdgeHttpGetArgs): Promise<EdgeStatus> {
|
|
103
|
+
return this._call(new URL('/status', this.baseUrl), { ...args, method: 'GET' });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//
|
|
107
|
+
// Agents
|
|
108
|
+
//
|
|
109
|
+
|
|
67
110
|
public createAgent(body: CreateAgentRequestBody, args?: EdgeHttpGetArgs): Promise<CreateAgentResponseBody> {
|
|
68
|
-
return this._call('/agents/create', { ...args, method: 'POST', body });
|
|
111
|
+
return this._call(new URL('/agents/create', this.baseUrl), { ...args, method: 'POST', body });
|
|
69
112
|
}
|
|
70
113
|
|
|
71
114
|
public getAgentStatus(
|
|
72
115
|
request: { ownerIdentityKey: PublicKey },
|
|
73
116
|
args?: EdgeHttpGetArgs,
|
|
74
117
|
): Promise<GetAgentStatusResponseBody> {
|
|
75
|
-
return this._call(`/users/${request.ownerIdentityKey.toHex()}/agent/status`,
|
|
118
|
+
return this._call(new URL(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, this.baseUrl), {
|
|
119
|
+
...args,
|
|
120
|
+
method: 'GET',
|
|
121
|
+
});
|
|
76
122
|
}
|
|
77
123
|
|
|
124
|
+
//
|
|
125
|
+
// Credentials
|
|
126
|
+
//
|
|
127
|
+
|
|
78
128
|
public getCredentialsForNotarization(spaceId: SpaceId, args?: EdgeHttpGetArgs): Promise<GetNotarizationResponseBody> {
|
|
79
|
-
return this._call(`/spaces/${spaceId}/notarization`, { ...args, method: 'GET' });
|
|
129
|
+
return this._call(new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, method: 'GET' });
|
|
80
130
|
}
|
|
81
131
|
|
|
82
132
|
public async notarizeCredentials(
|
|
@@ -84,49 +134,59 @@ export class EdgeHttpClient {
|
|
|
84
134
|
body: PostNotarizationRequestBody,
|
|
85
135
|
args?: EdgeHttpGetArgs,
|
|
86
136
|
): Promise<void> {
|
|
87
|
-
await this._call(`/spaces/${spaceId}/notarization`, { ...args, body, method: 'POST' });
|
|
137
|
+
await this._call(new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
88
138
|
}
|
|
89
139
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
args?: EdgeHttpGetArgs,
|
|
94
|
-
): Promise<JoinSpaceResponseBody> {
|
|
95
|
-
return this._call(`/spaces/${spaceId}/join`, { ...args, body, method: 'POST' });
|
|
96
|
-
}
|
|
140
|
+
//
|
|
141
|
+
// Identity
|
|
142
|
+
//
|
|
97
143
|
|
|
98
144
|
public async recoverIdentity(
|
|
99
145
|
body: RecoverIdentityRequest,
|
|
100
146
|
args?: EdgeHttpGetArgs,
|
|
101
147
|
): Promise<RecoverIdentityResponseBody> {
|
|
102
|
-
return this._call('/identity/recover', { ...args, body, method: 'POST' });
|
|
148
|
+
return this._call(new URL('/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });
|
|
103
149
|
}
|
|
104
150
|
|
|
105
|
-
|
|
151
|
+
//
|
|
152
|
+
// Invitations
|
|
153
|
+
//
|
|
154
|
+
|
|
155
|
+
public async joinSpaceByInvitation(
|
|
106
156
|
spaceId: SpaceId,
|
|
107
|
-
|
|
108
|
-
input: any,
|
|
157
|
+
body: JoinSpaceRequest,
|
|
109
158
|
args?: EdgeHttpGetArgs,
|
|
110
|
-
): Promise<
|
|
111
|
-
return this._call(`/
|
|
159
|
+
): Promise<JoinSpaceResponseBody> {
|
|
160
|
+
return this._call(new URL(`/spaces/${spaceId}/join`, this.baseUrl), { ...args, body, method: 'POST' });
|
|
112
161
|
}
|
|
113
162
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
): Promise<
|
|
119
|
-
|
|
120
|
-
return this._call(path, { ...args, body, method: 'PUT' });
|
|
163
|
+
//
|
|
164
|
+
// OAuth and credentials
|
|
165
|
+
//
|
|
166
|
+
|
|
167
|
+
public async listFunctions(args?: EdgeHttpGetArgs): Promise<any> {
|
|
168
|
+
return this._call(new URL('/functions', this.baseUrl), { ...args, method: 'GET' });
|
|
121
169
|
}
|
|
122
170
|
|
|
123
171
|
public async initiateOAuthFlow(
|
|
124
172
|
body: InitiateOAuthFlowRequest,
|
|
125
173
|
args?: EdgeHttpGetArgs,
|
|
126
174
|
): Promise<InitiateOAuthFlowResponse> {
|
|
127
|
-
return this._call('/oauth/initiate', { ...args, body, method: 'POST' });
|
|
175
|
+
return this._call(new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST' });
|
|
128
176
|
}
|
|
129
177
|
|
|
178
|
+
//
|
|
179
|
+
// Spaces
|
|
180
|
+
//
|
|
181
|
+
|
|
182
|
+
async createSpace(body: CreateSpaceRequest, args?: EdgeHttpGetArgs): Promise<CreateSpaceResponseBody> {
|
|
183
|
+
return this._call(new URL('/spaces/create', this.baseUrl), { ...args, body, method: 'POST' });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//
|
|
187
|
+
// Queues
|
|
188
|
+
//
|
|
189
|
+
|
|
130
190
|
public async queryQueue(
|
|
131
191
|
subspaceTag: string,
|
|
132
192
|
spaceId: SpaceId,
|
|
@@ -134,26 +194,19 @@ export class EdgeHttpClient {
|
|
|
134
194
|
args?: EdgeHttpGetArgs,
|
|
135
195
|
): Promise<QueryResult> {
|
|
136
196
|
const { queueId } = query;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (query.objectIds != null) {
|
|
151
|
-
queryParams.set('objectIds', query.objectIds.join(','));
|
|
152
|
-
}
|
|
153
|
-
return this._call(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query?${queryParams.toString()}`, {
|
|
154
|
-
...args,
|
|
155
|
-
method: 'GET',
|
|
156
|
-
});
|
|
197
|
+
return this._call(
|
|
198
|
+
createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {
|
|
199
|
+
after: query.after,
|
|
200
|
+
before: query.before,
|
|
201
|
+
limit: query.limit,
|
|
202
|
+
reverse: query.reverse,
|
|
203
|
+
objectIds: query.objectIds?.join(','),
|
|
204
|
+
}),
|
|
205
|
+
{
|
|
206
|
+
...args,
|
|
207
|
+
method: 'GET',
|
|
208
|
+
},
|
|
209
|
+
);
|
|
157
210
|
}
|
|
158
211
|
|
|
159
212
|
public async insertIntoQueue(
|
|
@@ -163,72 +216,105 @@ export class EdgeHttpClient {
|
|
|
163
216
|
objects: unknown[],
|
|
164
217
|
args?: EdgeHttpGetArgs,
|
|
165
218
|
): Promise<void> {
|
|
166
|
-
return this._call(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, {
|
|
219
|
+
return this._call(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
167
220
|
...args,
|
|
168
221
|
body: { objects },
|
|
169
222
|
method: 'POST',
|
|
170
223
|
});
|
|
171
224
|
}
|
|
172
225
|
|
|
173
|
-
async deleteFromQueue(
|
|
226
|
+
public async deleteFromQueue(
|
|
174
227
|
subspaceTag: string,
|
|
175
228
|
spaceId: SpaceId,
|
|
176
229
|
queueId: ObjectId,
|
|
177
230
|
objectIds: ObjectId[],
|
|
178
231
|
args?: EdgeHttpGetArgs,
|
|
179
232
|
): Promise<void> {
|
|
180
|
-
return this._call(
|
|
233
|
+
return this._call(
|
|
234
|
+
createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
|
|
235
|
+
ids: objectIds.join(','),
|
|
236
|
+
}),
|
|
237
|
+
{
|
|
238
|
+
...args,
|
|
239
|
+
method: 'DELETE',
|
|
240
|
+
},
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
//
|
|
245
|
+
// Functions
|
|
246
|
+
//
|
|
247
|
+
|
|
248
|
+
public async uploadFunction(
|
|
249
|
+
pathParts: { functionId?: string },
|
|
250
|
+
body: UploadFunctionRequest,
|
|
251
|
+
args?: EdgeHttpGetArgs,
|
|
252
|
+
): Promise<UploadFunctionResponseBody> {
|
|
253
|
+
const path = ['functions', ...(pathParts.functionId ? [pathParts.functionId] : [])].join('/');
|
|
254
|
+
return this._call(new URL(path, this.baseUrl), { ...args, body, method: 'PUT' });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
//
|
|
258
|
+
// Workflows
|
|
259
|
+
//
|
|
260
|
+
|
|
261
|
+
public async executeWorkflow(
|
|
262
|
+
spaceId: SpaceId,
|
|
263
|
+
graphId: ObjectId,
|
|
264
|
+
input: any,
|
|
265
|
+
args?: EdgeHttpGetArgs,
|
|
266
|
+
): Promise<ExecuteWorkflowResponseBody> {
|
|
267
|
+
return this._call(new URL(`/workflows/${spaceId}/${graphId}`, this.baseUrl), {
|
|
181
268
|
...args,
|
|
182
|
-
|
|
183
|
-
method: '
|
|
269
|
+
body: input,
|
|
270
|
+
method: 'POST',
|
|
184
271
|
});
|
|
185
272
|
}
|
|
186
273
|
|
|
187
|
-
|
|
188
|
-
|
|
274
|
+
//
|
|
275
|
+
// Internal
|
|
276
|
+
//
|
|
277
|
+
|
|
278
|
+
private async _fetch<T>(url: URL, args: EdgeHttpRequestArgs): Promise<T> {
|
|
279
|
+
return pipe(
|
|
280
|
+
HttpClient.get(url),
|
|
281
|
+
withLogging,
|
|
282
|
+
withRetryConfig,
|
|
283
|
+
Effect.provide(FetchHttpClient.layer),
|
|
284
|
+
Effect.provide(HttpConfig.default),
|
|
285
|
+
Effect.withSpan('EdgeHttpClient'),
|
|
286
|
+
Effect.runPromise,
|
|
287
|
+
) as T;
|
|
189
288
|
}
|
|
190
289
|
|
|
191
|
-
|
|
192
|
-
|
|
290
|
+
// TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
|
|
291
|
+
private async _call<T>(url: URL, args: EdgeHttpRequestArgs): Promise<T> {
|
|
193
292
|
const shouldRetry = createRetryHandler(args);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (args.query) {
|
|
197
|
-
const queryParams = new URLSearchParams();
|
|
198
|
-
for (const [key, value] of Object.entries(args.query)) {
|
|
199
|
-
queryParams.set(key, value.toString());
|
|
200
|
-
}
|
|
201
|
-
url += `?${queryParams.toString()}`;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
log('call', { method: args.method, path, request: args.body });
|
|
293
|
+
const requestContext = args.context ?? new Context();
|
|
294
|
+
log('fetch', { url, request: args.body });
|
|
205
295
|
|
|
206
296
|
let handledAuth = false;
|
|
207
|
-
let authHeader = this._authHeader;
|
|
208
297
|
while (true) {
|
|
209
298
|
let processingError: EdgeCallFailedError;
|
|
210
299
|
let retryAfterHeaderValue: number = Number.NaN;
|
|
211
300
|
try {
|
|
212
|
-
const request = createRequest(args,
|
|
301
|
+
const request = createRequest(args, this._authHeader);
|
|
213
302
|
const response = await fetch(url, request);
|
|
214
|
-
|
|
215
303
|
retryAfterHeaderValue = Number(response.headers.get('Retry-After'));
|
|
216
|
-
|
|
217
304
|
if (response.ok) {
|
|
218
305
|
const body = (await response.json()) as EdgeHttpResponse<T>;
|
|
219
306
|
if (body.success) {
|
|
220
307
|
return body.data;
|
|
221
308
|
}
|
|
222
309
|
|
|
223
|
-
log('unsuccessful edge response', {
|
|
224
|
-
|
|
310
|
+
log.warn('unsuccessful edge response', { url, body });
|
|
225
311
|
if (body.errorData?.type === 'auth_challenge' && typeof body.errorData?.challenge === 'string') {
|
|
226
312
|
processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
|
|
227
313
|
} else {
|
|
228
314
|
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
229
315
|
}
|
|
230
316
|
} else if (response.status === 401 && !handledAuth) {
|
|
231
|
-
|
|
317
|
+
this._authHeader = await this._handleUnauthorized(response);
|
|
232
318
|
handledAuth = true;
|
|
233
319
|
continue;
|
|
234
320
|
} else {
|
|
@@ -239,7 +325,7 @@ export class EdgeHttpClient {
|
|
|
239
325
|
}
|
|
240
326
|
|
|
241
327
|
if (processingError.isRetryable && (await shouldRetry(requestContext, retryAfterHeaderValue))) {
|
|
242
|
-
log('retrying edge request', {
|
|
328
|
+
log('retrying edge request', { url, processingError });
|
|
243
329
|
} else {
|
|
244
330
|
throw processingError;
|
|
245
331
|
}
|
|
@@ -248,24 +334,35 @@ export class EdgeHttpClient {
|
|
|
248
334
|
|
|
249
335
|
private async _handleUnauthorized(response: Response): Promise<string> {
|
|
250
336
|
if (!this._edgeIdentity) {
|
|
251
|
-
log.warn('
|
|
337
|
+
log.warn('unauthorized response received before identity was set');
|
|
252
338
|
throw EdgeCallFailedError.fromHttpFailure(response);
|
|
253
339
|
}
|
|
340
|
+
|
|
254
341
|
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
255
|
-
|
|
256
|
-
log('auth header updated');
|
|
257
|
-
return this._authHeader;
|
|
342
|
+
return encodeAuthHeader(challenge);
|
|
258
343
|
}
|
|
259
344
|
}
|
|
260
345
|
|
|
261
|
-
const
|
|
262
|
-
|
|
346
|
+
const createRequest = ({ method, body }: EdgeHttpRequestArgs, authHeader: string | undefined): RequestInit => {
|
|
347
|
+
return {
|
|
348
|
+
method,
|
|
349
|
+
body: body && JSON.stringify(body),
|
|
350
|
+
headers: authHeader ? { Authorization: authHeader } : undefined,
|
|
351
|
+
};
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @deprecated
|
|
356
|
+
*/
|
|
357
|
+
const createRetryHandler = ({ retry }: EdgeHttpRequestArgs) => {
|
|
358
|
+
if (!retry || retry.count < 1) {
|
|
263
359
|
return async () => false;
|
|
264
360
|
}
|
|
361
|
+
|
|
265
362
|
let retries = 0;
|
|
266
|
-
const maxRetries =
|
|
267
|
-
const baseTimeout =
|
|
268
|
-
const jitter =
|
|
363
|
+
const maxRetries = retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
364
|
+
const baseTimeout = retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
365
|
+
const jitter = retry.jitter ?? DEFAULT_RETRY_JITTER;
|
|
269
366
|
return async (ctx: Context, retryAfter: number) => {
|
|
270
367
|
if (++retries > maxRetries || ctx.disposed) {
|
|
271
368
|
return false;
|
|
@@ -281,43 +378,3 @@ const createRetryHandler = (args: EdgeHttpCallArgs) => {
|
|
|
281
378
|
return true;
|
|
282
379
|
};
|
|
283
380
|
};
|
|
284
|
-
|
|
285
|
-
export type RetryConfig = {
|
|
286
|
-
/**
|
|
287
|
-
* A number of call retries, not counting the initial request.
|
|
288
|
-
*/
|
|
289
|
-
count: number;
|
|
290
|
-
/**
|
|
291
|
-
* Delay before retries in ms.
|
|
292
|
-
*/
|
|
293
|
-
timeout?: number;
|
|
294
|
-
/**
|
|
295
|
-
* A random amount of time before retrying to help prevent large bursts of requests.
|
|
296
|
-
*/
|
|
297
|
-
jitter?: number;
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
export type EdgeHttpGetArgs = { context?: Context; retry?: RetryConfig };
|
|
301
|
-
|
|
302
|
-
export type EdgeHttpPostArgs = { context?: Context; body?: any; retry?: RetryConfig };
|
|
303
|
-
|
|
304
|
-
type EdgeHttpCallArgs = {
|
|
305
|
-
method: string;
|
|
306
|
-
body?: any;
|
|
307
|
-
context?: Context;
|
|
308
|
-
retry?: RetryConfig;
|
|
309
|
-
query?: Record<string, string>;
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const createRequest = (args: EdgeHttpCallArgs, authHeader: string | undefined): RequestInit => {
|
|
313
|
-
return {
|
|
314
|
-
method: args.method,
|
|
315
|
-
body: args.body && JSON.stringify(args.body),
|
|
316
|
-
headers: authHeader ? { Authorization: authHeader } : undefined,
|
|
317
|
-
};
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const encodeAuthHeader = (challenge: Uint8Array) => {
|
|
321
|
-
const encodedChallenge = Buffer.from(challenge).toString('base64');
|
|
322
|
-
return `VerifiablePresentation pb;base64,${encodedChallenge}`;
|
|
323
|
-
};
|
|
@@ -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 {
|
|
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';
|
|
@@ -30,6 +30,7 @@ export class EdgeWsConnection extends Resource {
|
|
|
30
30
|
private _inactivityTimeoutCtx: Context | undefined;
|
|
31
31
|
private _ws: WebSocket | undefined;
|
|
32
32
|
private _wsMuxer: WebSocketMuxer | undefined;
|
|
33
|
+
private _lastReceivedMessageTimestamp = Date.now();
|
|
33
34
|
|
|
34
35
|
constructor(
|
|
35
36
|
private readonly _identity: EdgeIdentity,
|
|
@@ -111,6 +112,7 @@ export class EdgeWsConnection extends Resource {
|
|
|
111
112
|
log.verbose('message ignored on closed connection', { event: event.type });
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
115
|
+
this._lastReceivedMessageTimestamp = Date.now();
|
|
114
116
|
if (event.data === '__pong__') {
|
|
115
117
|
this._rescheduleHeartbeatTimeout();
|
|
116
118
|
return;
|
|
@@ -172,8 +174,14 @@ export class EdgeWsConnection extends Resource {
|
|
|
172
174
|
this._inactivityTimeoutCtx,
|
|
173
175
|
() => {
|
|
174
176
|
if (this.isOpen) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
if (Date.now() - this._lastReceivedMessageTimestamp > SIGNAL_KEEPALIVE_TIMEOUT) {
|
|
178
|
+
log.warn('restart due to inactivity timeout', {
|
|
179
|
+
lastReceivedMessageTimestamp: this._lastReceivedMessageTimestamp,
|
|
180
|
+
});
|
|
181
|
+
this._callbacks.onRestartRequired();
|
|
182
|
+
} else {
|
|
183
|
+
this._rescheduleHeartbeatTimeout();
|
|
184
|
+
}
|
|
177
185
|
}
|
|
178
186
|
},
|
|
179
187
|
SIGNAL_KEEPALIVE_TIMEOUT,
|
package/src/edge-ws-muxer.ts
CHANGED
|
@@ -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 {
|
|
8
|
+
import { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
9
9
|
|
|
10
10
|
import { protocol } from './defs';
|
|
11
11
|
|