@molecule/api-instance-leadership 0.0.1-stage → 1.1.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/README.md CHANGED
@@ -1,11 +1,294 @@
1
+ <!--
2
+ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
+ Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
+ Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
+ To change this document, edit the module-level JSDoc in src/index.ts.
6
+ Generated: 2026-10-19T22:02:10.338Z
7
+ -->
8
+
1
9
  # @molecule/api-instance-leadership
2
10
 
3
- Instance-leadership core interface for molecule.dev: elect one API instance to run platform-wide singleton work, with demotion and takeover when the holder's connection is lost.
11
+ Instance-leadership core interface for molecule.dev: elect ONE API instance
12
+ to run platform-wide singleton work, and hand leadership over within seconds
13
+ when the holder's connection is lost.
14
+
15
+ Bond a provider — `@molecule/api-instance-leadership-postgresql` — at
16
+ startup, then start leader-only work from `acquireLeadership()`:
17
+
18
+ ```typescript
19
+ import { acquireLeadership, isLeader } from '@molecule/api-instance-leadership'
20
+ import { provider } from '@molecule/api-instance-leadership-postgresql'
21
+ import { setProvider } from '@molecule/api-instance-leadership'
22
+
23
+ setProvider(provider)
24
+
25
+ acquireLeadership({
26
+ onAcquired: () => startPoolManager(),
27
+ onLost: () => stopPoolManager(),
28
+ })
29
+ ```
30
+
31
+ **Only the code you run inside `onAcquired` is leader-only.** Every instance
32
+ still serves HTTP traffic, processes queue messages, and answers readiness
33
+ probes. Reach for this package when a job must run once across the whole
34
+ platform — pre-warm pool managers, retention sweeps, status recorders,
35
+ periodic billing — not for anything request-driven.
36
+
37
+ Use `isLeader()` for facts that must be decided at call time (e.g. skipping a
38
+ duplicate periodic tick); use `onAcquired`/`onLost` to start and stop the
39
+ loops themselves.
40
+
41
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
42
+ > It is written to be read by coding agents as much as by people, and is generated from this
43
+ > package's source — edit `src/index.ts` JSDoc, not this file.
44
+
45
+ ## Type
46
+
47
+ `core`
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ npm install @molecule/api-instance-leadership @molecule/api-bond
53
+ ```
54
+
55
+ ## API
56
+
57
+ ### Interfaces
58
+
59
+ #### `LeaderLockSession`
60
+
61
+ One dedicated lock session, as a provider bond sees it.
62
+
63
+ A session must be backed by a connection the provider owns exclusively: the
64
+ lock lives and dies with that connection, so an operator never has to tune a
65
+ TTL and a crashed instance never leaves a stale lock row behind.
66
+
67
+ ```typescript
68
+ interface LeaderLockSession {
69
+ /**
70
+ * Ask the shared store for the lock WITHOUT blocking.
71
+ *
72
+ * @returns `true` when this session now holds it.
73
+ */
74
+ tryLock(): Promise<boolean>
75
+
76
+ /**
77
+ * Release the lock (a no-op when this session does not hold it) and close
78
+ * the connection.
79
+ */
80
+ release(): Promise<void>
81
+
82
+ /**
83
+ * Register the "this connection is gone" handler. Called at most once, when
84
+ * the connection errors or the store ends the session.
85
+ *
86
+ * @param handler - Invoked on connection loss.
87
+ */
88
+ onLost(handler: () => void): void
89
+ }
90
+ ```
91
+
92
+ #### `AcquireLeadershipOptions`
93
+
94
+ What {@link InstanceLeadershipProvider.acquire} needs from the application.
95
+
96
+ ```typescript
97
+ interface AcquireLeadershipOptions {
98
+ /**
99
+ * Start the singleton subsystems. Invoked once per acquisition — including a
100
+ * RE-acquisition after a connection loss, so it must be safe to call again
101
+ * after {@link AcquireLeadershipOptions.onLost} stopped everything.
102
+ */
103
+ onAcquired: () => void | Promise<void>
104
+
105
+ /**
106
+ * Stop the singleton subsystems. Invoked when a leader loses its lock
107
+ * connection, BEFORE the provider tries to re-acquire: the store has (or is
108
+ * about to) release the lock, another instance may already be taking over,
109
+ * and two live leaders is exactly what leadership exists to prevent.
110
+ */
111
+ onLost?: () => void | Promise<void>
112
+ }
113
+ ```
114
+
115
+ #### `InstanceLeadershipProvider`
116
+
117
+ The contract every instance-leadership provider implements.
118
+
119
+ Application code reaches the bonded provider through the accessors in
120
+ `@molecule/api-instance-leadership` (`setProvider`, `getProvider`,
121
+ `isLeader`, `acquireLeadership`, `releaseLeadership`) rather than importing
122
+ a bond.
123
+
124
+ ```typescript
125
+ interface InstanceLeadershipProvider {
126
+ /**
127
+ * Whether THIS instance currently holds the leadership lock.
128
+ *
129
+ * @returns `true` from the moment the lock is acquired until it is lost or
130
+ * released.
131
+ */
132
+ isLeader(): boolean
133
+
134
+ /**
135
+ * The current leadership phase.
136
+ *
137
+ * @returns The {@link LeadershipState}.
138
+ */
139
+ state(): LeadershipState
4
140
 
141
+ /**
142
+ * Start contending for the lock, retrying forever in the background. Never
143
+ * rejects and never blocks — the caller starts its singleton subsystems from
144
+ * {@link AcquireLeadershipOptions.onAcquired}.
145
+ *
146
+ * Calling `acquire()` again while already contending or leading is a no-op.
147
+ *
148
+ * @param options - What to start on acquisition and stop on loss.
149
+ */
150
+ acquire(options: AcquireLeadershipOptions): void
151
+
152
+ /**
153
+ * Stop contending and release the lock, so another instance can take over
154
+ * immediately instead of waiting for this process to exit.
155
+ *
156
+ * @returns Resolves once the lock release and the connection close settle.
157
+ */
158
+ release(): Promise<void>
159
+ }
5
160
  ```
6
- npm install @molecule/api-instance-leadership
161
+
162
+ ### Types
163
+
164
+ #### `LeadershipState`
165
+
166
+ The leadership phase of one instance.
167
+
168
+ - `follower` — the instance is contending for the lock but does not hold it.
169
+ - `leader` — the instance holds the lock and runs the singleton work.
170
+ - `stopped` — `release()` was called; the instance no longer contends.
171
+
172
+ ```typescript
173
+ type LeadershipState = 'follower' | 'leader' | 'stopped'
174
+ ```
175
+
176
+ #### `LeaderLockFactory`
177
+
178
+ Creates a fresh lock session.
179
+
180
+ Injected rather than imported so a host application can reuse its own
181
+ database connection settings, and so tests can drive several simulated
182
+ instances through one fake store.
183
+
184
+ ```typescript
185
+ type LeaderLockFactory = () => Promise<LeaderLockSession>
7
186
  ```
8
187
 
9
- Part of the [Molecule](https://www.molecule.dev) package fleet. This page carries the manifest summary; the full generated documentation ships with the package's first committed version.
188
+ ### Functions
189
+
190
+ #### `setProvider(provider)`
191
+
192
+ Registers an instance-leadership provider as the active singleton. Called by
193
+ a bond package during application startup.
194
+
195
+ ```typescript
196
+ function setProvider(provider: InstanceLeadershipProvider): void
197
+ ```
198
+
199
+ - `provider` — The provider implementation to bond.
200
+
201
+ #### `getProvider()`
202
+
203
+ Retrieves the bonded instance-leadership provider, throwing if none is wired.
204
+
205
+ ```typescript
206
+ function getProvider(): InstanceLeadershipProvider
207
+ ```
208
+
209
+ **Returns:** The bonded instance-leadership provider.
210
+
211
+ #### `hasProvider()`
212
+
213
+ Checks whether an instance-leadership provider is currently bonded.
214
+
215
+ ```typescript
216
+ function hasProvider(): boolean
217
+ ```
218
+
219
+ **Returns:** `true` if a provider is bonded.
220
+
221
+ #### `isLeader()`
222
+
223
+ Whether THIS instance currently holds platform leadership.
224
+
225
+ ```typescript
226
+ function isLeader(): boolean
227
+ ```
228
+
229
+ **Returns:** `false` when no provider is bonded or this instance is a follower.
230
+
231
+ #### `leadershipState()`
232
+
233
+ The current leadership phase of this instance.
234
+
235
+ ```typescript
236
+ function leadershipState(): LeadershipState
237
+ ```
238
+
239
+ **Returns:** The bonded provider's state, or `'follower'` when none is bonded.
240
+
241
+ #### `acquireLeadership(options)`
242
+
243
+ Starts contending for platform leadership through the bonded provider.
244
+
245
+ ```typescript
246
+ function acquireLeadership(options: AcquireLeadershipOptions): void
247
+ ```
248
+
249
+ - `options` — What to start on acquisition and stop on loss.
250
+
251
+ #### `releaseLeadership()`
252
+
253
+ Releases platform leadership through the bonded provider so another
254
+ instance can take over immediately.
255
+
256
+ ```typescript
257
+ function releaseLeadership(): Promise<void>
258
+ ```
259
+
260
+ **Returns:** Resolves once the release settles; a no-op when no provider is bonded.
261
+
262
+ ## Available Providers
263
+
264
+ | Provider | Package |
265
+ | ---------- | ---------------------------------------------- |
266
+ | PostgreSQL | `@molecule/api-instance-leadership-postgresql` |
267
+
268
+ ## Injection Notes
269
+
270
+ ### Requirements
271
+
272
+ Peer dependencies:
273
+
274
+ - `@molecule/api-bond` ^1.0.1
275
+
276
+ ### Runtime Dependencies
277
+
278
+ - `@molecule/api-bond`
10
279
 
11
- License: Apache-2.0
280
+ - **The lock must live in a store every instance shares.** A host-local lock
281
+ (a loopback port, a file) elects one leader PER HOST, so every singleton
282
+ runs twice — the failure this package exists to prevent.
283
+ - **`onLost` is not optional in practice for a real leader.** A provider may
284
+ lose the lock while the process keeps running (a database connection that
285
+ errors); the instance demotes itself and another may already be leading.
286
+ Stop the loops in `onLost`, or two copies run.
287
+ - **Re-acquisition is possible.** `onAcquired` runs again after a loss, so
288
+ it must be safe to call after `onLost` stopped everything.
289
+ - **Followers retry forever in the background.** `acquireLeadership()` never
290
+ rejects and never blocks; a database that is down at boot just means this
291
+ instance stays a follower until it is reachable.
292
+ - **No provider bonded ⇒ `isLeader()` is `false`.** A single-instance app can
293
+ use this package unchanged (it never leads until a bond exists) or omit it
294
+ and run its singleton work directly.
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Browser guard — `@molecule/api-instance-leadership` is SERVER-ONLY.
3
3
  *
4
- * Evaluating a server package in a browser bundle is always an import-graph
5
- * mistake (node APIs, secrets); without this guard it surfaces as a cryptic
6
- * downstream crash far from the culprit. Throwing here names the package and
7
- * the fix at the exact moment the client bundle evaluates it. jsdom tests and
8
- * SSR are unaffected: the throw requires browser globals AND the absence of a
9
- * node runtime.
4
+ * Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
5
+ * Evaluating a server package in a browser bundle is always an import-graph mistake
6
+ * (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
7
+ * ("Buffer is not defined") far from the culprit. Throwing here names the package and
8
+ * the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
9
+ * unaffected: the throw requires browser globals AND the absence of a node runtime.
10
10
  */
11
11
  const g = globalThis;
12
12
  if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-instance-leadership",
3
- "version": "0.0.1-stage",
3
+ "version": "1.1.0",
4
4
  "description": "Instance-leadership core interface for molecule.dev: elect one API instance to run platform-wide singleton work, with demotion and takeover when the holder's connection is lost.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",