nexabase-console 2.1.2 → 2.1.3
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/app/NexaApp.d.ts +3 -0
- package/dist/app/NexaApp.js +38 -11
- package/dist/firestore/CollectionReference.d.ts +2 -2
- package/dist/firestore/CollectionReference.js +41 -2
- package/dist/firestore/DocumentReference.d.ts +1 -1
- package/dist/firestore/DocumentReference.js +41 -11
- package/dist/firestore/FieldPath.d.ts +2 -1
- package/dist/firestore/FieldPath.js +31 -4
- package/dist/firestore/FieldValue.d.ts +8 -4
- package/dist/firestore/FieldValue.js +59 -7
- package/dist/firestore/Firestore.js +16 -4
- package/dist/firestore/GeoPoint.d.ts +1 -0
- package/dist/firestore/GeoPoint.js +13 -6
- package/dist/firestore/Query.d.ts +5 -0
- package/dist/firestore/Query.js +190 -56
- package/dist/firestore/QueryUtils.d.ts +19 -2
- package/dist/firestore/QueryUtils.js +296 -58
- package/dist/firestore/Snapshot.js +62 -17
- package/dist/firestore/SnapshotManager.js +27 -12
- package/dist/firestore/Timestamp.d.ts +1 -0
- package/dist/firestore/Timestamp.js +28 -2
- package/dist/firestore/batch.js +89 -36
- package/dist/firestore/pathValidation.d.ts +17 -0
- package/dist/firestore/pathValidation.js +61 -0
- package/dist/firestore/queryConstraints.d.ts +4 -4
- package/dist/firestore/queryConstraints.js +69 -8
- package/dist/firestore/transaction.js +72 -18
- package/dist/firestore/writes.d.ts +1 -1
- package/dist/firestore/writes.js +32 -14
- package/dist/transport/WebSocketClient.d.ts +341 -9
- package/dist/transport/WebSocketClient.js +1895 -131
- package/dist/transport/WebSocketClient.test.d.ts +1 -0
- package/dist/transport/WebSocketClient.test.js +454 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/firestore/writes.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getPendingWritesCount = exports.deleteDoc = exports.updateDoc = exports.patchDoc = exports.setDoc = exports.FieldValue = void 0;
|
|
4
4
|
const helpers_1 = require("../utils/helpers");
|
|
5
|
+
const NexaError_1 = require("../errors/NexaError");
|
|
6
|
+
const QueryUtils_1 = require("./QueryUtils");
|
|
7
|
+
const pathValidation_1 = require("./pathValidation");
|
|
5
8
|
var FieldValue_1 = require("./FieldValue");
|
|
6
9
|
Object.defineProperty(exports, "FieldValue", { enumerable: true, get: function () { return FieldValue_1.FieldValue; } });
|
|
7
10
|
const normalizeError = (error) => {
|
|
@@ -17,6 +20,18 @@ const normalizeError = (error) => {
|
|
|
17
20
|
if (status) {
|
|
18
21
|
if ([408, 425, 429, 500, 502, 503, 504].includes(status))
|
|
19
22
|
retryable = true;
|
|
23
|
+
if (status === 401)
|
|
24
|
+
code = 'unauthenticated';
|
|
25
|
+
if (status === 403)
|
|
26
|
+
code = 'permission-denied';
|
|
27
|
+
if (status === 404)
|
|
28
|
+
code = 'not-found';
|
|
29
|
+
if (status === 409)
|
|
30
|
+
code = 'already-exists';
|
|
31
|
+
if (status === 400)
|
|
32
|
+
code = 'invalid-argument';
|
|
33
|
+
if (status === 412)
|
|
34
|
+
code = 'failed-precondition';
|
|
20
35
|
}
|
|
21
36
|
else if (['network-error', 'timeout', 'unavailable', 'aborted', 'resource-exhausted'].includes(code)) {
|
|
22
37
|
retryable = true;
|
|
@@ -27,7 +42,7 @@ const normalizeError = (error) => {
|
|
|
27
42
|
return {
|
|
28
43
|
code,
|
|
29
44
|
status,
|
|
30
|
-
message: error?.message || '
|
|
45
|
+
message: error?.message || 'Write operation failed',
|
|
31
46
|
retryable,
|
|
32
47
|
source: error?.isAxiosError ? 'http' : (error?.source || 'internal')
|
|
33
48
|
};
|
|
@@ -47,12 +62,16 @@ const safelyNotifyMutation = (db, path, action, data, metadata) => {
|
|
|
47
62
|
}
|
|
48
63
|
};
|
|
49
64
|
const executeWriteMutation = async (docRef, action, data, options) => {
|
|
50
|
-
if (!docRef || !docRef.path || !docRef.db
|
|
51
|
-
throw
|
|
65
|
+
if (!docRef || !docRef.path || !docRef.db) {
|
|
66
|
+
throw new NexaError_1.NexaError('invalid-argument', 'Invalid DocumentReference provided to write operation.');
|
|
52
67
|
}
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
(0, pathValidation_1.validateDocumentPath)(docRef.path);
|
|
69
|
+
if (action !== 'delete') {
|
|
70
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
71
|
+
throw new NexaError_1.NexaError('invalid-argument', 'Data for write operation must be a plain object.');
|
|
72
|
+
}
|
|
55
73
|
}
|
|
74
|
+
const clonedData = action !== 'delete' ? (0, QueryUtils_1.cloneNexaData)(data) : null;
|
|
56
75
|
const db = docRef.db;
|
|
57
76
|
if (db._initPromise)
|
|
58
77
|
await db._initPromise;
|
|
@@ -64,7 +83,7 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
64
83
|
id: mutationId,
|
|
65
84
|
type: action,
|
|
66
85
|
path: docRef.path,
|
|
67
|
-
data:
|
|
86
|
+
data: clonedData,
|
|
68
87
|
idempotencyKey: key,
|
|
69
88
|
merge: options?.merge,
|
|
70
89
|
state: 'pending',
|
|
@@ -84,7 +103,7 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
84
103
|
}
|
|
85
104
|
catch (error) {
|
|
86
105
|
db.unmarkInflight(docRef.path, mutationId);
|
|
87
|
-
throw
|
|
106
|
+
throw new NexaError_1.NexaError('internal', 'Local persistence failed', error);
|
|
88
107
|
}
|
|
89
108
|
safelyNotifyMutation(db, docRef.path, action, localView.data, {
|
|
90
109
|
mutationId,
|
|
@@ -106,7 +125,7 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
106
125
|
action: action === 'patch' ? 'update' : action,
|
|
107
126
|
docPath: docRef.path,
|
|
108
127
|
projectId: db.projectId,
|
|
109
|
-
data,
|
|
128
|
+
data: clonedData,
|
|
110
129
|
merge: options?.merge,
|
|
111
130
|
idempotencyKey: key,
|
|
112
131
|
preconditionExists: options?.preconditionExists
|
|
@@ -115,10 +134,10 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
115
134
|
else {
|
|
116
135
|
const headers = { 'X-Idempotency-Key': key };
|
|
117
136
|
if (action === 'set') {
|
|
118
|
-
res = (await db.client.post(`/api/firestore/${db.projectId}/document`, { docPath: docRef.path, data, idempotencyKey: key, merge: options?.merge }, { headers })).data;
|
|
137
|
+
res = (await db.client.post(`/api/firestore/${db.projectId}/document`, { docPath: docRef.path, data: clonedData, idempotencyKey: key, merge: options?.merge }, { headers })).data;
|
|
119
138
|
}
|
|
120
139
|
else if (action === 'patch') {
|
|
121
|
-
res = (await db.client.patch(`/api/firestore/${db.projectId}/document`, { docPath: docRef.path, data, idempotencyKey: key, preconditionExists: options?.preconditionExists }, { headers })).data;
|
|
140
|
+
res = (await db.client.patch(`/api/firestore/${db.projectId}/document`, { docPath: docRef.path, data: clonedData, idempotencyKey: key, preconditionExists: options?.preconditionExists }, { headers })).data;
|
|
122
141
|
}
|
|
123
142
|
else {
|
|
124
143
|
res = (await db.client.delete(`/api/firestore/${db.projectId}/document?docPath=${encodeURIComponent(docRef.path)}`, { headers })).data;
|
|
@@ -146,7 +165,7 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
146
165
|
}
|
|
147
166
|
catch (rejectionError) {
|
|
148
167
|
db.unmarkInflight(docRef.path, mutationId);
|
|
149
|
-
throw
|
|
168
|
+
throw new NexaError_1.NexaError(normErr.code, `Server rejected mutation, but local rollback failed: ${normErr.message}`, rejectionError);
|
|
150
169
|
}
|
|
151
170
|
db.unmarkInflight(docRef.path, mutationId);
|
|
152
171
|
safelyNotifyMutation(db, docRef.path, rebasedView.data ? (action === 'delete' ? 'set' : action) : 'delete', rebasedView.data, {
|
|
@@ -155,13 +174,12 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
155
174
|
state: 'rejected',
|
|
156
175
|
hasPendingWrites: rebasedView.hasPendingWrites
|
|
157
176
|
});
|
|
158
|
-
throw
|
|
177
|
+
throw new NexaError_1.NexaError(normErr.code, normErr.message, networkError);
|
|
159
178
|
}
|
|
160
179
|
}
|
|
161
180
|
// Phase 4: ACK Validation
|
|
162
181
|
const isValidAck = res && res.success;
|
|
163
182
|
if (!isValidAck) {
|
|
164
|
-
console.error('[NexaBase] Protocol error: Invalid server ACK:', res);
|
|
165
183
|
db.unmarkInflight(docRef.path, mutationId);
|
|
166
184
|
return { status: 'pending', committed: false, writtenLocally: true, mutationId };
|
|
167
185
|
}
|
|
@@ -174,7 +192,7 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
174
192
|
action,
|
|
175
193
|
serverDocument: res.document,
|
|
176
194
|
updateTime: res.document?.updateTime,
|
|
177
|
-
mutationData:
|
|
195
|
+
mutationData: clonedData,
|
|
178
196
|
merge: options?.merge
|
|
179
197
|
});
|
|
180
198
|
}
|
|
@@ -1,24 +1,356 @@
|
|
|
1
|
+
import { Socket } from 'socket.io-client';
|
|
2
|
+
/**
|
|
3
|
+
* Client operational lifecycle states.
|
|
4
|
+
*/
|
|
5
|
+
export type ClientState = 'idle' | 'electing' | 'follower' | 'leader' | 'disconnecting' | 'disposed';
|
|
6
|
+
/**
|
|
7
|
+
* Coordination mode across browser tabs.
|
|
8
|
+
*/
|
|
9
|
+
export type CoordinationMode = 'shared' | 'independent';
|
|
10
|
+
/**
|
|
11
|
+
* Standardized typed error codes for WebSocket and coordination failures.
|
|
12
|
+
*/
|
|
13
|
+
export type WebSocketErrorCode = 'invalid-argument' | 'connection-timeout' | 'timeout' | 'broadcast-failure' | 'leader-changed' | 'unauthenticated' | 'disconnected' | 'disposed' | 'ambiguous-outcome' | 'unavailable' | 'cancelled';
|
|
14
|
+
/**
|
|
15
|
+
* Serialized error format passed across leader/follower boundaries and to consumers.
|
|
16
|
+
*/
|
|
17
|
+
export interface SerializedError {
|
|
18
|
+
name: string;
|
|
19
|
+
message: string;
|
|
20
|
+
code: WebSocketErrorCode;
|
|
21
|
+
retryable: boolean;
|
|
22
|
+
source: 'client' | 'leader' | 'follower' | 'server';
|
|
23
|
+
isAmbiguous: boolean;
|
|
24
|
+
details?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Production error class preserving structured metadata across tab boundaries.
|
|
28
|
+
*/
|
|
29
|
+
export declare class NexaWebSocketError extends Error implements SerializedError {
|
|
30
|
+
readonly code: WebSocketErrorCode;
|
|
31
|
+
readonly retryable: boolean;
|
|
32
|
+
readonly source: 'client' | 'leader' | 'follower' | 'server';
|
|
33
|
+
readonly isAmbiguous: boolean;
|
|
34
|
+
readonly details?: unknown;
|
|
35
|
+
constructor(options: {
|
|
36
|
+
message: string;
|
|
37
|
+
code?: WebSocketErrorCode;
|
|
38
|
+
retryable?: boolean;
|
|
39
|
+
source?: 'client' | 'leader' | 'follower' | 'server';
|
|
40
|
+
isAmbiguous?: boolean;
|
|
41
|
+
details?: unknown;
|
|
42
|
+
cause?: unknown;
|
|
43
|
+
});
|
|
44
|
+
toJSON(): SerializedError;
|
|
45
|
+
static from(err: unknown, defaultSource?: 'client' | 'leader' | 'follower' | 'server'): NexaWebSocketError;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Tri-state path extraction outcome to prevent invalid paths from becoming global events.
|
|
49
|
+
*/
|
|
50
|
+
export type PathExtractionResult = {
|
|
51
|
+
status: 'none';
|
|
52
|
+
} | {
|
|
53
|
+
status: 'valid';
|
|
54
|
+
path: string;
|
|
55
|
+
} | {
|
|
56
|
+
status: 'invalid';
|
|
57
|
+
raw: unknown;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Options for configuring WebSocketClient.
|
|
61
|
+
*/
|
|
62
|
+
export interface WebSocketClientOptions {
|
|
63
|
+
sessionScope?: string;
|
|
64
|
+
authPayload?: unknown;
|
|
65
|
+
getAuthPayload?: () => Promise<unknown> | unknown;
|
|
66
|
+
defaultTimeoutMs?: number;
|
|
67
|
+
coordinationMode?: CoordinationMode;
|
|
68
|
+
followerTtlMs?: number;
|
|
69
|
+
heartbeatIntervalMs?: number;
|
|
70
|
+
pruneIntervalMs?: number;
|
|
71
|
+
leaderLeaseIntervalMs?: number;
|
|
72
|
+
socketFactory?: (url: string, opts: any) => Socket;
|
|
73
|
+
broadcastChannelFactory?: (name: string) => BroadcastChannel;
|
|
74
|
+
locks?: LockManager;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Valid broadcast message types.
|
|
78
|
+
*/
|
|
79
|
+
export type BroadcastMessageType = 'follower_subscribe' | 'follower_unsubscribe' | 'follower_sync_request' | 'follower_announce' | 'follower_heartbeat' | 'follower_disconnect' | 'follower_send' | 'leader_elected' | 'leader_state' | 'leader_event' | 'leader_send_response';
|
|
80
|
+
export interface BaseBroadcastMessage {
|
|
81
|
+
type: BroadcastMessageType;
|
|
82
|
+
senderId: string;
|
|
83
|
+
timestamp: number;
|
|
84
|
+
}
|
|
85
|
+
export interface FollowerSubscribeMessage extends BaseBroadcastMessage {
|
|
86
|
+
type: 'follower_subscribe';
|
|
87
|
+
payload: {
|
|
88
|
+
path: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export interface FollowerUnsubscribeMessage extends BaseBroadcastMessage {
|
|
92
|
+
type: 'follower_unsubscribe';
|
|
93
|
+
payload: {
|
|
94
|
+
path: string;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export interface FollowerSyncRequestMessage extends BaseBroadcastMessage {
|
|
98
|
+
type: 'follower_sync_request';
|
|
99
|
+
payload?: Record<string, never>;
|
|
100
|
+
}
|
|
101
|
+
export interface FollowerAnnounceMessage extends BaseBroadcastMessage {
|
|
102
|
+
type: 'follower_announce';
|
|
103
|
+
payload: {
|
|
104
|
+
paths: string[];
|
|
105
|
+
globalListenersCount: number;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export interface FollowerHeartbeatMessage extends BaseBroadcastMessage {
|
|
109
|
+
type: 'follower_heartbeat';
|
|
110
|
+
payload: {
|
|
111
|
+
globalListenersCount: number;
|
|
112
|
+
paths?: string[];
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export interface FollowerDisconnectMessage extends BaseBroadcastMessage {
|
|
116
|
+
type: 'follower_disconnect';
|
|
117
|
+
payload?: {
|
|
118
|
+
tabId?: string;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export interface FollowerSendMessage extends BaseBroadcastMessage {
|
|
122
|
+
type: 'follower_send';
|
|
123
|
+
payload: {
|
|
124
|
+
requestId: string;
|
|
125
|
+
expectedLeaderId: string;
|
|
126
|
+
expectedElectionId: string;
|
|
127
|
+
event: string;
|
|
128
|
+
data: unknown;
|
|
129
|
+
targetTabId: string;
|
|
130
|
+
deadline: number;
|
|
131
|
+
idempotencyKey?: string;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export interface LeaderElectedMessage extends BaseBroadcastMessage {
|
|
135
|
+
type: 'leader_elected';
|
|
136
|
+
leaderId: string;
|
|
137
|
+
electionId: string;
|
|
138
|
+
termSeq: number;
|
|
139
|
+
payload: {
|
|
140
|
+
leaderId: string;
|
|
141
|
+
electionId: string;
|
|
142
|
+
isConnected: boolean;
|
|
143
|
+
termSeq: number;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export interface LeaderStateMessage extends BaseBroadcastMessage {
|
|
147
|
+
type: 'leader_state';
|
|
148
|
+
leaderId: string;
|
|
149
|
+
electionId: string;
|
|
150
|
+
termSeq: number;
|
|
151
|
+
payload: {
|
|
152
|
+
leaderId: string;
|
|
153
|
+
electionId: string;
|
|
154
|
+
isConnected: boolean;
|
|
155
|
+
termSeq: number;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export interface LeaderEventMessage extends BaseBroadcastMessage {
|
|
159
|
+
type: 'leader_event';
|
|
160
|
+
leaderId: string;
|
|
161
|
+
electionId: string;
|
|
162
|
+
payload: {
|
|
163
|
+
event: string;
|
|
164
|
+
data: unknown;
|
|
165
|
+
targetTabIds?: string[];
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export interface LeaderSendResponseMessage extends BaseBroadcastMessage {
|
|
169
|
+
type: 'leader_send_response';
|
|
170
|
+
leaderId: string;
|
|
171
|
+
electionId: string;
|
|
172
|
+
payload: {
|
|
173
|
+
requestId: string;
|
|
174
|
+
targetTabId: string;
|
|
175
|
+
response?: unknown;
|
|
176
|
+
error?: SerializedError;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export type BroadcastMessage = FollowerSubscribeMessage | FollowerUnsubscribeMessage | FollowerSyncRequestMessage | FollowerAnnounceMessage | FollowerHeartbeatMessage | FollowerDisconnectMessage | FollowerSendMessage | LeaderElectedMessage | LeaderStateMessage | LeaderEventMessage | LeaderSendResponseMessage;
|
|
180
|
+
/**
|
|
181
|
+
* Production-hardened WebSocketClient with distributed coordination,
|
|
182
|
+
* term-fenced follower forwarding, BFCache/background resilience, and strict invariants.
|
|
183
|
+
*/
|
|
1
184
|
export declare class WebSocketClient {
|
|
2
|
-
private
|
|
3
|
-
private
|
|
4
|
-
private
|
|
5
|
-
|
|
185
|
+
private readonly _projectId;
|
|
186
|
+
private readonly _serverUrl;
|
|
187
|
+
private readonly _sessionScope;
|
|
188
|
+
private readonly _tabId;
|
|
189
|
+
private readonly _options;
|
|
190
|
+
private readonly _coordinationKey;
|
|
191
|
+
private _coordinationMode;
|
|
192
|
+
private _socket;
|
|
193
|
+
private _isConnected;
|
|
194
|
+
private _lastReportedConnected;
|
|
195
|
+
private _state;
|
|
196
|
+
private _isSuspended;
|
|
197
|
+
private _authPayload;
|
|
198
|
+
private _electionId;
|
|
199
|
+
private _currentLeaderId;
|
|
200
|
+
private _currentElectionId;
|
|
201
|
+
private _currentTermSeq;
|
|
202
|
+
private _isLeaderSocketConnected;
|
|
6
203
|
private listeners;
|
|
7
|
-
private
|
|
204
|
+
private localSubscribedPaths;
|
|
205
|
+
private activeLocalSendsCount;
|
|
8
206
|
private broadcastChannel;
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
|
|
207
|
+
private lockAbortController;
|
|
208
|
+
private resolveLeaderLock;
|
|
209
|
+
private followerSubscriptions;
|
|
210
|
+
private followerSubscriptionsByTab;
|
|
211
|
+
private followerGlobalNeeds;
|
|
212
|
+
private processedFollowerRequests;
|
|
213
|
+
private pendingSendRequests;
|
|
214
|
+
private activeLeaderSends;
|
|
215
|
+
private readinessWaiters;
|
|
216
|
+
private heartbeatTimer;
|
|
217
|
+
private pruneTimer;
|
|
218
|
+
private leaseTimer;
|
|
219
|
+
private reconnectTimer;
|
|
220
|
+
private pageLifecycleCleanup;
|
|
221
|
+
private readonly followerTtlMs;
|
|
222
|
+
private readonly heartbeatIntervalMs;
|
|
223
|
+
private readonly pruneIntervalMs;
|
|
224
|
+
private readonly leaderLeaseIntervalMs;
|
|
225
|
+
private readonly defaultTimeoutMs;
|
|
226
|
+
private static readonly DEFAULT_FOLLOWER_TTL;
|
|
227
|
+
private static readonly DEFAULT_HEARTBEAT_INTERVAL;
|
|
228
|
+
private static readonly DEFAULT_PRUNE_INTERVAL;
|
|
229
|
+
private static readonly DEFAULT_LEADER_LEASE_INTERVAL;
|
|
230
|
+
private static readonly DEFAULT_TIMEOUT_MS;
|
|
231
|
+
private static readonly MAX_DEDUP_CACHE_SIZE;
|
|
232
|
+
private static readonly DEDUP_CACHE_TTL_MS;
|
|
233
|
+
private static readonly RESERVED_SEND_EVENTS;
|
|
234
|
+
constructor(projectId: string, serverUrl: string, options?: WebSocketClientOptions);
|
|
235
|
+
get projectId(): string;
|
|
236
|
+
get serverUrl(): string;
|
|
237
|
+
get tabId(): string;
|
|
238
|
+
get isConnected(): boolean;
|
|
239
|
+
get isLeader(): boolean;
|
|
240
|
+
get state(): ClientState;
|
|
241
|
+
get electionId(): string | null;
|
|
242
|
+
get currentLeaderId(): string | null;
|
|
243
|
+
get currentElectionId(): string | null;
|
|
244
|
+
get isIndependent(): boolean;
|
|
245
|
+
get coordinationMode(): CoordinationMode;
|
|
246
|
+
get isSuspended(): boolean;
|
|
247
|
+
/**
|
|
248
|
+
* Unique ID generation with Web Crypto fallback.
|
|
249
|
+
*/
|
|
250
|
+
static generateId(): string;
|
|
251
|
+
/**
|
|
252
|
+
* Stable deterministic hashing function.
|
|
253
|
+
*/
|
|
254
|
+
static hashScope(scope: string): string;
|
|
255
|
+
/**
|
|
256
|
+
* Normalizes server URL and creates a unique coordination key.
|
|
257
|
+
*/
|
|
258
|
+
static deriveCoordinationKey(projectId: string, serverUrl: string, sessionScope: string | null): string;
|
|
259
|
+
/**
|
|
260
|
+
* Sanitizes, segments, and bounds path strings.
|
|
261
|
+
*/
|
|
262
|
+
static cleanAndValidatePath(path: unknown): string | null;
|
|
263
|
+
static validatePathOrThrow(path: unknown): string;
|
|
264
|
+
/**
|
|
265
|
+
* Segment-aware path matcher: checks exact match or segment hierarchy.
|
|
266
|
+
*/
|
|
267
|
+
static isPathMatch(eventPath: string, subPath: string): boolean;
|
|
268
|
+
/**
|
|
269
|
+
* Tri-state path extraction distinguishing: none vs valid vs invalid.
|
|
270
|
+
*/
|
|
271
|
+
static extractPayloadPathTriState(payload: unknown): PathExtractionResult;
|
|
272
|
+
/**
|
|
273
|
+
* Backward-compatible extractor returning valid path or null.
|
|
274
|
+
*/
|
|
275
|
+
static extractPayloadPath(payload: unknown): string | null;
|
|
276
|
+
/**
|
|
277
|
+
* Dynamic authentication updater.
|
|
278
|
+
*/
|
|
279
|
+
updateAuth(authPayload: unknown): void;
|
|
280
|
+
private getFreshAuthPayload;
|
|
281
|
+
private transitionTo;
|
|
282
|
+
/**
|
|
283
|
+
* Atomically downgrades from shared coordination to independent socket mode.
|
|
284
|
+
*/
|
|
285
|
+
private switchToIndependentMode;
|
|
286
|
+
/**
|
|
287
|
+
* Safe non-throwing broadcast poster returning a delivery indicator boolean.
|
|
288
|
+
*/
|
|
289
|
+
private postBroadcast;
|
|
290
|
+
/**
|
|
291
|
+
* Strictly validates incoming broadcast payload schema, types, bounds, and string sizes.
|
|
292
|
+
*/
|
|
293
|
+
validateBroadcastMessage(raw: unknown): BroadcastMessage | null;
|
|
12
294
|
private handleBroadcastMessage;
|
|
295
|
+
private handleMessageAsLeader;
|
|
296
|
+
private pruneDedupCache;
|
|
297
|
+
private handleFollowerSendRequest;
|
|
298
|
+
private handleMessageAsFollower;
|
|
299
|
+
private updateFollowerConnectionState;
|
|
300
|
+
private handleFollowerAnnounce;
|
|
301
|
+
private announceNeedsToLeader;
|
|
302
|
+
private announceGlobalListenersChange;
|
|
303
|
+
private getGlobalListenersCount;
|
|
304
|
+
private addFollowerPathSubscription;
|
|
305
|
+
private removeFollowerPathSubscription;
|
|
306
|
+
private pruneFollower;
|
|
307
|
+
private startFollowerHeartbeat;
|
|
308
|
+
private stopFollowerHeartbeat;
|
|
309
|
+
private startLeaderPruneTimer;
|
|
310
|
+
private stopLeaderPruneTimer;
|
|
311
|
+
private startLeaderLeaseTimer;
|
|
312
|
+
private stopLeaderLeaseTimer;
|
|
313
|
+
/**
|
|
314
|
+
* Subscribes an event listener callback with reference counting and connection management.
|
|
315
|
+
*/
|
|
13
316
|
addListener(event: string, callback: (data: any) => void): () => void;
|
|
317
|
+
/**
|
|
318
|
+
* Reference-counted path subscription.
|
|
319
|
+
*/
|
|
14
320
|
subscribe(path: string): void;
|
|
321
|
+
/**
|
|
322
|
+
* Decrements reference-counted path subscription.
|
|
323
|
+
*/
|
|
15
324
|
unsubscribe(path: string): void;
|
|
16
325
|
private broadcastToFollowers;
|
|
326
|
+
/**
|
|
327
|
+
* Connects socket directly or coordinates election via Web Locks.
|
|
328
|
+
*/
|
|
17
329
|
ensureConnected(): void;
|
|
18
330
|
private startSocketConnection;
|
|
19
|
-
|
|
331
|
+
private isLocalSubscribedToPath;
|
|
332
|
+
private handleSocketDataEvent;
|
|
333
|
+
private executeSendAsLeader;
|
|
334
|
+
private checkReadinessWaiters;
|
|
335
|
+
private waitForReady;
|
|
336
|
+
private static isMutatingEvent;
|
|
337
|
+
/**
|
|
338
|
+
* Production-hardened request execution with invariant counter handling in a single finally block.
|
|
339
|
+
*/
|
|
340
|
+
send(event: string, data: any, timeoutMs?: number): Promise<any>;
|
|
20
341
|
checkCloseConnection(): void;
|
|
342
|
+
hasNetworkNeeds(): boolean;
|
|
343
|
+
private hasLocalNeeds;
|
|
21
344
|
hasListeners(): boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Dispatches events using a defensive snapshot of listeners to avoid mutation hazards.
|
|
347
|
+
*/
|
|
22
348
|
dispatch(event: string, data: any): void;
|
|
349
|
+
private releaseLeaderLock;
|
|
350
|
+
private disconnectSocket;
|
|
351
|
+
private setupPageLifecycleListeners;
|
|
352
|
+
/**
|
|
353
|
+
* Idempotent client teardown releasing locks, clearing queues, and sending best-effort disconnect notice.
|
|
354
|
+
*/
|
|
23
355
|
close(): void;
|
|
24
356
|
}
|