@cat-factory/node-server 0.123.1 → 0.125.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/dist/realtime.js CHANGED
@@ -1,4 +1,4 @@
1
- import { authorizeWsUpgrade } from '@cat-factory/server';
1
+ import { MACHINE_EVENTS_SUBSCRIBE_PATTERN, authorizeMachineSubscribe, authorizeWsUpgrade, } from '@cat-factory/server';
2
2
  import { WebSocket, WebSocketServer } from 'ws';
3
3
  const WS_EVENTS_PATH = /^\/workspaces\/([^/]+)\/events$/;
4
4
  /**
@@ -16,6 +16,36 @@ export class NodeRealtimeHub {
16
16
  // The `?cid=` each socket connected with — used to skip echoing a board mutation back to
17
17
  // the connection that caused it (the Node analogue of the DO's serialized attachment).
18
18
  connectionIds = new WeakMap();
19
+ roomListener = null;
20
+ /**
21
+ * Observe room open/close transitions (see {@link RealtimeRoomWatcher}). Best-effort in the one
22
+ * direction that matters: a listener that throws must never break a subscribe/unsubscribe, since
23
+ * the socket lifecycle is the transport's core job and the listener is an optional
24
+ * mothership-mode add-on.
25
+ *
26
+ * At most ONE listener, and a second registration THROWS rather than replacing the first: silent
27
+ * replacement would leave the displaced observer believing it is still watching (a mothership
28
+ * node that never learns a room opened holds no upstream stream and the board just stays frozen),
29
+ * which is precisely the class of quiet breakage this seam exists to serve.
30
+ */
31
+ watchRooms(listener) {
32
+ if (this.roomListener) {
33
+ throw new Error('NodeRealtimeHub already has a room listener; detach before re-registering');
34
+ }
35
+ this.roomListener = listener;
36
+ return () => {
37
+ if (this.roomListener === listener)
38
+ this.roomListener = null;
39
+ };
40
+ }
41
+ notifyRoom(event, workspaceId) {
42
+ try {
43
+ this.roomListener?.[event](workspaceId);
44
+ }
45
+ catch {
46
+ // See watchRooms: the listener is an add-on, the socket lifecycle is not.
47
+ }
48
+ }
19
49
  /** Add a socket to a workspace's room; it is reaped on close/error. */
