@ultimat3/realtime 27.12.0 → 27.13.1
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/package.json +5 -5
- package/src/nats-client.ts +6 -0
- package/src/nats-fake.ts +5 -0
- package/src/nats-lib-client.ts +20 -1
- package/src/nats-transport.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/realtime",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.13.1",
|
|
4
4
|
"description": "Three-tier realtime: channels, live queries, local-first sync — one protocol, one mutator shape",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"test": "bun test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ultimat3/core": "27.
|
|
44
|
-
"@ultimat3/entity": "27.
|
|
45
|
-
"@ultimat3/policy": "27.
|
|
46
|
-
"@ultimat3/query": "27.
|
|
43
|
+
"@ultimat3/core": "27.13.1",
|
|
44
|
+
"@ultimat3/entity": "27.13.1",
|
|
45
|
+
"@ultimat3/policy": "27.13.1",
|
|
46
|
+
"@ultimat3/query": "27.13.1",
|
|
47
47
|
"nats": "2.29.3"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/nats-client.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface NatsClient {
|
|
|
43
43
|
readonly connected: boolean;
|
|
44
44
|
publish(subject: string, payload: Uint8Array): void;
|
|
45
45
|
subscribe(subject: string, handler: NatsMessageHandler): NatsSubscription;
|
|
46
|
+
/**
|
|
47
|
+
* Answers once the server has processed every line sent before it — a `SUB` included. `subscribe`
|
|
48
|
+
* only WRITES that line: a message another connection publishes before the server reads it is
|
|
49
|
+
* not delivered, so "subscribed" means flushed.
|
|
50
|
+
*/
|
|
51
|
+
flush(): Promise<void>;
|
|
46
52
|
request(subject: string, payload: Uint8Array, options?: NatsRequestOptions): Promise<NatsMessage>;
|
|
47
53
|
requestMany(
|
|
48
54
|
subject: string,
|
package/src/nats-fake.ts
CHANGED
|
@@ -167,6 +167,11 @@ class FakeNatsClient implements NatsClient {
|
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/** The broker delivers synchronously, so a subscription is held the moment it is added. */
|
|
171
|
+
async flush(): Promise<void> {
|
|
172
|
+
this.#assertOpen();
|
|
173
|
+
}
|
|
174
|
+
|
|
170
175
|
async request(
|
|
171
176
|
subject: string,
|
|
172
177
|
payload: Uint8Array,
|
package/src/nats-lib-client.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// This header said "every failure leaves here as an `UltimateError`" until 2026-08, which a reader
|
|
15
15
|
// took as a guarantee it never was.
|
|
16
16
|
|
|
17
|
-
import { finiteOption, renderThrowable } from '@ultimat3/core';
|
|
17
|
+
import { finiteOption, isUltimateError, renderThrowable } from '@ultimat3/core';
|
|
18
18
|
import { connect, Events, headers, Match, type Msg, type MsgHdrs, type NatsConnection } from 'nats';
|
|
19
19
|
import { TransportUnavailableError } from './errors';
|
|
20
20
|
import {
|
|
@@ -113,6 +113,25 @@ class LibNatsClient implements NatsClient {
|
|
|
113
113
|
return { unsubscribe: () => subscription.unsubscribe() };
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
async flush(): Promise<void> {
|
|
117
|
+
// Bounded like a request: the library's flush waits on a PONG forever, and a server that went
|
|
118
|
+
// silent (`nats-black-hole.live.test.ts`) never sends one.
|
|
119
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
120
|
+
const late = new Promise<never>((_, reject) => {
|
|
121
|
+
timer = setTimeout(
|
|
122
|
+
() => reject(unavailable(this.#target, `flush unanswered after ${this.#timeoutMs}ms`)),
|
|
123
|
+
this.#timeoutMs,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
try {
|
|
127
|
+
await Promise.race([this.#connection.flush(), late]);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
throw isUltimateError(error) ? error : unavailable(this.#target, `flush: ${describe(error)}`);
|
|
130
|
+
} finally {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
async request(
|
|
117
136
|
subject: string,
|
|
118
137
|
payload: Uint8Array,
|
package/src/nats-transport.ts
CHANGED
|
@@ -204,6 +204,11 @@ export class NatsTransport implements Transport {
|
|
|
204
204
|
},
|
|
205
205
|
(call) => this.#translating(`subscribe to ${subject}`, call),
|
|
206
206
|
);
|
|
207
|
+
// Resolved only once the server HOLDS it: a publish from another process in the gap between
|
|
208
|
+
// writing the SUB and the server reading it was lost (`nats-black-hole.live.test.ts`, CI run
|
|
209
|
+
// 39145087176). Mid-reconnect there is no server to ask: the library re-sends the SUB on the
|
|
210
|
+
// way back, and `onReconnect` reports that gap. A flush that fails is that same case.
|
|
211
|
+
if (client.connected) await client.flush().catch(() => undefined);
|
|
207
212
|
return { subject, unsubscribe: release };
|
|
208
213
|
}
|
|
209
214
|
|