@procedures/peer 0.10.0 → 0.12.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/src/index.ts CHANGED
@@ -26,8 +26,44 @@ export type {
26
26
  ProcedureUpdateMessage,
27
27
  } from "./types.js";
28
28
 
29
- // Peer
30
- export { createPeer, type Peer, type PeerOptions, type PeerEndpoint } from "./peer/index.js";
29
+ // The procedures host (CLI-7): the parts of a service, and its faces
30
+ export {
31
+ createProcedureHost,
32
+ httpFace,
33
+ wsFace,
34
+ LOCAL_LISTENER_KNOBS,
35
+ callOperations,
36
+ faceHandler,
37
+ operationOfCall,
38
+ } from "./host/index.js";
39
+ export type {
40
+ ProcedureHost,
41
+ ProcedureHostOptions,
42
+ ProcedureHostFaces,
43
+ HostListener,
44
+ HostCollection,
45
+ HostServerOptions,
46
+ HostPart,
47
+ HostDependency,
48
+ HostCheck,
49
+ HostPattern,
50
+ HostController,
51
+ HostEndpoint,
52
+ HostInfo,
53
+ HostKnobs,
54
+ HostFace,
55
+ HostFaceContext,
56
+ HostFaceEndpoint,
57
+ HostFaceFactory,
58
+ HostFaceUpgrade,
59
+ CallDetail,
60
+ CallOperation,
61
+ FaceHandler,
62
+ FaceHandlerOptions,
63
+ } from "./host/index.js";
64
+
65
+ // Peer: a procedures host with a service of its own, for a process that has no service
66
+ export { createPeer, peerHostOptions, PEER_PROFILE, type Peer, type PeerOptions, type PeerEndpoint } from "./peer/index.js";
31
67
  export { exposeRule } from "./expose.js";
32
68
 
33
69
  // Procedures
package/src/lockfile.ts CHANGED
@@ -5,7 +5,8 @@
5
5
  * the build of `mark` and its peer id. Before, five copies of this logic had drifted, and a
6
6
  * server counted as alive when its PID existed: Windows reuses PIDs, so a stale lockfile broke
7
7
  * every `mark` command and `server stop` killed an unrelated process (deep dive CLI-4). Now a
8
- * server is alive only when its health endpoint answers with the peer id of its lockfile.
8
+ * server is alive only when its procedure `host.info` answers the token of its lockfile with the
9
+ * peer id of its lockfile.
9
10
  */
10
11
 
11
12
  import * as fs from "node:fs";
@@ -53,6 +54,30 @@ export function writeLockfile(data: LockfileData): void {
53
54
  fs.writeFileSync(lockfilePath(data.port), JSON.stringify(data, null, 2), { mode: 0o600 });
54
55
  }
55
56
 
57
+ /** A lockfile as a dependency of a service: the shape of `DependencyHandle` of `@mark1russell7/service`. */
58
+ export interface LockfileHandle {
59
+ /** This method writes the lockfile. */
60
+ connect(): void;
61
+ /** This method removes the lockfile, when it is still the lockfile of this host. */
62
+ close(): void;
63
+ }
64
+
65
+ /**
66
+ * This function gives the lockfile of a host as a dependency of its service (CLI-8). The lifecycle
67
+ * connects the dependencies before the listeners start, and it closes them after the listeners
68
+ * close. Thus the stages of `run()` own the lockfile, and the entry has no signal handler for it.
69
+ * The close leaves a lockfile of another peer on the same port.
70
+ */
71
+ export function lockfileHandle(data: LockfileData): LockfileHandle {
72
+ return {
73
+ connect: () => writeLockfile(data),
74
+ close: () => {
75
+ const current = readLockfileForPort(data.port);
76
+ if (current === null || current.peerId === data.peerId) removeLockfileForPort(data.port);
77
+ },
78
+ };
79
+ }
80
+
56
81
  export function readLockfileForPort(port: number): LockfileData | null {
57
82
  try {
58
83
  return JSON.parse(fs.readFileSync(lockfilePath(port), "utf-8")) as LockfileData;
@@ -95,21 +120,28 @@ export function removeLockfileForPort(port: number): void {
95
120
  }
96
121
 
97
122
  /**
98
- * The state of a lockfile's server:
99
- * - "alive": the health endpoint answers with the lockfile's peer id;
123
+ * The state of a lockfile's server, from the procedure `host.info` with the token of the lockfile
124
+ * (CLI-7, CLI-9). Before, the server answered a `/health` route without the token, and that route said
125
+ * "ok" also while the server stopped.
126
+ * - "alive": `host.info` answers with the lockfile's peer id;
100
127
  * - "dead": nothing answers (the lockfile is stale);
101
- * - "other": something else answers on the port (another server, or a lockfile without a peer id).
128
+ * - "other": something else answers on the port (another server, a refused token, or a lockfile without a peer id).
102
129
  */
103
130
  export async function checkServer(lockfile: LockfileData, timeoutMs = 1000): Promise<"alive" | "dead" | "other"> {
104
131
  let response: Response;
105
132
  try {
106
- response = await fetch(`${lockfile.endpoint}/health`, { signal: AbortSignal.timeout(timeoutMs) });
133
+ response = await fetch(`${lockfile.endpoint}/host/info`, {
134
+ method: "POST",
135
+ headers: { "content-type": "application/json", ...(lockfile.token !== undefined ? { authorization: `Bearer ${lockfile.token}` } : {}) },
136
+ body: "{}",
137
+ signal: AbortSignal.timeout(timeoutMs),
138
+ });
107
139
  } catch {
108
140
  return "dead";
109
141
  }
110
142
  try {
111
- const health = (await response.json()) as { peerId?: unknown };
112
- return lockfile.peerId !== undefined && health.peerId === lockfile.peerId ? "alive" : "other";
143
+ const info = (await response.json()) as { peerId?: unknown };
144
+ return response.ok && lockfile.peerId !== undefined && info.peerId === lockfile.peerId ? "alive" : "other";
113
145
  } catch {
114
146
  return "other";
115
147
  }