@privateclaw/privateclaw-relay 0.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.
Files changed (42) hide show
  1. package/.env.example +13 -0
  2. package/README.md +86 -0
  3. package/dist/cli-error.d.ts +3 -0
  4. package/dist/cli-error.js +7 -0
  5. package/dist/cli-error.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +14 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.d.ts +13 -0
  10. package/dist/config.js +36 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/frame-cache.d.ts +77 -0
  13. package/dist/frame-cache.js +127 -0
  14. package/dist/frame-cache.js.map +1 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +5 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/push-notifier.d.ts +36 -0
  19. package/dist/push-notifier.js +198 -0
  20. package/dist/push-notifier.js.map +1 -0
  21. package/dist/push-registration-store.d.ts +39 -0
  22. package/dist/push-registration-store.js +98 -0
  23. package/dist/push-registration-store.js.map +1 -0
  24. package/dist/relay-cli.d.ts +28 -0
  25. package/dist/relay-cli.js +256 -0
  26. package/dist/relay-cli.js.map +1 -0
  27. package/dist/relay-cluster.d.ts +144 -0
  28. package/dist/relay-cluster.js +436 -0
  29. package/dist/relay-cluster.js.map +1 -0
  30. package/dist/relay-server.d.ts +25 -0
  31. package/dist/relay-server.js +1090 -0
  32. package/dist/relay-server.js.map +1 -0
  33. package/dist/session-store.d.ts +40 -0
  34. package/dist/session-store.js +159 -0
  35. package/dist/session-store.js.map +1 -0
  36. package/dist/tunnel-installer.d.ts +36 -0
  37. package/dist/tunnel-installer.js +402 -0
  38. package/dist/tunnel-installer.js.map +1 -0
  39. package/dist/tunnel.d.ts +35 -0
  40. package/dist/tunnel.js +334 -0
  41. package/dist/tunnel.js.map +1 -0
  42. package/package.json +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-notifier.js","sourceRoot":"","sources":["../src/push-notifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface RelayPushRegistrationRecord {
2
+ sessionId: string;
3
+ appId: string;
4
+ token: string;
5
+ updatedAt: number;
6
+ }
7
+ export interface RelayPushRegistrationStore {
8
+ readonly persistent: boolean;
9
+ saveRegistration(registration: RelayPushRegistrationRecord, expiresAt: number): Promise<void>;
10
+ deleteRegistration(sessionId: string, appId: string): Promise<void>;
11
+ listRegistrations(sessionId: string): Promise<RelayPushRegistrationRecord[]>;
12
+ clearSession(sessionId: string): Promise<void>;
13
+ touchSession(sessionId: string, expiresAt: number): Promise<void>;
14
+ close(): Promise<void>;
15
+ }
16
+ export declare class InMemoryRelayPushRegistrationStore implements RelayPushRegistrationStore {
17
+ readonly persistent = false;
18
+ private readonly registrations;
19
+ saveRegistration(registration: RelayPushRegistrationRecord, _expiresAt: number): Promise<void>;
20
+ deleteRegistration(sessionId: string, appId: string): Promise<void>;
21
+ listRegistrations(sessionId: string): Promise<RelayPushRegistrationRecord[]>;
22
+ clearSession(sessionId: string): Promise<void>;
23
+ touchSession(_sessionId: string, _expiresAt: number): Promise<void>;
24
+ close(): Promise<void>;
25
+ }
26
+ export declare class RedisRelayPushRegistrationStore implements RelayPushRegistrationStore {
27
+ readonly persistent = true;
28
+ private readonly redis;
29
+ constructor(redisUrl: string);
30
+ saveRegistration(registration: RelayPushRegistrationRecord, expiresAt: number): Promise<void>;
31
+ deleteRegistration(sessionId: string, appId: string): Promise<void>;
32
+ listRegistrations(sessionId: string): Promise<RelayPushRegistrationRecord[]>;
33
+ clearSession(sessionId: string): Promise<void>;
34
+ touchSession(sessionId: string, expiresAt: number): Promise<void>;
35
+ close(): Promise<void>;
36
+ }
37
+ export declare function createRelayPushRegistrationStore(params: {
38
+ redisUrl?: string;
39
+ }): RelayPushRegistrationStore;
@@ -0,0 +1,98 @@
1
+ import { Redis } from "ioredis";
2
+ const PUSH_REGISTRATION_KEY_PREFIX = "privateclaw:push-registrations:v1";
3
+ function pushRegistrationsKey(sessionId) {
4
+ return `${PUSH_REGISTRATION_KEY_PREFIX}:${sessionId}`;
5
+ }
6
+ export class InMemoryRelayPushRegistrationStore {
7
+ persistent = false;
8
+ registrations = new Map();
9
+ async saveRegistration(registration, _expiresAt) {
10
+ const sessionRegistrations = this.registrations.get(registration.sessionId) ??
11
+ new Map();
12
+ sessionRegistrations.set(registration.appId, { ...registration });
13
+ this.registrations.set(registration.sessionId, sessionRegistrations);
14
+ }
15
+ async deleteRegistration(sessionId, appId) {
16
+ const sessionRegistrations = this.registrations.get(sessionId);
17
+ if (!sessionRegistrations) {
18
+ return;
19
+ }
20
+ sessionRegistrations.delete(appId);
21
+ if (sessionRegistrations.size === 0) {
22
+ this.registrations.delete(sessionId);
23
+ }
24
+ }
25
+ async listRegistrations(sessionId) {
26
+ const sessionRegistrations = this.registrations.get(sessionId);
27
+ if (!sessionRegistrations) {
28
+ return [];
29
+ }
30
+ return [...sessionRegistrations.values()].map((registration) => ({
31
+ ...registration,
32
+ }));
33
+ }
34
+ async clearSession(sessionId) {
35
+ this.registrations.delete(sessionId);
36
+ }
37
+ async touchSession(_sessionId, _expiresAt) { }
38
+ async close() {
39
+ this.registrations.clear();
40
+ }
41
+ }
42
+ export class RedisRelayPushRegistrationStore {
43
+ persistent = true;
44
+ redis;
45
+ constructor(redisUrl) {
46
+ this.redis = new Redis(redisUrl, {
47
+ lazyConnect: false,
48
+ maxRetriesPerRequest: 1,
49
+ });
50
+ }
51
+ async saveRegistration(registration, expiresAt) {
52
+ const ttlMs = Math.max(expiresAt - Date.now(), 1);
53
+ await this.redis
54
+ .multi()
55
+ .hset(pushRegistrationsKey(registration.sessionId), registration.appId, JSON.stringify(registration))
56
+ .pexpire(pushRegistrationsKey(registration.sessionId), ttlMs)
57
+ .exec();
58
+ }
59
+ async deleteRegistration(sessionId, appId) {
60
+ await this.redis.hdel(pushRegistrationsKey(sessionId), appId);
61
+ }
62
+ async listRegistrations(sessionId) {
63
+ const values = await this.redis.hvals(pushRegistrationsKey(sessionId));
64
+ const registrations = [];
65
+ for (const rawValue of values) {
66
+ try {
67
+ const registration = JSON.parse(rawValue);
68
+ if (typeof registration.sessionId === "string" &&
69
+ typeof registration.appId === "string" &&
70
+ typeof registration.token === "string" &&
71
+ typeof registration.updatedAt === "number") {
72
+ registrations.push(registration);
73
+ }
74
+ }
75
+ catch {
76
+ continue;
77
+ }
78
+ }
79
+ return registrations;
80
+ }
81
+ async clearSession(sessionId) {
82
+ await this.redis.del(pushRegistrationsKey(sessionId));
83
+ }
84
+ async touchSession(sessionId, expiresAt) {
85
+ const ttlMs = Math.max(expiresAt - Date.now(), 1);
86
+ await this.redis.pexpire(pushRegistrationsKey(sessionId), ttlMs);
87
+ }
88
+ async close() {
89
+ await this.redis.quit();
90
+ }
91
+ }
92
+ export function createRelayPushRegistrationStore(params) {
93
+ if (params.redisUrl) {
94
+ return new RedisRelayPushRegistrationStore(params.redisUrl);
95
+ }
96
+ return new InMemoryRelayPushRegistrationStore();
97
+ }
98
+ //# sourceMappingURL=push-registration-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-registration-store.js","sourceRoot":"","sources":["../src/push-registration-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { type RelayServerConfig } from "./config.js";
2
+ import { createRelayServer } from "./relay-server.js";
3
+ import { type RelayTunnelProvider } from "./tunnel.js";
4
+ export interface RelayCliOptions {
5
+ showHelp: boolean;
6
+ configOverrides: Partial<RelayServerConfig>;
7
+ publicTunnel?: RelayTunnelProvider;
8
+ }
9
+ type RelayServerInstance = ReturnType<typeof createRelayServer>;
10
+ interface StartedRelayServer {
11
+ relayServer: RelayServerInstance;
12
+ port: number;
13
+ url: string;
14
+ }
15
+ interface StartRelayServerWithPortFallbackOptions {
16
+ config: RelayServerConfig;
17
+ allowPortFallback: boolean;
18
+ maxAttempts?: number;
19
+ createRelayServerInstance?: typeof createRelayServer;
20
+ onLog?: (line: string) => void;
21
+ }
22
+ export declare function renderRelayCliHelp(): string;
23
+ export declare function parseRelayCliArgs(args: string[]): RelayCliOptions;
24
+ export declare function shouldAllowRelayCliPortFallback(parsed: RelayCliOptions, env?: NodeJS.ProcessEnv): boolean;
25
+ export declare function startRelayServerWithPortFallback(params: StartRelayServerWithPortFallbackOptions): Promise<StartedRelayServer>;
26
+ export declare function applyRelayCliOverrides(config: RelayServerConfig, overrides: Partial<RelayServerConfig>): RelayServerConfig;
27
+ export declare function runRelayCli(args: string[]): Promise<void>;
28
+ export {};
@@ -0,0 +1,256 @@
1
+ import { parseArgs } from "node:util";
2
+ import { RelayCliUserError } from "./cli-error.js";
3
+ import { loadRelayConfig } from "./config.js";
4
+ import { createRelayServer } from "./relay-server.js";
5
+ import { ensureRelayTunnelDependencyAvailable } from "./tunnel-installer.js";
6
+ import { isRelayTunnelProvider, MissingRelayTunnelBinaryError, openRelayTunnel, } from "./tunnel.js";
7
+ const DEFAULT_PORT_FALLBACK_ATTEMPTS = 50;
8
+ function parsePositiveIntegerFlag(value, label) {
9
+ if (!value || value.trim() === "") {
10
+ return undefined;
11
+ }
12
+ const parsed = Number.parseInt(value, 10);
13
+ if (!Number.isInteger(parsed) || parsed <= 0) {
14
+ throw new Error(`${label} must be a positive integer.`);
15
+ }
16
+ return parsed;
17
+ }
18
+ export function renderRelayCliHelp() {
19
+ return `privateclaw-relay [serve] [--host <host>] [--port <port>] [--ttl-ms <ms>] [--frame-cache-size <count>] [--redis-url <url>] [--public <tailscale|cloudflare>]
20
+
21
+ Start the local PrivateClaw relay server.
22
+
23
+ Examples:
24
+ privateclaw-relay
25
+ privateclaw-relay --port 8787 --public tailscale
26
+ privateclaw-relay serve --host 0.0.0.0 --redis-url redis://127.0.0.1:6379
27
+
28
+ Notes:
29
+ default local port automatically retries the next free port when 8787 is busy
30
+ --public tailscale enables Tailscale Funnel for the relay port
31
+ --public cloudflare starts a temporary Cloudflare quick tunnel
32
+ `;
33
+ }
34
+ export function parseRelayCliArgs(args) {
35
+ const parsed = parseArgs({
36
+ args,
37
+ allowPositionals: true,
38
+ options: {
39
+ host: { type: "string" },
40
+ port: { type: "string" },
41
+ "ttl-ms": { type: "string" },
42
+ "frame-cache-size": { type: "string" },
43
+ "redis-url": { type: "string" },
44
+ public: { type: "string" },
45
+ help: { type: "boolean", short: "h" },
46
+ },
47
+ });
48
+ const [command, ...rest] = parsed.positionals;
49
+ if (command === "help") {
50
+ return {
51
+ showHelp: true,
52
+ configOverrides: {},
53
+ };
54
+ }
55
+ if (command && command !== "serve") {
56
+ throw new Error(`Unsupported privateclaw-relay command: ${command}`);
57
+ }
58
+ if (rest.length > 0) {
59
+ throw new Error(`Unexpected extra arguments: ${rest.join(" ")}`);
60
+ }
61
+ if (parsed.values.help) {
62
+ return {
63
+ showHelp: true,
64
+ configOverrides: {},
65
+ };
66
+ }
67
+ const publicTunnelRaw = parsed.values.public?.trim();
68
+ if (publicTunnelRaw && !isRelayTunnelProvider(publicTunnelRaw)) {
69
+ throw new Error(`--public must be either "tailscale" or "cloudflare". Received: ${publicTunnelRaw}`);
70
+ }
71
+ const publicTunnel = publicTunnelRaw && isRelayTunnelProvider(publicTunnelRaw)
72
+ ? publicTunnelRaw
73
+ : undefined;
74
+ const port = parsePositiveIntegerFlag(parsed.values.port, "--port");
75
+ const sessionTtlMs = parsePositiveIntegerFlag(parsed.values["ttl-ms"], "--ttl-ms");
76
+ const frameCacheSize = parsePositiveIntegerFlag(parsed.values["frame-cache-size"], "--frame-cache-size");
77
+ return {
78
+ showHelp: false,
79
+ configOverrides: {
80
+ ...(parsed.values.host?.trim()
81
+ ? { host: parsed.values.host.trim() }
82
+ : {}),
83
+ ...(typeof port === "number" ? { port } : {}),
84
+ ...(typeof sessionTtlMs === "number" ? { sessionTtlMs } : {}),
85
+ ...(typeof frameCacheSize === "number" ? { frameCacheSize } : {}),
86
+ ...(parsed.values["redis-url"]?.trim()
87
+ ? { redisUrl: parsed.values["redis-url"].trim() }
88
+ : {}),
89
+ },
90
+ ...(publicTunnel ? { publicTunnel } : {}),
91
+ };
92
+ }
93
+ function isAddressInUseError(error) {
94
+ return (typeof error === "object" &&
95
+ error !== null &&
96
+ "code" in error &&
97
+ error.code === "EADDRINUSE");
98
+ }
99
+ export function shouldAllowRelayCliPortFallback(parsed, env = process.env) {
100
+ if (typeof parsed.configOverrides.port === "number") {
101
+ return false;
102
+ }
103
+ return !(env.PRIVATECLAW_RELAY_PORT?.trim() ||
104
+ env.PORT?.trim());
105
+ }
106
+ export async function startRelayServerWithPortFallback(params) {
107
+ const createRelayServerInstance = params.createRelayServerInstance ?? createRelayServer;
108
+ const maxAttempts = params.allowPortFallback
109
+ ? Math.max(1, params.maxAttempts ?? DEFAULT_PORT_FALLBACK_ATTEMPTS)
110
+ : 1;
111
+ let currentPort = params.config.port;
112
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
113
+ const relayServer = createRelayServerInstance({
114
+ ...params.config,
115
+ port: currentPort,
116
+ });
117
+ try {
118
+ const started = await relayServer.start();
119
+ if (attempt > 0) {
120
+ params.onLog?.(`[privateclaw-relay] port ${params.config.port} was unavailable, listening on ${started.port} instead.`);
121
+ }
122
+ return {
123
+ relayServer,
124
+ port: started.port,
125
+ url: started.url,
126
+ };
127
+ }
128
+ catch (error) {
129
+ await relayServer.stop();
130
+ if (!params.allowPortFallback ||
131
+ !isAddressInUseError(error) ||
132
+ currentPort >= 65_535 ||
133
+ attempt + 1 >= maxAttempts) {
134
+ throw error;
135
+ }
136
+ const nextPort = currentPort + 1;
137
+ params.onLog?.(`[privateclaw-relay] port ${currentPort} is unavailable, retrying on ${nextPort}.`);
138
+ currentPort = nextPort;
139
+ }
140
+ }
141
+ throw new Error("Relay CLI exhausted its local port fallback attempts.");
142
+ }
143
+ export function applyRelayCliOverrides(config, overrides) {
144
+ return {
145
+ ...config,
146
+ ...overrides,
147
+ };
148
+ }
149
+ export async function runRelayCli(args) {
150
+ let parsed;
151
+ try {
152
+ parsed = parseRelayCliArgs(args);
153
+ }
154
+ catch (error) {
155
+ if (error instanceof Error) {
156
+ throw new RelayCliUserError(error.message);
157
+ }
158
+ throw error;
159
+ }
160
+ if (parsed.showHelp) {
161
+ console.log(renderRelayCliHelp());
162
+ return;
163
+ }
164
+ const config = applyRelayCliOverrides(loadRelayConfig(), parsed.configOverrides);
165
+ const allowPortFallback = shouldAllowRelayCliPortFallback(parsed);
166
+ let relayServer;
167
+ let tunnel;
168
+ let shuttingDown = false;
169
+ async function shutdown(signal) {
170
+ if (shuttingDown) {
171
+ return;
172
+ }
173
+ shuttingDown = true;
174
+ console.log(`[privateclaw-relay] received ${signal}, shutting down`);
175
+ await tunnel?.close();
176
+ await relayServer?.stop();
177
+ process.exit(0);
178
+ }
179
+ process.on("SIGINT", () => {
180
+ void shutdown("SIGINT");
181
+ });
182
+ process.on("SIGTERM", () => {
183
+ void shutdown("SIGTERM");
184
+ });
185
+ try {
186
+ const startedRelay = await startRelayServerWithPortFallback({
187
+ config,
188
+ allowPortFallback,
189
+ onLog: (line) => {
190
+ console.log(line);
191
+ },
192
+ });
193
+ relayServer = startedRelay.relayServer;
194
+ const { port, url } = startedRelay;
195
+ console.log(`[privateclaw-relay] listening on ${url}`);
196
+ if (!parsed.publicTunnel) {
197
+ return;
198
+ }
199
+ console.log(`[privateclaw-relay] exposing the relay to the internet through ${parsed.publicTunnel}.`);
200
+ try {
201
+ tunnel = await openRelayTunnel({
202
+ provider: parsed.publicTunnel,
203
+ localPort: port,
204
+ localUrl: `http://127.0.0.1:${port}`,
205
+ onLog: (line) => {
206
+ console.log(line);
207
+ },
208
+ });
209
+ }
210
+ catch (error) {
211
+ if (parsed.publicTunnel &&
212
+ error instanceof MissingRelayTunnelBinaryError) {
213
+ await ensureRelayTunnelDependencyAvailable({
214
+ provider: parsed.publicTunnel,
215
+ missingDependency: error,
216
+ onLog: (line) => {
217
+ console.log(line);
218
+ },
219
+ });
220
+ console.log(`[privateclaw-relay] retrying ${parsed.publicTunnel} setup after installing ${error.command}.`);
221
+ try {
222
+ tunnel = await openRelayTunnel({
223
+ provider: parsed.publicTunnel,
224
+ localPort: port,
225
+ localUrl: `http://127.0.0.1:${port}`,
226
+ onLog: (line) => {
227
+ console.log(line);
228
+ },
229
+ });
230
+ }
231
+ catch (retryError) {
232
+ if (retryError instanceof MissingRelayTunnelBinaryError &&
233
+ retryError.command === error.command) {
234
+ throw new RelayCliUserError(`Installed \`${error.command}\`, but it is still unavailable in the current shell PATH. Open a new terminal and run \`privateclaw-relay --public ${parsed.publicTunnel}\` again.`);
235
+ }
236
+ throw retryError;
237
+ }
238
+ }
239
+ else {
240
+ throw error;
241
+ }
242
+ }
243
+ if (tunnel.publicUrl) {
244
+ console.log(`[privateclaw-relay] public URL: ${tunnel.publicUrl}`);
245
+ }
246
+ for (const note of tunnel.notes) {
247
+ console.log(`[privateclaw-relay] ${note}`);
248
+ }
249
+ }
250
+ catch (error) {
251
+ await tunnel?.close();
252
+ await relayServer?.stop();
253
+ throw error;
254
+ }
255
+ }
256
+ //# sourceMappingURL=relay-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-cli.js","sourceRoot":"","sources":["../src/relay-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ import type { EncryptedEnvelope } from "@privateclaw/protocol";
2
+ export interface RelayClusterAppBinding {
3
+ sessionId: string;
4
+ appId: string;
5
+ groupMode: boolean;
6
+ }
7
+ export interface RelayClusterPresenceRefresh {
8
+ providerIds: string[];
9
+ appBindings: RelayClusterAppBinding[];
10
+ }
11
+ export interface RelayClusterCallbacks {
12
+ onRemoteAppFrame(sessionId: string, envelope: EncryptedEnvelope, targetAppId?: string): Promise<void> | void;
13
+ onRemoteProviderFrame(sessionId: string, envelope: EncryptedEnvelope): Promise<void> | void;
14
+ onRemoteSessionClosed(sessionId: string, reason: string): Promise<void> | void;
15
+ onRemoteAppClosed(sessionId: string, appId: string, reason: string): Promise<void> | void;
16
+ onRemoteAppReconnected(sessionId: string, appId: string): Promise<void> | void;
17
+ onRemoteProviderReconnected(providerId: string): Promise<void> | void;
18
+ }
19
+ export interface RelayClusterClient {
20
+ readonly persistent: boolean;
21
+ claimProvider(providerId: string): Promise<{
22
+ previousNodeId?: string;
23
+ }>;
24
+ releaseProvider(providerId: string): Promise<void>;
25
+ claimApp(binding: RelayClusterAppBinding): Promise<{
26
+ previousNodeId?: string;
27
+ }>;
28
+ releaseApp(binding: RelayClusterAppBinding): Promise<void>;
29
+ refreshPresence(params: RelayClusterPresenceRefresh): Promise<void>;
30
+ subscribeProvider(providerId: string, sessionId: string): Promise<void>;
31
+ unsubscribeProvider(providerId: string, sessionId: string): Promise<void>;
32
+ subscribeApp(sessionId: string, appId: string): Promise<void>;
33
+ unsubscribeApp(sessionId: string, appId: string): Promise<void>;
34
+ publishFrameToApp(sessionId: string, envelope: EncryptedEnvelope, targetAppId?: string): Promise<number>;
35
+ publishFrameToProvider(sessionId: string, envelope: EncryptedEnvelope): Promise<number>;
36
+ publishSessionClosed(sessionId: string, reason: string): Promise<void>;
37
+ publishAppClosed(sessionId: string, appId: string, reason: string): Promise<void>;
38
+ publishAppReconnected(sessionId: string, appId: string, targetNodeId: string): Promise<void>;
39
+ publishProviderReconnected(providerId: string, targetNodeId: string): Promise<void>;
40
+ hasProviderSessionSubscriber(sessionId: string): Promise<boolean>;
41
+ hasAppBinding(sessionId: string, appId: string): Promise<boolean>;
42
+ close(): Promise<void>;
43
+ }
44
+ export declare class RelayClaimConflictError extends Error {
45
+ constructor(message: string);
46
+ }
47
+ declare abstract class BaseRelayClusterClient implements RelayClusterClient {
48
+ protected readonly nodeId: string;
49
+ protected readonly callbacks: RelayClusterCallbacks;
50
+ readonly persistent = true;
51
+ private readonly channelRefs;
52
+ protected constructor(nodeId: string, callbacks: RelayClusterCallbacks);
53
+ subscribeProvider(providerId: string, sessionId: string): Promise<void>;
54
+ unsubscribeProvider(providerId: string, sessionId: string): Promise<void>;
55
+ subscribeApp(sessionId: string, appId: string): Promise<void>;
56
+ unsubscribeApp(sessionId: string, appId: string): Promise<void>;
57
+ publishFrameToApp(sessionId: string, envelope: EncryptedEnvelope, targetAppId?: string): Promise<number>;
58
+ publishFrameToProvider(sessionId: string, envelope: EncryptedEnvelope): Promise<number>;
59
+ publishSessionClosed(sessionId: string, reason: string): Promise<void>;
60
+ publishAppClosed(sessionId: string, appId: string, reason: string): Promise<void>;
61
+ publishAppReconnected(sessionId: string, appId: string, targetNodeId: string): Promise<void>;
62
+ publishProviderReconnected(providerId: string, targetNodeId: string): Promise<void>;
63
+ protected handleChannelMessage(channel: string, payload: string): Promise<void>;
64
+ private retainChannel;
65
+ private releaseChannel;
66
+ protected abstract subscribeChannel(channel: string): Promise<void>;
67
+ protected abstract unsubscribeChannel(channel: string): Promise<void>;
68
+ protected abstract publish(channel: string, payload: string): Promise<number>;
69
+ abstract hasProviderSessionSubscriber(sessionId: string): Promise<boolean>;
70
+ abstract hasAppBinding(sessionId: string, appId: string): Promise<boolean>;
71
+ abstract claimProvider(providerId: string): Promise<{
72
+ previousNodeId?: string;
73
+ }>;
74
+ abstract releaseProvider(providerId: string): Promise<void>;
75
+ abstract claimApp(binding: RelayClusterAppBinding): Promise<{
76
+ previousNodeId?: string;
77
+ }>;
78
+ abstract releaseApp(binding: RelayClusterAppBinding): Promise<void>;
79
+ abstract refreshPresence(params: RelayClusterPresenceRefresh): Promise<void>;
80
+ abstract close(): Promise<void>;
81
+ }
82
+ interface InMemoryRelayClusterSharedState {
83
+ channelSubscribers: Map<string, Set<InMemoryRelayClusterClient>>;
84
+ providerPresence: Map<string, string>;
85
+ appPresence: Map<string, string>;
86
+ singleSessionOccupants: Map<string, string>;
87
+ }
88
+ export declare function createInMemoryRelayClusterSharedState(): InMemoryRelayClusterSharedState;
89
+ export declare class InMemoryRelayClusterClient extends BaseRelayClusterClient {
90
+ private readonly shared;
91
+ constructor(shared: InMemoryRelayClusterSharedState, params: {
92
+ nodeId: string;
93
+ callbacks: RelayClusterCallbacks;
94
+ });
95
+ claimProvider(providerId: string): Promise<{
96
+ previousNodeId?: string;
97
+ }>;
98
+ releaseProvider(providerId: string): Promise<void>;
99
+ claimApp(binding: RelayClusterAppBinding): Promise<{
100
+ previousNodeId?: string;
101
+ }>;
102
+ releaseApp(binding: RelayClusterAppBinding): Promise<void>;
103
+ refreshPresence(params: RelayClusterPresenceRefresh): Promise<void>;
104
+ protected subscribeChannel(channel: string): Promise<void>;
105
+ protected unsubscribeChannel(channel: string): Promise<void>;
106
+ protected publish(channel: string, payload: string): Promise<number>;
107
+ close(): Promise<void>;
108
+ hasAppBinding(sessionId: string, appId: string): Promise<boolean>;
109
+ hasProviderSessionSubscriber(sessionId: string): Promise<boolean>;
110
+ }
111
+ export declare class RedisRelayClusterClient extends BaseRelayClusterClient {
112
+ private readonly redis;
113
+ private readonly subscriber;
114
+ constructor(redisUrl: string, params: {
115
+ nodeId: string;
116
+ callbacks: RelayClusterCallbacks;
117
+ });
118
+ claimProvider(providerId: string): Promise<{
119
+ previousNodeId?: string;
120
+ }>;
121
+ releaseProvider(providerId: string): Promise<void>;
122
+ claimApp(binding: RelayClusterAppBinding): Promise<{
123
+ previousNodeId?: string;
124
+ }>;
125
+ releaseApp(binding: RelayClusterAppBinding): Promise<void>;
126
+ refreshPresence(params: RelayClusterPresenceRefresh): Promise<void>;
127
+ protected subscribeChannel(channel: string): Promise<void>;
128
+ protected unsubscribeChannel(channel: string): Promise<void>;
129
+ protected publish(channel: string, payload: string): Promise<number>;
130
+ close(): Promise<void>;
131
+ hasAppBinding(sessionId: string, appId: string): Promise<boolean>;
132
+ hasProviderSessionSubscriber(sessionId: string): Promise<boolean>;
133
+ }
134
+ export declare function createRedisRelayClusterClient(params: {
135
+ redisUrl: string;
136
+ nodeId: string;
137
+ callbacks: RelayClusterCallbacks;
138
+ }): RelayClusterClient;
139
+ export declare function createInMemoryRelayClusterClient(params: {
140
+ shared: InMemoryRelayClusterSharedState;
141
+ nodeId: string;
142
+ callbacks: RelayClusterCallbacks;
143
+ }): RelayClusterClient;
144
+ export {};