@procedures/client 0.4.0 → 0.5.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 +39 -0
- package/README.md +46 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/transport.d.ts +2 -1
- package/dist/adapters/http/client/transport.d.ts.map +1 -1
- package/dist/adapters/http/client/transport.js +9 -2
- package/dist/adapters/http/client/transport.js.map +1 -1
- package/dist/adapters/http/server/transport.d.ts +3 -1
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +6 -3
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/local/client/transport.d.ts +2 -1
- package/dist/adapters/local/client/transport.d.ts.map +1 -1
- package/dist/adapters/local/client/transport.js +8 -2
- package/dist/adapters/local/client/transport.js.map +1 -1
- package/dist/adapters/local/client/types.d.ts +6 -0
- package/dist/adapters/local/client/types.d.ts.map +1 -1
- package/dist/adapters/local/client/types.js.map +1 -1
- package/dist/adapters/sequence.d.ts +36 -0
- package/dist/adapters/sequence.d.ts.map +1 -0
- package/dist/adapters/sequence.js +55 -0
- package/dist/adapters/sequence.js.map +1 -0
- package/dist/adapters/websocket/client/transport.d.ts +39 -1
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +102 -7
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/adapters/websocket/client/types.d.ts +12 -3
- package/dist/adapters/websocket/client/types.d.ts.map +1 -1
- package/dist/adapters/websocket/client/types.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts +22 -1
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +29 -1
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/adapters/websocket/server/types.d.ts +6 -0
- package/dist/adapters/websocket/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.d.ts +4 -3
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.js +18 -9
- package/dist/adapters/websocket/shared/rpc.js.map +1 -1
- package/dist/browser.d.ts +3 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -0
- package/dist/browser.js.map +1 -1
- package/dist/client/errors/registry.d.ts.map +1 -1
- package/dist/client/errors/registry.js +1 -0
- package/dist/client/errors/registry.js.map +1 -1
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +3 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/publisher.d.ts +95 -0
- package/dist/events/publisher.d.ts.map +1 -0
- package/dist/events/publisher.js +268 -0
- package/dist/events/publisher.js.map +1 -0
- package/dist/events/remote.d.ts +164 -0
- package/dist/events/remote.d.ts.map +1 -0
- package/dist/events/remote.js +250 -0
- package/dist/events/remote.js.map +1 -0
- package/dist/procedures/collection/index.d.ts +1 -1
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js.map +1 -1
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +6 -1
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/collection/schemas.d.ts +16 -1
- package/dist/procedures/collection/schemas.d.ts.map +1 -1
- package/dist/procedures/collection/schemas.js +13 -2
- package/dist/procedures/collection/schemas.js.map +1 -1
- package/dist/procedures/collection/storage.d.ts +14 -2
- package/dist/procedures/collection/storage.d.ts.map +1 -1
- package/dist/procedures/invoke.d.ts +4 -2
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +1 -1
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/protocol.d.ts +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +1 -0
- package/dist/protocol.js.map +1 -1
- package/dist/server/host-policy.d.ts +11 -0
- package/dist/server/host-policy.d.ts.map +1 -1
- package/dist/server/host-policy.js +5 -0
- package/dist/server/host-policy.js.map +1 -1
- package/dist/server/procedure-server.d.ts +24 -2
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +34 -4
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/http/client/transport.ts +9 -2
- package/src/adapters/http/server/transport.ts +6 -3
- package/src/adapters/local/client/transport.ts +8 -2
- package/src/adapters/local/client/types.ts +7 -0
- package/src/adapters/sequence.ts +55 -0
- package/src/adapters/websocket/client/transport.ts +108 -7
- package/src/adapters/websocket/client/types.ts +13 -3
- package/src/adapters/websocket/server/transport.ts +43 -1
- package/src/adapters/websocket/server/types.ts +7 -0
- package/src/adapters/websocket/shared/rpc.ts +19 -9
- package/src/browser.ts +15 -0
- package/src/client/errors/registry.ts +1 -0
- package/src/events/index.ts +16 -0
- package/src/events/publisher.ts +314 -0
- package/src/events/remote.ts +369 -0
- package/src/procedures/collection/index.ts +1 -0
- package/src/procedures/collection/procedures.ts +6 -1
- package/src/procedures/collection/schemas.ts +28 -2
- package/src/procedures/collection/storage.ts +14 -2
- package/src/procedures/invoke.ts +4 -2
- package/src/protocol.ts +3 -1
- package/src/server/host-policy.ts +14 -0
- package/src/server/procedure-server.ts +41 -6
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
deleteBatchInputSchema,
|
|
34
34
|
deleteBatchOutputSchema,
|
|
35
35
|
type GetInput,
|
|
36
|
+
type GetFoundOutput,
|
|
36
37
|
type SetInput,
|
|
37
38
|
type DeleteInput,
|
|
38
39
|
type HasInput,
|
|
@@ -107,7 +108,11 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
107
108
|
},
|
|
108
109
|
handler: async (input: GetInput, ctx : ProcedureContext) => {
|
|
109
110
|
const storage = getStorage(ctx);
|
|
110
|
-
|
|
111
|
+
const value = await storage.get(input.id);
|
|
112
|
+
if (input.found !== true) return value;
|
|
113
|
+
// The output with `found` keeps a stored null apart from a missing id, in one call
|
|
114
|
+
const output: GetFoundOutput = value === undefined ? { found: false } : { found: true, value };
|
|
115
|
+
return output;
|
|
111
116
|
},
|
|
112
117
|
}),
|
|
113
118
|
|
|
@@ -117,6 +117,12 @@ export const valueSchema : ZodLike<unknown> = createSchema<unknown>((data) => {
|
|
|
117
117
|
*/
|
|
118
118
|
export interface GetInput {
|
|
119
119
|
id: string;
|
|
120
|
+
/**
|
|
121
|
+
* True: the output is `{ found, value }` (a `GetFoundOutput`), so a stored `null` and a missing
|
|
122
|
+
* id give different outputs. Without it, the output is the item, or `null` on the wire for a
|
|
123
|
+
* missing id. `ApiStorage` of `@procedures/store` sends it.
|
|
124
|
+
*/
|
|
125
|
+
found?: boolean;
|
|
120
126
|
}
|
|
121
127
|
|
|
122
128
|
export const getInputSchema : ZodLike<GetInput> = createSchema<GetInput>((data) => {
|
|
@@ -138,11 +144,31 @@ export const getInputSchema : ZodLike<GetInput> = createSchema<GetInput>((data)
|
|
|
138
144
|
},
|
|
139
145
|
};
|
|
140
146
|
}
|
|
141
|
-
|
|
147
|
+
const found = data["found"];
|
|
148
|
+
if (found !== undefined && typeof found !== "boolean") {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: {
|
|
152
|
+
message: "found must be a boolean",
|
|
153
|
+
errors: [{ path: ["found"], message: "found must be a boolean" }],
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return { success: true, data: found === true ? { id: data["id"], found: true } : { id: data["id"] } };
|
|
142
158
|
});
|
|
143
159
|
|
|
144
160
|
/**
|
|
145
|
-
*
|
|
161
|
+
* The output of `get` with `found: true` in the input. `found` is true when the collection has
|
|
162
|
+
* the id. Then `value` is the item, also when the item is `null`.
|
|
163
|
+
*/
|
|
164
|
+
export interface GetFoundOutput {
|
|
165
|
+
found: boolean;
|
|
166
|
+
value?: unknown;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Output for get operation: the item, or undefined for a missing id. With `found: true` in the
|
|
171
|
+
* input, the output is a `GetFoundOutput`.
|
|
146
172
|
*/
|
|
147
173
|
export const getOutputSchema : ZodLike<unknown> = createSchema<unknown>((data) => {
|
|
148
174
|
// Accept any value (item) or undefined
|
|
@@ -9,9 +9,16 @@
|
|
|
9
9
|
* the collections package (roadmap 3.3).
|
|
10
10
|
*
|
|
11
11
|
* Each method gives a Promise: a caller cannot tell a local storage from a remote one.
|
|
12
|
+
*
|
|
13
|
+
* The contract (the suite of `@procedures/collections/testing` tests it for each storage):
|
|
14
|
+
* - A storage keeps a copy of each value that a write takes, and each read gives a new copy.
|
|
15
|
+
* - A stored `null` reads back as `null`. A missing id reads back as `undefined`.
|
|
16
|
+
* - A delta of `getChangesSince` follows the order of the last change of each id.
|
|
17
|
+
* - After `close()`, each operation rejects with an error whose message says "closed". A second
|
|
18
|
+
* `close()` does nothing.
|
|
12
19
|
*/
|
|
13
20
|
export interface CollectionStorage<T> {
|
|
14
|
-
/** The item with this id, or undefined. */
|
|
21
|
+
/** The item with this id, or undefined. A stored null is null. */
|
|
15
22
|
get(id: string): Promise<T | undefined>;
|
|
16
23
|
/** Every item of the collection. */
|
|
17
24
|
getAll(): Promise<T[]>;
|
|
@@ -50,7 +57,10 @@ export interface CollectionStorage<T> {
|
|
|
50
57
|
* that keeps no writes does not have this method. `ProcedureServer.useStorage` calls it.
|
|
51
58
|
*/
|
|
52
59
|
flush?(): Promise<void>;
|
|
53
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* This method releases the resources of the storage. After it, each operation rejects. A second
|
|
62
|
+
* `close()` does nothing.
|
|
63
|
+
*/
|
|
54
64
|
close(): Promise<void>;
|
|
55
65
|
/** The kind, the size and the statistics of the storage. */
|
|
56
66
|
getMetadata?(): Promise<StorageMetadata>;
|
|
@@ -65,6 +75,8 @@ export interface CollectionStorage<T> {
|
|
|
65
75
|
* the first call, a version of an older run, or a change log that dropped old deletes.
|
|
66
76
|
* - When `full` is false, `changed` has the items that changed after the version, and `deleted`
|
|
67
77
|
* has the ids that were deleted after it.
|
|
78
|
+
* - The order of a full result is the order of the entries. The order of a delta is the order of
|
|
79
|
+
* the last change of each id.
|
|
68
80
|
*/
|
|
69
81
|
export interface StorageChanges<T> {
|
|
70
82
|
/** The version of the storage that includes these changes. */
|
package/src/procedures/invoke.ts
CHANGED
|
@@ -68,8 +68,10 @@ export type InvocationErrorCode =
|
|
|
68
68
|
* of `@procedures/fs`).
|
|
69
69
|
* - `host-not-allowed`: the input has a URL whose host the host does not allow, or a redirect goes
|
|
70
70
|
* to such a host (`createHttpModule({ allow })` of `@procedures/http`).
|
|
71
|
+
* - `topic-not-allowed`: the `allow` function of an `EventPublisher` refuses a topic of a
|
|
72
|
+
* subscription (`documentation/EVENTS.md`).
|
|
71
73
|
*/
|
|
72
|
-
export type ForbiddenReason = CapabilityRefusalReason | "outside-root" | "host-not-allowed";
|
|
74
|
+
export type ForbiddenReason = CapabilityRefusalReason | "outside-root" | "host-not-allowed" | "topic-not-allowed";
|
|
73
75
|
|
|
74
76
|
/** An error of the invocation itself. An error that the handler throws passes through unchanged. */
|
|
75
77
|
export class InvocationError extends Error {
|
|
@@ -79,7 +81,7 @@ export class InvocationError extends Error {
|
|
|
79
81
|
readonly retryable: boolean = false;
|
|
80
82
|
/**
|
|
81
83
|
* The reason of a `FORBIDDEN` error: `effects-undeclared`, `effects-exceeded`, `clearance`,
|
|
82
|
-
* `deadline`, `outside-root` or `
|
|
84
|
+
* `deadline`, `outside-root`, `host-not-allowed` or `topic-not-allowed`.
|
|
83
85
|
*/
|
|
84
86
|
readonly reason: ForbiddenReason | undefined;
|
|
85
87
|
|
package/src/protocol.ts
CHANGED
|
@@ -46,7 +46,8 @@ export type ProtocolErrorCode =
|
|
|
46
46
|
| "TIMEOUT"
|
|
47
47
|
| "UNAVAILABLE"
|
|
48
48
|
| "CONNECTION_CLOSED"
|
|
49
|
-
| "CONNECTION_NOT_FOUND"
|
|
49
|
+
| "CONNECTION_NOT_FOUND"
|
|
50
|
+
| "TOPIC_NOT_FOUND";
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* The error codes of the protocol, on each face (HTTP, WebSocket, MCP, local). An error that a
|
|
@@ -76,6 +77,7 @@ export const ERROR_CODES: Readonly<Record<ProtocolErrorCode, ErrorCodeInfo>> = {
|
|
|
76
77
|
UNAVAILABLE: { http: 503, retryable: true, meaning: "The server cannot take the call now: it stops, or it is busy" },
|
|
77
78
|
CONNECTION_CLOSED: { http: 503, retryable: true, meaning: "The connection closed before the call left" },
|
|
78
79
|
CONNECTION_NOT_FOUND: { http: 404, retryable: false, meaning: "The server has no connection with this id" },
|
|
80
|
+
TOPIC_NOT_FOUND: { http: 404, retryable: false, meaning: "The topic rule of the host has no topic with this name" },
|
|
79
81
|
};
|
|
80
82
|
|
|
81
83
|
/** True when a code is an error code of the protocol. */
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* - `token`, `browser`: the checks of the network transports (`client-server` applies them).
|
|
15
15
|
* - `capability`, `readOnly`, `clearanceOrder`, `resources`, `admitItem`: the capability of each
|
|
16
16
|
* call and what the host gives for it (CAPABILITIES-2026-10).
|
|
17
|
+
* - `events`: the remote events of the host (`documentation/EVENTS.md`). The server pins
|
|
18
|
+
* `events.subscribe` with this topic rule, and its procedures get the bus as `ctx.bus`.
|
|
17
19
|
*/
|
|
18
20
|
|
|
19
21
|
import type { Capability, ClearanceOrder, ProcedurePath, ResourcesOf } from "../procedures/types.js";
|
|
@@ -24,6 +26,7 @@ import { timeoutInterceptor } from "../procedures/interceptors.js";
|
|
|
24
26
|
import type { BrowserGuardOptions } from "./browser-guard.js";
|
|
25
27
|
import type { ProcedureRegistry } from "../procedures/registry.js";
|
|
26
28
|
import type { ProcedureServerOptions } from "./procedure-server.js";
|
|
29
|
+
import type { EventPublisherOptions } from "../events/publisher.js";
|
|
27
30
|
import { toMs, type Duration } from "../duration.js";
|
|
28
31
|
|
|
29
32
|
/** The procedures of a host that are tools: namespaces (first path segments), less some keys. */
|
|
@@ -86,6 +89,12 @@ export interface HostPolicyOptions {
|
|
|
86
89
|
resources?: ((invocation: Invocation) => ResourcesOf) | undefined;
|
|
87
90
|
/** The filter of the output that leaves the host. */
|
|
88
91
|
admitItem?: AdmitItem | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* The remote events of the host (`documentation/EVENTS.md`): the topic rule, with an optional
|
|
94
|
+
* bus, `allow` function and buffer. The default bus is the global bus, which is also the default
|
|
95
|
+
* `ctx.bus`. Without this option, the host does not serve `events.subscribe`.
|
|
96
|
+
*/
|
|
97
|
+
events?: EventPublisherOptions | undefined;
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
/** What a host lets its callers do. */
|
|
@@ -100,6 +109,8 @@ export interface HostPolicy {
|
|
|
100
109
|
readonly clearanceOrder: ClearanceOrder | undefined;
|
|
101
110
|
readonly resources: ((invocation: Invocation) => ResourcesOf) | undefined;
|
|
102
111
|
readonly admitItem: AdmitItem | undefined;
|
|
112
|
+
/** The remote events: each server of the policy makes its own publisher from these options. */
|
|
113
|
+
readonly events: EventPublisherOptions | undefined;
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
/** The capability function of a policy: the capability of the options, narrowed by `readOnly`. */
|
|
@@ -145,6 +156,7 @@ export function hostPolicy(options: HostPolicyOptions = {}): HostPolicy {
|
|
|
145
156
|
clearanceOrder: options.clearanceOrder,
|
|
146
157
|
resources: options.resources,
|
|
147
158
|
admitItem: options.admitItem,
|
|
159
|
+
events: options.events,
|
|
148
160
|
};
|
|
149
161
|
}
|
|
150
162
|
|
|
@@ -162,5 +174,7 @@ export function procedureServerOptions(policy: HostPolicy, registry?: ProcedureR
|
|
|
162
174
|
...(policy.clearanceOrder ? { clearanceOrder: policy.clearanceOrder } : {}),
|
|
163
175
|
...(policy.resources ? { resources: policy.resources } : {}),
|
|
164
176
|
...(policy.admitItem ? { admitItem: policy.admitItem } : {}),
|
|
177
|
+
// The remote events: the server makes its publisher and pins events.subscribe (EVENTS.md)
|
|
178
|
+
...(policy.events ? { events: policy.events } : {}),
|
|
165
179
|
};
|
|
166
180
|
}
|
|
@@ -25,6 +25,8 @@ import { optionalMs, type Duration } from "../duration.js";
|
|
|
25
25
|
import type { CollectionStorage } from "../procedures/collection/storage.js";
|
|
26
26
|
import { TIMER_DEFAULTS } from "../timers.js";
|
|
27
27
|
import { errorCodeOf } from "../protocol.js";
|
|
28
|
+
import { EventPublisher, type EventPublisherOptions } from "../events/publisher.js";
|
|
29
|
+
import { getGlobalEventBus } from "../events/bus.js";
|
|
28
30
|
|
|
29
31
|
// =============================================================================
|
|
30
32
|
// Types
|
|
@@ -119,6 +121,14 @@ export interface ProcedureServerOptions extends ServerOptions {
|
|
|
119
121
|
* `request.principal`.
|
|
120
122
|
*/
|
|
121
123
|
authenticate?: ((request: ServerRequest<unknown>) => Principal | undefined | Promise<Principal | undefined>) | undefined;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The remote events of the server (`documentation/EVENTS.md`): a bus and its topic rule, or an
|
|
127
|
+
* `EventPublisher`. The server pins the procedure `events.subscribe`, outside its registry, so a
|
|
128
|
+
* peer can subscribe to the topics. The procedures of the server get this bus as `ctx.bus`.
|
|
129
|
+
* `stop()` ends each subscription first, with `UNAVAILABLE`.
|
|
130
|
+
*/
|
|
131
|
+
events?: EventPublisherOptions | EventPublisher | undefined;
|
|
122
132
|
}
|
|
123
133
|
|
|
124
134
|
/**
|
|
@@ -288,6 +298,8 @@ export class ProcedureServer extends Server implements RepositoryProvider {
|
|
|
288
298
|
private readonly authenticate: ProcedureServerOptions["authenticate"];
|
|
289
299
|
/** The functions that `stop()` waits on: each one runs when no call is in flight */
|
|
290
300
|
private idleWaiters: Array<() => void> = [];
|
|
301
|
+
/** The publisher of the remote events, from the option `events`, or undefined. */
|
|
302
|
+
readonly publisher: EventPublisher | undefined;
|
|
291
303
|
|
|
292
304
|
constructor(options: ProcedureServerOptions = {}) {
|
|
293
305
|
super(options);
|
|
@@ -305,6 +317,9 @@ export class ProcedureServer extends Server implements RepositoryProvider {
|
|
|
305
317
|
this.idempotency = options.idempotency === false ? undefined : new IdempotencyCache(options.idempotency ?? {});
|
|
306
318
|
this.token = options.token;
|
|
307
319
|
this.authenticate = options.authenticate;
|
|
320
|
+
// The remote events: the server pins events.subscribe, as it pins host.health (EVENTS.md)
|
|
321
|
+
this.publisher = options.events instanceof EventPublisher ? options.events : options.events ? new EventPublisher(options.events) : undefined;
|
|
322
|
+
if (this.publisher) this.registerProcedure(this.publisher.procedure);
|
|
308
323
|
|
|
309
324
|
// Configure collections
|
|
310
325
|
if (options.collections) {
|
|
@@ -645,6 +660,8 @@ export class ProcedureServer extends Server implements RepositoryProvider {
|
|
|
645
660
|
clearanceOrder,
|
|
646
661
|
resources,
|
|
647
662
|
admitItem,
|
|
663
|
+
// The bus of the remote events is the bus of the procedures (EVENTS.md)
|
|
664
|
+
...(this.publisher ? { bus: this.publisher.bus } : {}),
|
|
648
665
|
});
|
|
649
666
|
if (output.kind === "stream") {
|
|
650
667
|
// The faces that collect a stream read this mark (roadmap R15)
|
|
@@ -723,23 +740,41 @@ export class ProcedureServer extends Server implements RepositoryProvider {
|
|
|
723
740
|
}
|
|
724
741
|
|
|
725
742
|
/**
|
|
726
|
-
*
|
|
743
|
+
* This function publishes an event on the bus of the server: the bus of the option `events`, or
|
|
744
|
+
* the global bus. Each subscription of the topic gets it (`documentation/EVENTS.md`).
|
|
745
|
+
*
|
|
746
|
+
* @param topic - The topic: a channel of the bus
|
|
747
|
+
* @param data - The data of the event. It crosses the wire as JSON.
|
|
748
|
+
*/
|
|
749
|
+
publish<T>(topic: string, data: T): void {
|
|
750
|
+
(this.publisher?.bus ?? getGlobalEventBus()).emit(topic, data);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* This method closes each storage one time, also a storage of two collections, and removes the
|
|
755
|
+
* storages from the server. A closed storage rejects each operation, so the server does not keep
|
|
756
|
+
* it. Before a new `start()`, give the storages again with `useStorage()`.
|
|
727
757
|
*/
|
|
728
758
|
async closeStorages(): Promise<void> {
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
);
|
|
732
|
-
|
|
759
|
+
const storages = new Set(this.storages.values());
|
|
760
|
+
this.storages.clear();
|
|
761
|
+
const results = await Promise.allSettled(Array.from(storages, (storage) => storage.close()));
|
|
762
|
+
const failed = results.find((result): result is PromiseRejectedResult => result.status === "rejected");
|
|
763
|
+
if (failed) throw failed.reason;
|
|
733
764
|
}
|
|
734
765
|
|
|
735
766
|
/**
|
|
736
|
-
*
|
|
767
|
+
* This method stops the server, then closes the storages and removes them (`closeStorages()`).
|
|
768
|
+
* The server can start again, with new storages from `useStorage()`. Before, a server that
|
|
769
|
+
* started again used the closed storages.
|
|
737
770
|
*/
|
|
738
771
|
override async stop(options: StopOptions = {}): Promise<void> {
|
|
739
772
|
const graceMs = optionalMs(options.grace, "grace") ?? TIMER_DEFAULTS.server.stopGraceMs;
|
|
740
773
|
// A graceful stop: new calls get UNAVAILABLE, and the calls in flight have the grace time
|
|
741
774
|
// to end (roadmap R14). Before, the transports aborted every call at once.
|
|
742
775
|
this.draining = true;
|
|
776
|
+
// A subscription has no end: it ends now, with a retryable UNAVAILABLE (EVENTS.md)
|
|
777
|
+
this.publisher?.end();
|
|
743
778
|
if (graceMs > 0 && this.inFlight > 0) {
|
|
744
779
|
await new Promise<void>((resolve) => {
|
|
745
780
|
const timer = setTimeout(resolve, graceMs);
|