20
50
  subscribe(workspaceId, socket, connectionId) {
21
51
  let room = this.rooms.get(workspaceId);
@@ -23,7 +53,10 @@ export class NodeRealtimeHub {
23
53
  room = new Set();
24
54
  this.rooms.set(workspaceId, room);
25
55
  }
56
+ const wasEmpty = room.size === 0;
26
57
  room.add(socket);
58
+ if (wasEmpty)
59
+ this.notifyRoom('roomOpened', workspaceId);
27
60
  if (connectionId)
28
61
  this.connectionIds.set(socket, connectionId);
29
62
  const drop = () => this.unsubscribe(workspaceId, socket);
@@ -35,8 +68,10 @@ export class NodeRealtimeHub {
35
68
  if (!room)
36
69
  return;
37
70
  room.delete(socket);
38
- if (room.size === 0)
71
+ if (room.size === 0) {
39
72
  this.rooms.delete(workspaceId);
73
+ this.notifyRoom('roomClosed', workspaceId);
74
+ }
40
75
  }
41
76
  /**
42
77
  * Fan a pre-serialised JSON event out to every socket on a workspace's stream. When
@@ -140,41 +175,90 @@ export class NodeEventPublisher {
140
175
  }
141
176
  /** How often to ping idle sockets to detect (and reap) half-open connections. */
142
177
  const HEARTBEAT_INTERVAL_MS = 30_000;
178
+ /**
179
+ * Status-line reasons for a refused machine subscription. Keyed by the exact statuses
180
+ * {@link authorizeMachineSubscribe} can return, so a new verdict status fails `tsc` here rather
181
+ * than writing `undefined` into a raw HTTP status line. `503` is unreachable on THIS runtime (see
182
+ * {@link MachineSubscribeDeps}) but is kept so the map stays total over the verdict union — a
183
+ * `Partial` here would trade the compile-time guarantee for a runtime `undefined`.
184
+ */
185
+ const MACHINE_REJECT_REASON = {
186
+ 403: 'Forbidden',
187
+ 404: 'Not Found',
188
+ 503: 'Service Unavailable',
189
+ };
143
190
  /**
144
191
  * Attach the real-time WebSocket transport to a running Node HTTP server: accept
145
192
  * `GET /workspaces/:ws/events` upgrades (authorising the `?ticket=` exactly like the
146
193
  * shared EventsController), register each socket into the {@link NodeRealtimeHub}, and
147
194
  * run a heartbeat that terminates dead connections. Returns a stop function that clears
148
195
  * the heartbeat and closes the WS server (call it on graceful shutdown).
196
+ *
197
+ * When `machineSubscribe` is supplied it ALSO accepts the mothership-mode machine subscription
198
+ * (`GET /internal/events/subscribe/:ws`), authorised by the shared `authorizeMachineSubscribe`
199
+ * rather than a browser `?ticket=`. Both handshakes land in the same hub room, so a subscribed
200
+ * node receives exactly what a browser on that workspace does — see the initiative doc. The
201
+ * upgrade is handled here (not in the shared `eventsRelayController`) for the same reason the
202
+ * browser stream is: `@hono/node-server` cannot upgrade from a Hono `Response`, so the request
203
+ * never reaches the controller on this runtime.
149
204
  */
150
- export function attachRealtime(server, hub, auth, log) {
205
+ export function attachRealtime(server, hub, auth, log, machineSubscribe) {
151
206
  const wss = new WebSocketServer({ noServer: true });
207
+ /** Complete an authorised handshake: join the hub room and hand the socket to the heartbeat. */
208
+ const accept = (request, socket, head, workspaceId, cid) => {
209
+ wss.handleUpgrade(request, socket, head, (ws) => {
210
+ hub.subscribe(workspaceId, ws, cid);
211
+ wss.emit('connection', ws, request);
212
+ });
213
+ };
214
+ /** Reject a handshake with a bare status line (there is no Response object at this layer). */
215
+ const reject = (socket, status, reason) => {
216
+ socket.write(`HTTP/1.1 ${status} ${reason}\r\n\r\n`);
217
+ socket.destroy();
218
+ };
152
219
  server.on('upgrade', (request, socket, head) => {
153
220
  const url = new URL(request.url ?? '/', 'http://localhost');
221
+ // The tab's stable connection id (see the SPA's `utils/connectionId.ts`) — or, on the machine
222
+ // route, the subscribing NODE's stable id — so the hub can skip echoing a mutation back to the
223
+ // connection that caused it.
224
+ const cid = url.searchParams.get('cid');
225
+ // Mothership-mode inbound subscription. Checked before the browser route because the two
226
+ // patterns are disjoint; a deployment that isn't a mothership simply never matches (the
227
+ // request falls through to the 404 below).
228
+ const machineMatch = machineSubscribe
229
+ ? MACHINE_EVENTS_SUBSCRIBE_PATTERN.exec(url.pathname)
230
+ : null;
231
+ if (machineMatch && machineSubscribe) {
232
+ const workspaceId = decodeURIComponent(machineMatch[1]);
233
+ void authorizeMachineSubscribe({
234
+ auth,
235
+ token: request.headers.authorization,
236
+ workspaceId,
237
+ accountOf: machineSubscribe.accountOf,
238
+ }).then((verdict) => {
239
+ if (!verdict.ok) {
240
+ reject(socket, verdict.status, MACHINE_REJECT_REASON[verdict.status]);
241
+ return;
242
+ }
243
+ accept(request, socket, head, workspaceId, cid);
244
+ });
245
+ return;
246
+ }
154
247
  const match = WS_EVENTS_PATH.exec(url.pathname);
155
248
  // Not our route: refuse rather than leave the socket dangling. (Node mode has no
156
249
  // other WebSocket endpoints.)
157
250
  if (!match) {
158
- socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
159
- socket.destroy();
251
+ reject(socket, 404, 'Not Found');
160
252
  return;
161
253
  }
162
254
  const workspaceId = decodeURIComponent(match[1]);
163
255
  const ticket = url.searchParams.get('ticket') ?? undefined;
164
- // The tab's stable connection id (see the SPA's `utils/connectionId.ts`), so the hub can
165
- // skip echoing a board mutation back to the connection that caused it.
166
- const cid = url.searchParams.get('cid');
167
256
  void authorizeWsUpgrade(auth, ticket, workspaceId).then((verdict) => {
168
257
  if (!verdict.ok) {
169
- const reason = verdict.status === 401 ? 'Unauthorized' : 'Service Unavailable';
170
- socket.write(`HTTP/1.1 ${verdict.status} ${reason}\r\n\r\n`);
171
- socket.destroy();
258
+ reject(socket, verdict.status, verdict.status === 401 ? 'Unauthorized' : 'Service Unavailable');
172
259
  return;
173
260
  }
174
- wss.handleUpgrade(request, socket, head, (ws) => {
175
- hub.subscribe(workspaceId, ws, cid);
176
- wss.emit('connection', ws, request);
177
- });
261
+ accept(request, socket, head, workspaceId, cid);
178
262
  });
179
263
  });
180
264
  // Liveness sweep: a socket that doesn't answer a ping before the next tick is
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAmB,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACzE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AAyB/C,MAAM,cAAc,GAAG,iCAAiC,CAAA;AAexD;;;;;;;;;GASG;AACH,MAAM,OAAO,eAAe;IACT,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAA;IAC1D,yFAAyF;IACzF,uFAAuF;IACtE,aAAa,GAAG,IAAI,OAAO,EAAqB,CAAA;IAEjE,uEAAuE;IACvE,SAAS,CAAC,WAAmB,EAAE,MAAiB,EAAE,YAA4B;QAC5E,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACtC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,IAAI,GAAG,EAAE,CAAA;YAChB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;QACnC,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAChB,IAAI,YAAY;YAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;QAC9D,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QACxD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QACxB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC1B,CAAC;IAEO,WAAW,CAAC,WAAmB,EAAE,MAAiB;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACxC,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACnB,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IACrD,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,WAAmB,EAAE,OAAe,EAAE,kBAAkC;QAChF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACxC,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;YAC1B,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI;gBAAE,SAAQ;YAClD,IAAI,kBAAkB,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,kBAAkB;gBAAE,SAAQ;YACzF,IAAI,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACtB,CAAC;YAAC,MAAM,CAAC;gBACP,2DAA2D;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,kBAAkB;IAKA,IAAI;IAJjC,0FAA0F;IAC1F,2FAA2F;IAC3F,wFAAwF;IACxF,aAAa;IACb,YAA6B,IAAoB;oBAApB,IAAI;IAAmB,CAAC;IAErD,KAAK,CAAC,gBAAgB,CACpB,WAAmB,EACnB,QAA2B,EAC3B,KAAoB;QAEpB,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;YACxB,IAAI,EAAE,WAAW;YACjB,QAAQ;YACR,KAAK,EAAE,KAAK,IAAI,IAAI;YACpB,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;SACf,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,WAAmB,EACnB,MAAc,EACd,QAAwB,EACxB,kBAAkC;QAElC,0FAA0F;QAC1F,yEAAyE;QACzE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAA;IAC1F,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,WAAmB,EACnB,GAAiB,EACjB,KAAoB;QAEpB,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC7F,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,WAAmB,EAAE,GAAuB;QACvE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,WAAmB,EAAE,GAAuB;QAC/D,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IACrE,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,WAAmB,EAAE,YAA0B;QACvE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IACnF,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,WAAmB,EAAE,QAAyB;QAClE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAChF,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,WAAmB,EAAE,MAAyB;QAC3E,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC7E,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,WAAmB,EAAE,OAAyB;QAC1E,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC3E,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,WAAmB,EAAE,MAAqB;QACnE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IACxE,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,WAAmB,EAAE,OAA0B;QAC5E,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC5E,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,WAAmB,EAAE,OAAsB;QACpE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IACxE,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,WAAmB,EAAE,UAAsB;QACjE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,WAAmB,EAAE,OAA4B;QACzE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC9E,CAAC;IAEO,OAAO,CACb,WAAmB,EACnB,KAAqB,EACrB,kBAAkC;QAElC,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC,CAAA;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,gFAAgF;YAChF,qDAAqD;QACvD,CAAC;IACH,CAAC;CACF;AAED,iFAAiF;AACjF,MAAM,qBAAqB,GAAG,MAAM,CAAA;AAEpC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAwB,EACxB,GAAoB,EACpB,IAAgB,EAChB,GAAmB;IAEnB,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAEnD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAA;QAC3D,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAC/C,iFAAiF;QACjF,8BAA8B;QAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAA;YAC9C,MAAM,CAAC,OAAO,EAAE,CAAA;YAChB,OAAM;QACR,CAAC;QACD,MAAM,WAAW,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;QACjD,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAA;QAC1D,yFAAyF;QACzF,uEAAuE;QACvE,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAEvC,KAAK,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YAClE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,qBAAqB,CAAA;gBAC9E,MAAM,CAAC,KAAK,CAAC,YAAY,OAAO,CAAC,MAAM,IAAI,MAAM,UAAU,CAAC,CAAA;gBAC5D,MAAM,CAAC,OAAO,EAAE,CAAA;gBAChB,OAAM;YACR,CAAC;YACD,GAAG,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE;gBAC9C,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;gBACnC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;YACrC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,8EAA8E;IAC9E,mFAAmF;IACnF,MAAM,KAAK,GAAG,IAAI,OAAO,EAAa,CAAA;IACtC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE;QAC1B,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACb,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACnB,EAAE,CAAC,SAAS,EAAE,CAAA;gBACd,SAAQ;YACV,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAChB,IAAI,CAAC;gBACH,EAAE,CAAC,IAAI,EAAE,CAAA;YACX,CAAC;YAAC,MAAM,CAAC;gBACP,EAAE,CAAC,SAAS,EAAE,CAAA;YAChB,CAAC;QACH,CAAC;IACH,CAAC,EAAE,qBAAqB,CAAC,CAAA;IACzB,oEAAoE;IACpE,SAAS,CAAC,KAAK,EAAE,EAAE,CAAA;IAEnB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,iEAAiE,CAAC,CAAA;IAE/E,OAAO,GAAG,EAAE;QACV,aAAa,CAAC,SAAS,CAAC,CAAA;QACxB,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,OAAO;YAAE,EAAE,CAAC,SAAS,EAAE,CAAA;QAC5C,GAAG,CAAC,KAAK,EAAE,CAAA;IACb,CAAC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,33 @@
1
1
  import type { BrainstormSession, BrainstormSessionRepository, BrainstormStage, ClarityReview, ClarityReviewRepository, ConsensusSession, ConsensusSessionRepository, DocInterviewRepository, DocInterviewSession, RequirementReview, RequirementReviewRepository } from '@cat-factory/kernel';
2
2
  import type { DrizzleDb } from '../../db/client.js';
3
- /**
4
- * Requirements reviews over Postgres (the Drizzle mirror of the Worker's
5
- * `D1RequirementReviewRepository`, migration 0021). The reviewed items live as a JSON
6
- * array in `items`; the service keeps at most one live review per block (it deletes
7
- * the block's prior review before inserting a fresh one), so `getByBlock` returns the
8
- * latest. Behaviourally identical to the D1 repo so the cross-runtime conformance
9
- * suite asserts the same requirements-rework substitution against both stores.
10
- */
11
3
  export declare class DrizzleRequirementReviewRepository implements RequirementReviewRepository {
12
4
  private readonly db;
13
5
  constructor(db: DrizzleDb);
14
6
  getByBlock(workspaceId: string, blockId: string): Promise<RequirementReview | null>;
15
7
  get(workspaceId: string, id: string): Promise<RequirementReview | null>;
8
+ /**
9
+ * Force-write. A fresh insert starts at rev 0; a write over an existing row BUMPS it, so a
10
+ * concurrent {@link compareAndSwap} holding the old revision still detects that the row moved.
11
+ */
16
12
  upsert(workspaceId: string, review: RequirementReview): Promise<void>;
17
- deleteByBlock(workspaceId: string, blockId: string): Promise<void>;
13
+ compareAndSwap(workspaceId: string, review: RequirementReview): Promise<boolean>;
14
+ /**
15
+ * Publish `review` as the block's one live review, as a SINGLE conflict-targeted upsert on the
16
+ * block's UNIQUE index.
17
+ *
18
+ * This deliberately is NOT a transactioned delete-then-insert. A transaction is not a
19
+ * uniqueness constraint: at Postgres' default READ COMMITTED a DELETE takes no predicate lock,
20
+ * so two concurrent review runs each delete nothing (or each other's already-gone row), each
21
+ * insert, and both commit — leaving the block with TWO live reviews, which is the exact hazard
22
+ * this method exists to prevent. (SQLite serializes writers, so D1 was accidentally safe; a
23
+ * domain invariant must not rest on which runtime it happens to run under.) One statement
24
+ * against the constraint has no window to interleave into on either runtime.
25
+ *
26
+ * The predecessor's row IS this row — its `id` is overwritten — so the superseded review is
27
+ * gone rather than left beside the live one, and `rev` restarts at 0 so a writer still holding
28
+ * the predecessor's revision can't match.
29
+ */
30
+ replaceForBlock(workspaceId: string, review: RequirementReview): Promise<void>;
18
31
  }
19
32
  /**
20
33
  * Interactive document-interview sessions over Postgres (the Drizzle mirror of the Worker's
@@ -44,32 +57,34 @@ export declare class DrizzleConsensusSessionRepository implements ConsensusSessi
44
57
  getByBlock(workspaceId: string, blockId: string): Promise<ConsensusSession | null>;
45
58
  upsert(workspaceId: string, session: ConsensusSession): Promise<void>;
46
59
  }
47
- /**
48
- * Clarity (bug-report triage) reviews over Postgres — the Drizzle mirror of the Worker's
49
- * `D1ClarityReviewRepository`. Behaviourally identical to the D1 repo so the cross-runtime
50
- * conformance suite asserts the same clarified-brief substitution against both stores.
51
- */
52
60
  export declare class DrizzleClarityReviewRepository implements ClarityReviewRepository {
53
61
  private readonly db;
54
62
  constructor(db: DrizzleDb);
55
63
  getByBlock(workspaceId: string, blockId: string): Promise<ClarityReview | null>;
56
64
  get(workspaceId: string, id: string): Promise<ClarityReview | null>;
57
65
  upsert(workspaceId: string, review: ClarityReview): Promise<void>;
58
- deleteByBlock(workspaceId: string, blockId: string): Promise<void>;
66
+ compareAndSwap(workspaceId: string, review: ClarityReview): Promise<boolean>;
67
+ /**
68
+ * A single conflict-targeted upsert on the block's UNIQUE index — see
69
+ * {@link DrizzleRequirementReviewRepository.replaceForBlock} for why a transactioned
70
+ * delete-then-insert does not hold this invariant under READ COMMITTED.
71
+ */
72
+ replaceForBlock(workspaceId: string, review: ClarityReview): Promise<void>;
59
73
  }
60
- /**
61
- * Brainstorm (structured-dialogue) sessions over Postgres — the Drizzle mirror of the Worker's
62
- * `D1BrainstormSessionRepository`. Behaviourally identical so the cross-runtime conformance
63
- * suite asserts the same per-stage round-trip and brainstorm direction handoff against both
64
- * stores. Keyed per (block, stage): a block may hold a live `requirements` AND `architecture`
65
- * session at once.
66
- */
67
74
  export declare class DrizzleBrainstormSessionRepository implements BrainstormSessionRepository {
68
75
  private readonly db;
69
76
  constructor(db: DrizzleDb);
70
77
  getByBlockStage(workspaceId: string, blockId: string, stage: BrainstormStage): Promise<BrainstormSession | null>;
71
78
  get(workspaceId: string, id: string): Promise<BrainstormSession | null>;
72
79
  upsert(workspaceId: string, session: BrainstormSession): Promise<void>;
73
- deleteByBlockStage(workspaceId: string, blockId: string, stage: BrainstormStage): Promise<void>;
80
+ compareAndSwap(workspaceId: string, session: BrainstormSession): Promise<boolean>;
81
+ /**
82
+ * A single conflict-targeted upsert on the (block, STAGE) UNIQUE index — see
83
+ * {@link DrizzleRequirementReviewRepository.replaceForBlock} for why a transactioned
84
+ * delete-then-insert does not hold this invariant under READ COMMITTED. The stage is part of
85
+ * the conflict target, so the block's other live stage is untouched by construction rather
86
+ * than by a scoped delete.
87
+ */
88
+ replaceForBlockStage(workspaceId: string, session: BrainstormSession): Promise<void>;
74
89
  }
75
90
  //# sourceMappingURL=reviews.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../../../src/repositories/drizzle/reviews.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../../../src/repositories/drizzle/reviews.ts"],"names":[],"mappings":"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"}