@procedures/peer 0.2.3 → 0.3.0
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/CHANGELOG.md +48 -0
- package/README.md +3 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/connection/index.d.ts +3 -2
- package/dist/connection/index.d.ts.map +1 -1
- package/dist/connection/index.js +2 -1
- package/dist/connection/index.js.map +1 -1
- package/dist/discovery/index.d.ts +4 -2
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +22 -34
- package/dist/discovery/index.js.map +1 -1
- package/dist/peer/index.d.ts +16 -3
- package/dist/peer/index.d.ts.map +1 -1
- package/dist/peer/index.js +45 -6
- package/dist/peer/index.js.map +1 -1
- package/dist/procedures/manifest.generate.d.ts.map +1 -1
- package/dist/procedures/manifest.generate.js +10 -33
- package/dist/procedures/manifest.generate.js.map +1 -1
- package/dist/procedures/server.call.d.ts.map +1 -1
- package/dist/procedures/server.call.js +8 -3
- package/dist/procedures/server.call.js.map +1 -1
- package/dist/procedures/server.connect.d.ts.map +1 -1
- package/dist/procedures/server.connect.js +3 -1
- package/dist/procedures/server.connect.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/connection/index.ts +5 -4
- package/src/discovery/index.ts +26 -35
- package/src/peer/index.ts +70 -7
- package/src/procedures/manifest.generate.ts +10 -33
- package/src/procedures/server.call.ts +8 -3
- package/src/procedures/server.connect.ts +3 -1
- package/src/types.ts +2 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/peer",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Serve procedures: HTTP and WebSocket peers with token authentication, browser checks, an expose list, server.create, status and stop, and manifests.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"server",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"procedures": "./dist/register.js"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@procedures/client": "0.
|
|
80
|
+
"@procedures/client": "0.3.0",
|
|
81
81
|
"zod": "^4.6.5"
|
|
82
82
|
},
|
|
83
83
|
"optionalDependencies": {
|
package/src/connection/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
ProcedureInfo,
|
|
20
20
|
ProcedureAnnounceMessage,
|
|
21
21
|
} from "../types.js";
|
|
22
|
+
import { ms, type Duration } from "@procedures/client";
|
|
22
23
|
|
|
23
24
|
// =============================================================================
|
|
24
25
|
// Types
|
|
@@ -29,8 +30,8 @@ export interface ConnectOptions {
|
|
|
29
30
|
address: string;
|
|
30
31
|
/** Transport type (auto-detected from URL if not specified) */
|
|
31
32
|
transport?: TransportType;
|
|
32
|
-
/**
|
|
33
|
-
timeout?:
|
|
33
|
+
/** The time limit of the connection, for example `seconds(10)` */
|
|
34
|
+
timeout?: Duration;
|
|
34
35
|
/** Local peer ID to identify ourselves */
|
|
35
36
|
localPeerId?: string;
|
|
36
37
|
}
|
|
@@ -143,7 +144,7 @@ function detectTransport(address: string): TransportType {
|
|
|
143
144
|
function createClient(
|
|
144
145
|
address: string,
|
|
145
146
|
transport: TransportType,
|
|
146
|
-
timeout?:
|
|
147
|
+
timeout?: Duration
|
|
147
148
|
): Client {
|
|
148
149
|
switch (transport) {
|
|
149
150
|
case "http": {
|
|
@@ -164,7 +165,7 @@ function createClient(
|
|
|
164
165
|
return new Client({
|
|
165
166
|
transport: new WebSocketTransport({
|
|
166
167
|
url: address,
|
|
167
|
-
connectionTimeout: timeout ?? 10000,
|
|
168
|
+
connectionTimeout: timeout ?? ms(10000),
|
|
168
169
|
reconnect: {
|
|
169
170
|
enabled: true,
|
|
170
171
|
maxAttempts: 3,
|
package/src/discovery/index.ts
CHANGED
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
type ProcedureRegistryLike,
|
|
15
15
|
PROCEDURE_REGISTRY,
|
|
16
16
|
REGISTRY_READ_EFFECTS,
|
|
17
|
+
describeRegistry,
|
|
18
|
+
type AnyProcedure,
|
|
19
|
+
type ProcedureEntry,
|
|
20
|
+
type ProcedurePath,
|
|
17
21
|
} from "@procedures/client";
|
|
18
22
|
import { schema } from "../schema.js";
|
|
19
23
|
import type {
|
|
@@ -55,27 +59,14 @@ export const discoveryAnnounceProcedure: Procedure<
|
|
|
55
59
|
effects: REGISTRY_READ_EFFECTS,
|
|
56
60
|
})
|
|
57
61
|
.handler(async (_input: DiscoveryAnnounceInput, ctx?: ProcedureContext) => {
|
|
58
|
-
// The procedures of the registry that serves this
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return !meta?.internal;
|
|
67
|
-
})
|
|
68
|
-
.map((proc) => {
|
|
69
|
-
const meta = proc.metadata as { description?: string } | undefined;
|
|
70
|
-
const info: ProcedureInfo = {
|
|
71
|
-
path: proc.path,
|
|
72
|
-
};
|
|
73
|
-
if (meta?.description !== undefined) {
|
|
74
|
-
info.description = meta.description;
|
|
75
|
-
}
|
|
76
|
-
// TODO: Generate JSON schema from input/output schemas
|
|
77
|
-
return info;
|
|
78
|
-
});
|
|
62
|
+
// The one manifest of the core (roadmap R17): the procedures of the registry that serves this
|
|
63
|
+
// call and that the host exposes, with their JSON Schemas, without the internal ones.
|
|
64
|
+
// Before, the announce gave no schemas and listed the procedures that the host hides.
|
|
65
|
+
const manifest = describeRegistry(ctx?.registry ?? PROCEDURE_REGISTRY, {
|
|
66
|
+
...(ctx?.expose ? { expose: ctx.expose } : {}),
|
|
67
|
+
include: (proc: AnyProcedure) => !(proc.metadata as { internal?: boolean } | undefined)?.internal,
|
|
68
|
+
});
|
|
69
|
+
const procedureInfos: ProcedureInfo[] = manifest.procedures.map(infoOf);
|
|
79
70
|
|
|
80
71
|
// Generate a peer ID based on process (could be configured)
|
|
81
72
|
const peerId = `peer-${process.pid}-${Date.now().toString(36)}`;
|
|
@@ -95,21 +86,21 @@ export const discoveryAnnounceProcedure: Procedure<
|
|
|
95
86
|
/**
|
|
96
87
|
* Get procedure info for all registered procedures of a registry (default: the global registry)
|
|
97
88
|
*/
|
|
98
|
-
export function getProcedureInfos(
|
|
99
|
-
|
|
89
|
+
export function getProcedureInfos(
|
|
90
|
+
registry: ProcedureRegistryLike = PROCEDURE_REGISTRY,
|
|
91
|
+
expose?: (path: ProcedurePath) => boolean
|
|
92
|
+
): ProcedureInfo[] {
|
|
93
|
+
// The one manifest of the core (roadmap R17), without the schemas
|
|
94
|
+
return describeRegistry(registry, { schemas: false, ...(expose ? { expose } : {}) }).procedures.map(infoOf);
|
|
95
|
+
}
|
|
100
96
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (meta?.description !== undefined) {
|
|
109
|
-
info.description = meta.description;
|
|
110
|
-
}
|
|
111
|
-
return info;
|
|
112
|
-
});
|
|
97
|
+
/** The ProcedureInfo of a manifest entry: the form that the peers exchange. */
|
|
98
|
+
export function infoOf(entry: ProcedureEntry): ProcedureInfo {
|
|
99
|
+
const info: ProcedureInfo = { path: [...entry.path] };
|
|
100
|
+
if (entry.description !== undefined) info.description = entry.description;
|
|
101
|
+
if (entry.input) info.inputSchema = entry.input;
|
|
102
|
+
if (entry.output) info.outputSchema = entry.output;
|
|
103
|
+
return info;
|
|
113
104
|
}
|
|
114
105
|
|
|
115
106
|
/**
|
package/src/peer/index.ts
CHANGED
|
@@ -14,12 +14,15 @@ import {
|
|
|
14
14
|
defaultServerUrlStrategy,
|
|
15
15
|
defaultUrlPattern,
|
|
16
16
|
procedureServerOptions,
|
|
17
|
+
hostProcedures,
|
|
18
|
+
metricsInterceptor,
|
|
19
|
+
Client,
|
|
17
20
|
type BrowserGuardOptions,
|
|
18
21
|
type HostPolicy,
|
|
19
22
|
type Method,
|
|
20
23
|
} from "@procedures/client";
|
|
21
24
|
import type { Request } from "express";
|
|
22
|
-
import type { ProcedurePath } from "@procedures/client";
|
|
25
|
+
import type { ProcedurePath, StopOptions } from "@procedures/client";
|
|
23
26
|
import type { HttpTransportConfig, TransportConfig, TransportType, WebSocketTransportConfig } from "../types.js";
|
|
24
27
|
import { bearerToken, hostAllowed, originAllowed, tokenMatches } from "./security.js";
|
|
25
28
|
|
|
@@ -57,6 +60,11 @@ export interface PeerOptions {
|
|
|
57
60
|
registry?: ProcedureRegistry;
|
|
58
61
|
/** The procedures that the peer serves (see `exposeRule`). Without it, the peer serves every procedure. */
|
|
59
62
|
expose?: ((path: ProcedurePath) => boolean) | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The peer counts the calls of each procedure, and `host.metrics` gives the counts (roadmap
|
|
65
|
+
* R19). The default is true.
|
|
66
|
+
*/
|
|
67
|
+
metrics?: boolean | undefined;
|
|
60
68
|
/**
|
|
61
69
|
* The policy of the host (`hostPolicy()` of the core, roadmap 2.3): the expose rule, the call
|
|
62
70
|
* rule, the interceptors (a time limit), the token of each network transport that has none of
|
|
@@ -76,11 +84,19 @@ export interface Peer {
|
|
|
76
84
|
/** Start all transports */
|
|
77
85
|
start(): Promise<void>;
|
|
78
86
|
/** Stop all transports */
|
|
79
|
-
|
|
87
|
+
/** Stop the peer. With `grace`, the calls in flight have that time to end (roadmap R14). */
|
|
88
|
+
stop(options?: StopOptions): Promise<void>;
|
|
80
89
|
/** Get active endpoints */
|
|
81
90
|
getEndpoints(): PeerEndpoint[];
|
|
82
91
|
/** Get underlying server */
|
|
83
92
|
getServer(): ProcedureServer;
|
|
93
|
+
/** The ids of the WebSocket connections of the clients (roadmap R24). */
|
|
94
|
+
connections(): string[];
|
|
95
|
+
/**
|
|
96
|
+
* A client of one connected client (roadmap R24): its calls go to the procedures that the client
|
|
97
|
+
* serves (the `serve` option of its WebSocketTransport), over the same WebSocket.
|
|
98
|
+
*/
|
|
99
|
+
clientFor(connectionId: string): Client;
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
// =============================================================================
|
|
@@ -93,6 +109,10 @@ class PeerImpl implements Peer {
|
|
|
93
109
|
private transports: TransportConfig[];
|
|
94
110
|
private endpoints: PeerEndpoint[] = [];
|
|
95
111
|
private started = false;
|
|
112
|
+
/** The WebSocket transports of the peer: the calls to its clients go over them (R24) */
|
|
113
|
+
private wsTransports: WebSocketServerTransport[] = [];
|
|
114
|
+
/** The HTTP servers that the peer made for its WebSocket transports: `stop()` closes them */
|
|
115
|
+
private ownedServers: Array<import("node:http").Server> = [];
|
|
96
116
|
private registry: ProcedureRegistry;
|
|
97
117
|
private startTime: number = Date.now();
|
|
98
118
|
|
|
@@ -111,13 +131,27 @@ class PeerImpl implements Peer {
|
|
|
111
131
|
this.registry = options.registry ?? PROCEDURE_REGISTRY;
|
|
112
132
|
this.browser = policy?.browser ?? {};
|
|
113
133
|
|
|
114
|
-
// Create procedure server
|
|
134
|
+
// Create procedure server. The metrics interceptor counts each call for host.metrics (R19).
|
|
135
|
+
const metrics = options.metrics === false ? undefined : metricsInterceptor();
|
|
136
|
+
const serverOptions = policy ? procedureServerOptions(policy, this.registry) : {};
|
|
137
|
+
const interceptors = [...(serverOptions.interceptors ?? []), ...(metrics ? [metrics.interceptor] : [])];
|
|
115
138
|
this.server = new ProcedureServer({
|
|
116
|
-
...
|
|
139
|
+
...serverOptions,
|
|
140
|
+
...(interceptors.length > 0 ? { interceptors } : {}),
|
|
117
141
|
autoRegister: options.autoRegister ?? true,
|
|
118
142
|
registry: this.registry,
|
|
119
143
|
...(options.expose ? { expose: options.expose } : {}),
|
|
120
144
|
});
|
|
145
|
+
// host.health, host.info and host.metrics: pinned on the server, not in the registry (R19)
|
|
146
|
+
this.server.registerProcedures(
|
|
147
|
+
hostProcedures({
|
|
148
|
+
id: this.id,
|
|
149
|
+
startedAt: this.startTime,
|
|
150
|
+
server: this.server,
|
|
151
|
+
endpoints: () => this.getEndpoints(),
|
|
152
|
+
...(metrics ? { metrics } : {}),
|
|
153
|
+
})
|
|
154
|
+
);
|
|
121
155
|
}
|
|
122
156
|
|
|
123
157
|
async start(): Promise<void> {
|
|
@@ -160,7 +194,9 @@ class PeerImpl implements Peer {
|
|
|
160
194
|
const express = (await import("express")).default;
|
|
161
195
|
|
|
162
196
|
const app = express();
|
|
163
|
-
|
|
197
|
+
// No body parser here: the transport reads the body itself, with bodyLimit, and accepts each
|
|
198
|
+
// JSON value (roadmap R5). Before, the strict parser of express refused a bare value (a
|
|
199
|
+
// number, a string, null) with 400, and its limit of 100 kB replaced bodyLimit.
|
|
164
200
|
|
|
165
201
|
const port = config.port ?? 3000;
|
|
166
202
|
// Loopback by default so the full procedure registry (incl. shell.run / fs.write) is not
|
|
@@ -227,6 +263,7 @@ class PeerImpl implements Peer {
|
|
|
227
263
|
allowedOrigins: this.browser.allowedOrigins ?? ((origin) => originAllowed(origin, config.corsOrigins)),
|
|
228
264
|
...(this.browser.allowedHosts ? { allowedHosts: this.browser.allowedHosts } : {}),
|
|
229
265
|
allowGet: this.browser.allowGet ?? config.urlStrategy === "rest",
|
|
266
|
+
...(config.bodyLimit !== undefined ? { bodyLimit: config.bodyLimit } : {}),
|
|
230
267
|
});
|
|
231
268
|
|
|
232
269
|
this.server.addTransport(httpTransport);
|
|
@@ -290,6 +327,8 @@ class PeerImpl implements Peer {
|
|
|
290
327
|
httpServer.close();
|
|
291
328
|
throw error;
|
|
292
329
|
}
|
|
330
|
+
this.ownedServers.push(httpServer);
|
|
331
|
+
this.wsTransports.push(wsTransport);
|
|
293
332
|
|
|
294
333
|
this.endpoints.push({
|
|
295
334
|
type: "websocket",
|
|
@@ -297,11 +336,25 @@ class PeerImpl implements Peer {
|
|
|
297
336
|
});
|
|
298
337
|
}
|
|
299
338
|
|
|
300
|
-
async stop(): Promise<void> {
|
|
339
|
+
async stop(options: StopOptions = {}): Promise<void> {
|
|
301
340
|
if (!this.started) return;
|
|
302
|
-
await this.server.stop();
|
|
341
|
+
await this.server.stop(options);
|
|
342
|
+
// The HTTP servers of the WebSocket transports: before, they stayed open, so the port stayed
|
|
343
|
+
// bound after stop() (roadmap R5)
|
|
344
|
+
const owned = this.ownedServers;
|
|
345
|
+
this.ownedServers = [];
|
|
346
|
+
await Promise.all(
|
|
347
|
+
owned.map(
|
|
348
|
+
(httpServer) =>
|
|
349
|
+
new Promise<void>((resolve) => {
|
|
350
|
+
httpServer.close(() => resolve());
|
|
351
|
+
httpServer.closeAllConnections();
|
|
352
|
+
}),
|
|
353
|
+
),
|
|
354
|
+
);
|
|
303
355
|
this.started = false;
|
|
304
356
|
this.endpoints = [];
|
|
357
|
+
this.wsTransports = [];
|
|
305
358
|
}
|
|
306
359
|
|
|
307
360
|
getEndpoints(): PeerEndpoint[] {
|
|
@@ -311,6 +364,16 @@ class PeerImpl implements Peer {
|
|
|
311
364
|
getServer(): ProcedureServer {
|
|
312
365
|
return this.server;
|
|
313
366
|
}
|
|
367
|
+
|
|
368
|
+
connections(): string[] {
|
|
369
|
+
return this.wsTransports.flatMap((transport) => transport.getTrackedConnections().map((connection) => connection.id));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
clientFor(connectionId: string): Client {
|
|
373
|
+
const transport = this.wsTransports.find((ws) => ws.getTrackedConnection(connectionId) !== undefined);
|
|
374
|
+
if (!transport) throw new Error(`Connection not found: ${connectionId}`);
|
|
375
|
+
return new Client({ transport: transport.transportFor(connectionId), throwOnError: true });
|
|
376
|
+
}
|
|
314
377
|
}
|
|
315
378
|
|
|
316
379
|
// =============================================================================
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Generates procedure manifests in various formats
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createProcedure,
|
|
6
|
+
import { createProcedure, describeRegistry, type AnyProcedure, type Effects, type Procedure, type ProcedureContext, PROCEDURE_REGISTRY } from "@procedures/client";
|
|
7
|
+
import { infoOf } from "../discovery/index.js";
|
|
7
8
|
import { wrapSchema } from "../schema.js";
|
|
8
9
|
import { ManifestGenerateOutputSchema } from "../output-schemas.js";
|
|
9
10
|
import { manifestGenerateInputSchema } from "../input-schemas.js";
|
|
@@ -30,39 +31,15 @@ export const manifestGenerateProcedure: Procedure<
|
|
|
30
31
|
effects: { reads: ["registry"], writes: ["fs"], idempotent: true },
|
|
31
32
|
})
|
|
32
33
|
.handler(async (input: ManifestGenerateInput, ctx?: ProcedureContext) => {
|
|
33
|
-
// The
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// Check if procedure path starts with namespace
|
|
41
|
-
for (let i = 0; i < input.namespace!.length; i++) {
|
|
42
|
-
if (proc.path[i] !== input.namespace![i]) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return true;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Convert to ProcedureInfo format
|
|
51
|
-
const procedureInfos: ProcedureInfo[] = procedures.map((proc) => {
|
|
52
|
-
const meta = proc.metadata as { description?: string; internal?: boolean } | undefined;
|
|
53
|
-
const info: ProcedureInfo = {
|
|
54
|
-
path: proc.path,
|
|
55
|
-
};
|
|
56
|
-
if (meta?.description !== undefined) {
|
|
57
|
-
info.description = meta.description;
|
|
58
|
-
}
|
|
59
|
-
// The JSON Schemas of the input and the output, when the schemas can describe themselves
|
|
60
|
-
const inputSchema = jsonSchemaOf(proc.input, { io: "input" });
|
|
61
|
-
const outputSchema = jsonSchemaOf(proc.output, { io: "output" });
|
|
62
|
-
if (inputSchema) info.inputSchema = inputSchema;
|
|
63
|
-
if (outputSchema) info.outputSchema = outputSchema;
|
|
64
|
-
return info;
|
|
34
|
+
// The one manifest of the core (roadmap R17): the procedures that the host exposes, with
|
|
35
|
+
// their JSON Schemas, under the namespace when the input gives one
|
|
36
|
+
const namespace = input.namespace ?? [];
|
|
37
|
+
const manifest = describeRegistry(ctx?.registry ?? PROCEDURE_REGISTRY, {
|
|
38
|
+
...(ctx?.expose ? { expose: ctx.expose } : {}),
|
|
39
|
+
withoutHandler: true,
|
|
40
|
+
include: (proc: AnyProcedure) => namespace.every((segment, index) => proc.path[index] === segment),
|
|
65
41
|
});
|
|
42
|
+
const procedureInfos: ProcedureInfo[] = manifest.procedures.map(infoOf);
|
|
66
43
|
|
|
67
44
|
// Determine formats to generate
|
|
68
45
|
const formats = input.formats ?? ["json"];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Call a procedure on a remote peer through an outbound connection
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createProcedure, pathToMethod, type Effects, type Procedure } from "@procedures/client";
|
|
6
|
+
import { TIMEOUT_METADATA_KEY, createProcedure, pathToMethod, type Effects, type Procedure, type ProcedureContext } from "@procedures/client";
|
|
7
7
|
import { wrapSchema } from "../schema.js";
|
|
8
8
|
import { ServerCallOutputSchema } from "../output-schemas.js";
|
|
9
9
|
import { serverCallInputSchema } from "../input-schemas.js";
|
|
@@ -55,7 +55,7 @@ export const serverCallProcedure: Procedure<
|
|
|
55
55
|
shorts: { connectionId: "c", address: "a" },
|
|
56
56
|
output: "json",
|
|
57
57
|
})
|
|
58
|
-
.handler(async (input: ServerCallInput): Promise<ServerCallOutput> => {
|
|
58
|
+
.handler(async (input: ServerCallInput, ctx?: ProcedureContext): Promise<ServerCallOutput> => {
|
|
59
59
|
// Normalize path: accept dot-separated string or array
|
|
60
60
|
const pathArray = typeof input.path === "string"
|
|
61
61
|
? input.path.split(".")
|
|
@@ -87,7 +87,12 @@ export const serverCallProcedure: Procedure<
|
|
|
87
87
|
// (deep dive architecture review 3.3)
|
|
88
88
|
const method = pathToMethod(pathArray);
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
// The signal and the remaining time of this call go with the relayed call (roadmap R14)
|
|
91
|
+
const deadline = ctx?.capability?.deadline;
|
|
92
|
+
const result = await client.call(method, input.input ?? {}, {
|
|
93
|
+
...(ctx?.signal ? { signal: ctx.signal } : {}),
|
|
94
|
+
...(deadline !== undefined ? { context: { [TIMEOUT_METADATA_KEY]: Math.max(0, deadline - Date.now()) } } : {}),
|
|
95
|
+
});
|
|
91
96
|
|
|
92
97
|
return {
|
|
93
98
|
result,
|
|
@@ -9,6 +9,7 @@ import { ServerConnectOutputSchema } from "../output-schemas.js";
|
|
|
9
9
|
import { serverConnectInputSchema } from "../input-schemas.js";
|
|
10
10
|
import type { ServerConnectInput, ServerConnectOutput } from "../types.js";
|
|
11
11
|
import { connect } from "../connection/index.js";
|
|
12
|
+
import { ms } from "@procedures/client";
|
|
12
13
|
|
|
13
14
|
// Use pass-through schemas
|
|
14
15
|
const serverConnectOutputSchema = wrapSchema<ServerConnectOutput>(ServerConnectOutputSchema);
|
|
@@ -35,7 +36,8 @@ export const serverConnectProcedure: Procedure<
|
|
|
35
36
|
connectOptions.transport = input.transport;
|
|
36
37
|
}
|
|
37
38
|
if (input.timeout !== undefined) {
|
|
38
|
-
|
|
39
|
+
// The input is data: a number of milliseconds
|
|
40
|
+
connectOptions.timeout = ms(input.timeout);
|
|
39
41
|
}
|
|
40
42
|
const connection = await connect(connectOptions);
|
|
41
43
|
|
package/src/types.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface HttpTransportConfig {
|
|
|
21
21
|
urlStrategy?: "rest" | "rpc" | undefined;
|
|
22
22
|
/** A secret that every request must send as `Authorization: Bearer <token>` */
|
|
23
23
|
token?: string | undefined;
|
|
24
|
+
/** The largest request body in bytes. The default is 1 MiB. */
|
|
25
|
+
bodyLimit?: number | undefined;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export interface WebSocketTransportConfig {
|