@csntgao/ai-employee-client 0.15.7 → 0.16.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/index.d.ts +169 -2
- package/dist/index.js +663 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -221,9 +221,9 @@ function extractText(raw) {
|
|
|
221
221
|
try {
|
|
222
222
|
const parsed = JSON.parse(value);
|
|
223
223
|
if (typeof parsed === "string") return parsed.trim();
|
|
224
|
-
const
|
|
224
|
+
const record4 = asRecord(parsed);
|
|
225
225
|
for (const key of ["text", "result", "message", "content"]) {
|
|
226
|
-
const candidate = stringValue(
|
|
226
|
+
const candidate = stringValue(record4[key]).trim();
|
|
227
227
|
if (candidate) return candidate;
|
|
228
228
|
}
|
|
229
229
|
} catch {
|
|
@@ -941,9 +941,9 @@ var AiEmployeeClient = class {
|
|
|
941
941
|
for (const streamEvent of event.result.streamEvents) {
|
|
942
942
|
this.applyAssistantStreamEvent(event.sessionId, streamEvent);
|
|
943
943
|
}
|
|
944
|
-
for (const
|
|
944
|
+
for (const text3 of event.result.activities) {
|
|
945
945
|
if (!this.assistantReceivedInCurrentTurn) {
|
|
946
|
-
this.emit("work-activity", { sessionId: event.sessionId, text:
|
|
946
|
+
this.emit("work-activity", { sessionId: event.sessionId, text: text3 });
|
|
947
947
|
}
|
|
948
948
|
}
|
|
949
949
|
for (const message of event.result.messages) {
|
|
@@ -1085,20 +1085,20 @@ var AiEmployeeClient = class {
|
|
|
1085
1085
|
if (!this.isCurrentSocket(socket, generation)) return;
|
|
1086
1086
|
this.socket = null;
|
|
1087
1087
|
this.clearHeartbeatTimer();
|
|
1088
|
-
const
|
|
1088
|
+
const authenticationFailure4 = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
|
|
1089
1089
|
this.socketAuthenticationFailure = null;
|
|
1090
1090
|
if (!this.shouldReconnect || this.destroyed) {
|
|
1091
1091
|
if (!this.destroyed) this.setState("disconnected");
|
|
1092
1092
|
return;
|
|
1093
1093
|
}
|
|
1094
|
-
if (
|
|
1094
|
+
if (authenticationFailure4?.retryMode === "never") {
|
|
1095
1095
|
this.shouldReconnect = false;
|
|
1096
1096
|
this.setState("disconnected");
|
|
1097
1097
|
return;
|
|
1098
1098
|
}
|
|
1099
1099
|
if (code === 4400 || code === 4403) {
|
|
1100
1100
|
this.shouldReconnect = false;
|
|
1101
|
-
if (!
|
|
1101
|
+
if (!authenticationFailure4) {
|
|
1102
1102
|
const errorCode = code === 4403 ? "AUTHENTICATION_FAILED" : "PROTOCOL_ERROR";
|
|
1103
1103
|
const message = code === 4403 ? "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u5EFA\u7ACB\u5B9E\u65F6\u8FDE\u63A5\u3002" : "\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002";
|
|
1104
1104
|
this.emitError(errorCode, message, false);
|
|
@@ -1106,15 +1106,15 @@ var AiEmployeeClient = class {
|
|
|
1106
1106
|
this.setState("disconnected");
|
|
1107
1107
|
return;
|
|
1108
1108
|
}
|
|
1109
|
-
if (
|
|
1110
|
-
if (!
|
|
1109
|
+
if (authenticationFailure4?.retryMode === "availability" || code === 1013) {
|
|
1110
|
+
if (!authenticationFailure4) {
|
|
1111
1111
|
this.emitError("REALTIME_UNAVAILABLE", "\u5B9E\u65F6\u9274\u6743\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002", true);
|
|
1112
1112
|
}
|
|
1113
1113
|
this.scheduleAvailabilityReconnect();
|
|
1114
1114
|
return;
|
|
1115
1115
|
}
|
|
1116
1116
|
if (code === 4401) {
|
|
1117
|
-
if (!
|
|
1117
|
+
if (!authenticationFailure4) {
|
|
1118
1118
|
this.emitError("AUTHENTICATION_FAILED", "\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u3002", true);
|
|
1119
1119
|
}
|
|
1120
1120
|
}
|
|
@@ -2070,13 +2070,13 @@ var CustomerSupportClient = class {
|
|
|
2070
2070
|
this.socket = null;
|
|
2071
2071
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
2072
2072
|
this.heartbeatTimer = null;
|
|
2073
|
-
const
|
|
2073
|
+
const authenticationFailure4 = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
|
|
2074
2074
|
this.socketAuthenticationFailure = null;
|
|
2075
2075
|
if (!this.shouldReconnect || this.destroyed) {
|
|
2076
2076
|
if (!this.destroyed) this.setState("disconnected");
|
|
2077
2077
|
return;
|
|
2078
2078
|
}
|
|
2079
|
-
if (
|
|
2079
|
+
if (authenticationFailure4?.retryMode === "never") {
|
|
2080
2080
|
this.shouldReconnect = false;
|
|
2081
2081
|
this.stopFallbackHistoryRefresh();
|
|
2082
2082
|
this.setState("disconnected");
|
|
@@ -2085,7 +2085,7 @@ var CustomerSupportClient = class {
|
|
|
2085
2085
|
if (code === 4400 || code === 4403) {
|
|
2086
2086
|
this.shouldReconnect = false;
|
|
2087
2087
|
this.stopFallbackHistoryRefresh();
|
|
2088
|
-
if (!
|
|
2088
|
+
if (!authenticationFailure4) {
|
|
2089
2089
|
this.emitError(
|
|
2090
2090
|
code === 4400 ? "PROTOCOL_ERROR" : "AUTHENTICATION_FAILED",
|
|
2091
2091
|
code === 4400 ? "\u5BA2\u670D\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002" : "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u4F7F\u7528\u4EBA\u5DE5\u5BA2\u670D\u3002",
|
|
@@ -2095,15 +2095,15 @@ var CustomerSupportClient = class {
|
|
|
2095
2095
|
this.setState("disconnected");
|
|
2096
2096
|
return;
|
|
2097
2097
|
}
|
|
2098
|
-
if (
|
|
2099
|
-
if (!
|
|
2098
|
+
if (authenticationFailure4?.retryMode === "availability" || code === 1013) {
|
|
2099
|
+
if (!authenticationFailure4) {
|
|
2100
2100
|
this.emitError("REALTIME_UNAVAILABLE", "\u5BA2\u670D\u5B9E\u65F6\u9274\u6743\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002", true);
|
|
2101
2101
|
}
|
|
2102
2102
|
this.scheduleAvailabilityReconnect();
|
|
2103
2103
|
return;
|
|
2104
2104
|
}
|
|
2105
|
-
if (
|
|
2106
|
-
if (!
|
|
2105
|
+
if (authenticationFailure4?.retryMode === "token" || code === 4401) {
|
|
2106
|
+
if (!authenticationFailure4) {
|
|
2107
2107
|
this.emitError("AUTHENTICATION_FAILED", "\u5BA2\u670D\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u3002", true);
|
|
2108
2108
|
}
|
|
2109
2109
|
if (this.tokenReconnectAttempt >= MAX_TOKEN_RECONNECTS) {
|
|
@@ -2465,6 +2465,7 @@ var NotificationCenterClient = class {
|
|
|
2465
2465
|
this.applyRead([id], result.readAt);
|
|
2466
2466
|
this.setUnreadCount(result.unreadCount);
|
|
2467
2467
|
this.emit("notification-read", { notificationId: id });
|
|
2468
|
+
if (this.readStatus === "unread") this.resyncUnreadInbox();
|
|
2468
2469
|
} catch {
|
|
2469
2470
|
this.emitError("MARK_READ_FAILED", "\u901A\u77E5\u6807\u8BB0\u5DF2\u8BFB\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002", true);
|
|
2470
2471
|
}
|
|
@@ -2610,6 +2611,7 @@ var NotificationCenterClient = class {
|
|
|
2610
2611
|
if (change.kind === "read") {
|
|
2611
2612
|
this.applyRead(change.notificationIds, change.readAt ?? null);
|
|
2612
2613
|
this.syncRealtimeUnreadCount(change.unreadCount);
|
|
2614
|
+
if (this.readStatus === "unread") this.resyncUnreadInbox();
|
|
2613
2615
|
return;
|
|
2614
2616
|
}
|
|
2615
2617
|
const removed = new Set(change.notificationIds);
|
|
@@ -2621,6 +2623,15 @@ var NotificationCenterClient = class {
|
|
|
2621
2623
|
}
|
|
2622
2624
|
}
|
|
2623
2625
|
this.syncRealtimeUnreadCount(change.unreadCount);
|
|
2626
|
+
if (this.readStatus === "unread") this.resyncUnreadInbox();
|
|
2627
|
+
}
|
|
2628
|
+
resyncUnreadInbox() {
|
|
2629
|
+
if (this.destroyed) return;
|
|
2630
|
+
if (this.loadingInbox) {
|
|
2631
|
+
this.pendingResync = true;
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
void this.reloadInbox();
|
|
2624
2635
|
}
|
|
2625
2636
|
syncRealtimeUnreadCount(value) {
|
|
2626
2637
|
if (this.applicationName && typeof value === "number") this.setUnreadCount(value);
|
|
@@ -2684,6 +2695,640 @@ function normalizedText2(value) {
|
|
|
2684
2695
|
return typeof value === "string" ? value.trim() : "";
|
|
2685
2696
|
}
|
|
2686
2697
|
|
|
2687
|
-
|
|
2698
|
+
// src/user-realtime-client.ts
|
|
2699
|
+
var DEFAULT_RECONNECT3 = {
|
|
2700
|
+
initialDelayMs: 1e3,
|
|
2701
|
+
maximumDelayMs: 3e4,
|
|
2702
|
+
factor: 2,
|
|
2703
|
+
jitter: 0.2
|
|
2704
|
+
};
|
|
2705
|
+
var DEFAULT_MAXIMUM_SEEN_EVENT_IDS = 2e3;
|
|
2706
|
+
var DEFAULT_HEARTBEAT_INTERVAL_MS = 25e3;
|
|
2707
|
+
var CHANNEL_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
2708
|
+
var CURSOR_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/;
|
|
2709
|
+
var UserRealtimeClientError = class extends Error {
|
|
2710
|
+
code;
|
|
2711
|
+
constructor(code, message) {
|
|
2712
|
+
super(message);
|
|
2713
|
+
this.name = "UserRealtimeClientError";
|
|
2714
|
+
this.code = code;
|
|
2715
|
+
}
|
|
2716
|
+
};
|
|
2717
|
+
var UserRealtimeClient = class {
|
|
2718
|
+
requestedChannels;
|
|
2719
|
+
realtimeUrl;
|
|
2720
|
+
getAccessToken;
|
|
2721
|
+
cursorStore;
|
|
2722
|
+
reconnectOptions;
|
|
2723
|
+
webSocketFactory;
|
|
2724
|
+
minimumHeartbeatMs;
|
|
2725
|
+
maximumSeenEventIds;
|
|
2726
|
+
listeners = /* @__PURE__ */ new Map();
|
|
2727
|
+
cursors = /* @__PURE__ */ new Map();
|
|
2728
|
+
cursorWriteChains = /* @__PURE__ */ new Map();
|
|
2729
|
+
seenEventIds = /* @__PURE__ */ new Map();
|
|
2730
|
+
channelSequences = /* @__PURE__ */ new Map();
|
|
2731
|
+
pendingResyncs = /* @__PURE__ */ new Map();
|
|
2732
|
+
resyncGenerations = /* @__PURE__ */ new Map();
|
|
2733
|
+
socket = null;
|
|
2734
|
+
messageChain = Promise.resolve();
|
|
2735
|
+
reconnectTimer = null;
|
|
2736
|
+
heartbeatTimer = null;
|
|
2737
|
+
startPromise = null;
|
|
2738
|
+
currentState = "idle";
|
|
2739
|
+
socketGeneration = 0;
|
|
2740
|
+
lifecycleGeneration = 0;
|
|
2741
|
+
reconnectAttempt = 0;
|
|
2742
|
+
deliverySequence = 0;
|
|
2743
|
+
shouldReconnect = false;
|
|
2744
|
+
destroyed = false;
|
|
2745
|
+
socketAuthenticationFailure = null;
|
|
2746
|
+
constructor(options) {
|
|
2747
|
+
if (!options || typeof options !== "object") {
|
|
2748
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "\u7F3A\u5C11\u7EDF\u4E00\u5B9E\u65F6\u5BA2\u6237\u7AEF\u914D\u7F6E\u3002");
|
|
2749
|
+
}
|
|
2750
|
+
this.realtimeUrl = validateRealtimeUrl3(options.realtimeUrl);
|
|
2751
|
+
if (typeof options.getAccessToken !== "function") {
|
|
2752
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "\u7F3A\u5C11\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u63D0\u4F9B\u5668\u3002");
|
|
2753
|
+
}
|
|
2754
|
+
this.getAccessToken = options.getAccessToken;
|
|
2755
|
+
this.requestedChannels = normalizeChannels(options.requestedChannels);
|
|
2756
|
+
if (options.cursorStore !== void 0 && options.cursorStore !== null && (typeof options.cursorStore.load !== "function" || typeof options.cursorStore.save !== "function" || typeof options.cursorStore.clear !== "function")) {
|
|
2757
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "cursorStore \u914D\u7F6E\u65E0\u6548\u3002");
|
|
2758
|
+
}
|
|
2759
|
+
this.cursorStore = options.cursorStore ?? null;
|
|
2760
|
+
this.reconnectOptions = normalizeReconnect2(options.reconnect);
|
|
2761
|
+
this.minimumHeartbeatMs = positiveNumber2(options.minimumHeartbeatMs, 5e3);
|
|
2762
|
+
this.maximumSeenEventIds = positiveInteger(
|
|
2763
|
+
options.maximumSeenEventIds,
|
|
2764
|
+
DEFAULT_MAXIMUM_SEEN_EVENT_IDS
|
|
2765
|
+
);
|
|
2766
|
+
this.webSocketFactory = options.webSocketFactory ?? ((url) => {
|
|
2767
|
+
if (typeof WebSocket === "undefined") {
|
|
2768
|
+
throw new UserRealtimeClientError(
|
|
2769
|
+
"WEBSOCKET_UNAVAILABLE",
|
|
2770
|
+
"\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002"
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
return new WebSocket(url);
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
get connectionState() {
|
|
2777
|
+
return this.currentState;
|
|
2778
|
+
}
|
|
2779
|
+
get eventSequence() {
|
|
2780
|
+
return this.deliverySequence;
|
|
2781
|
+
}
|
|
2782
|
+
get isDestroyed() {
|
|
2783
|
+
return this.destroyed;
|
|
2784
|
+
}
|
|
2785
|
+
on(type, listener) {
|
|
2786
|
+
this.assertUsable();
|
|
2787
|
+
const listeners = this.listeners.get(type) ?? /* @__PURE__ */ new Set();
|
|
2788
|
+
const untyped = listener;
|
|
2789
|
+
listeners.add(untyped);
|
|
2790
|
+
this.listeners.set(type, listeners);
|
|
2791
|
+
return () => listeners.delete(untyped);
|
|
2792
|
+
}
|
|
2793
|
+
start() {
|
|
2794
|
+
this.assertUsable();
|
|
2795
|
+
if (this.shouldReconnect) return this.startPromise ?? Promise.resolve();
|
|
2796
|
+
this.shouldReconnect = true;
|
|
2797
|
+
this.reconnectAttempt = 0;
|
|
2798
|
+
const lifecycle = ++this.lifecycleGeneration;
|
|
2799
|
+
this.setState("connecting");
|
|
2800
|
+
const pending = this.loadCursors(lifecycle).then(() => {
|
|
2801
|
+
if (!this.isCurrentLifecycle(lifecycle) || !this.shouldReconnect) return;
|
|
2802
|
+
this.openSocket(false);
|
|
2803
|
+
});
|
|
2804
|
+
this.startPromise = pending;
|
|
2805
|
+
const clear = () => {
|
|
2806
|
+
if (this.startPromise === pending) this.startPromise = null;
|
|
2807
|
+
};
|
|
2808
|
+
void pending.then(clear, clear);
|
|
2809
|
+
return pending;
|
|
2810
|
+
}
|
|
2811
|
+
stop() {
|
|
2812
|
+
if (this.destroyed) return;
|
|
2813
|
+
this.stopConnection();
|
|
2814
|
+
this.setState("stopped");
|
|
2815
|
+
}
|
|
2816
|
+
async restart(options = {}) {
|
|
2817
|
+
this.assertUsable();
|
|
2818
|
+
this.stopConnection();
|
|
2819
|
+
this.clearDeliveryState();
|
|
2820
|
+
if (options.clearCursors) await this.clearCursors();
|
|
2821
|
+
await this.start();
|
|
2822
|
+
}
|
|
2823
|
+
async clearCursors() {
|
|
2824
|
+
this.assertUsable();
|
|
2825
|
+
this.cursors.clear();
|
|
2826
|
+
this.pendingResyncs.clear();
|
|
2827
|
+
await Promise.all(this.requestedChannels.map((channel) => this.clearStoredCursor(channel)));
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Confirm that Host HTTP state was restored for a previously emitted resync signal.
|
|
2831
|
+
* Stale confirmations never overwrite a cursor advanced by a newer realtime event.
|
|
2832
|
+
*/
|
|
2833
|
+
async confirmResync(detail) {
|
|
2834
|
+
this.assertUsable();
|
|
2835
|
+
const pending = this.pendingResyncs.get(detail.channel)?.detail;
|
|
2836
|
+
if (!pending || pending.generation !== detail.generation || pending.latestEventId !== detail.latestEventId || pending.sequence !== detail.sequence || (this.channelSequences.get(detail.channel) ?? 0) !== detail.sequence || !validCursor(detail.latestEventId)) {
|
|
2837
|
+
return false;
|
|
2838
|
+
}
|
|
2839
|
+
this.pendingResyncs.delete(detail.channel);
|
|
2840
|
+
this.rememberCursor(detail.channel, detail.latestEventId);
|
|
2841
|
+
await this.flushCursorWrite(detail.channel);
|
|
2842
|
+
return true;
|
|
2843
|
+
}
|
|
2844
|
+
destroy() {
|
|
2845
|
+
if (this.destroyed) return;
|
|
2846
|
+
this.stopConnection();
|
|
2847
|
+
this.destroyed = true;
|
|
2848
|
+
this.setState("destroyed");
|
|
2849
|
+
this.listeners.clear();
|
|
2850
|
+
this.clearDeliveryState();
|
|
2851
|
+
this.cursors.clear();
|
|
2852
|
+
this.cursorWriteChains.clear();
|
|
2853
|
+
}
|
|
2854
|
+
async loadCursors(lifecycle) {
|
|
2855
|
+
if (!this.cursorStore) return;
|
|
2856
|
+
await Promise.all(
|
|
2857
|
+
this.requestedChannels.map(async (channel) => {
|
|
2858
|
+
try {
|
|
2859
|
+
const value = await this.cursorStore?.load(channel);
|
|
2860
|
+
if (!this.isCurrentLifecycle(lifecycle)) return;
|
|
2861
|
+
const cursor = normalizedCursor(value);
|
|
2862
|
+
if (cursor) this.cursors.set(channel, cursor);
|
|
2863
|
+
else this.cursors.delete(channel);
|
|
2864
|
+
} catch {
|
|
2865
|
+
if (this.isCurrentLifecycle(lifecycle)) {
|
|
2866
|
+
this.emitError(
|
|
2867
|
+
"CURSOR_STORE_FAILED",
|
|
2868
|
+
"\u5B9E\u65F6\u6E38\u6807\u8BFB\u53D6\u5931\u8D25\uFF0C\u5C06\u4ECE\u5F53\u524D\u670D\u52A1\u7AEF\u4F4D\u7F6E\u7EE7\u7EED\u3002",
|
|
2869
|
+
true,
|
|
2870
|
+
channel
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
})
|
|
2875
|
+
);
|
|
2876
|
+
}
|
|
2877
|
+
openSocket(reconnecting) {
|
|
2878
|
+
if (this.destroyed || !this.shouldReconnect) return;
|
|
2879
|
+
this.clearReconnectTimer();
|
|
2880
|
+
this.closeCurrentSocket();
|
|
2881
|
+
this.setState(reconnecting ? "reconnecting" : "connecting");
|
|
2882
|
+
let socket;
|
|
2883
|
+
try {
|
|
2884
|
+
socket = this.webSocketFactory(this.realtimeUrl);
|
|
2885
|
+
} catch {
|
|
2886
|
+
this.shouldReconnect = false;
|
|
2887
|
+
this.emitError("WEBSOCKET_UNAVAILABLE", "\u65E0\u6CD5\u5EFA\u7ACB\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002", false);
|
|
2888
|
+
this.setState("stopped");
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
const generation = ++this.socketGeneration;
|
|
2892
|
+
this.socket = socket;
|
|
2893
|
+
this.socketAuthenticationFailure = null;
|
|
2894
|
+
socket.onopen = () => void this.authenticate(socket, generation);
|
|
2895
|
+
socket.onmessage = (event) => {
|
|
2896
|
+
const next = this.messageChain.catch(() => void 0).then(() => this.handleSocketData(event.data, socket, generation));
|
|
2897
|
+
this.messageChain = next;
|
|
2898
|
+
void next.catch(() => {
|
|
2899
|
+
if (this.isCurrentSocket(socket, generation)) {
|
|
2900
|
+
this.emitError("PROTOCOL_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u6D88\u606F\u5904\u7406\u5931\u8D25\u3002", true);
|
|
2901
|
+
}
|
|
2902
|
+
});
|
|
2903
|
+
};
|
|
2904
|
+
socket.onerror = () => {
|
|
2905
|
+
if (!this.isCurrentSocket(socket, generation)) return;
|
|
2906
|
+
this.emitError("WEBSOCKET_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u53D1\u751F\u5F02\u5E38\u3002", true);
|
|
2907
|
+
};
|
|
2908
|
+
socket.onclose = (event) => this.handleSocketClose(socket, generation, event.code);
|
|
2909
|
+
}
|
|
2910
|
+
async authenticate(socket, generation) {
|
|
2911
|
+
if (!this.isCurrentSocket(socket, generation)) return;
|
|
2912
|
+
this.setState("authenticating");
|
|
2913
|
+
let accessToken = "";
|
|
2914
|
+
try {
|
|
2915
|
+
const supplied = await this.getAccessToken();
|
|
2916
|
+
accessToken = typeof supplied === "string" ? supplied.trim() : "";
|
|
2917
|
+
} catch {
|
|
2918
|
+
}
|
|
2919
|
+
if (!this.isCurrentSocket(socket, generation)) return;
|
|
2920
|
+
if (!accessToken) {
|
|
2921
|
+
this.shouldReconnect = false;
|
|
2922
|
+
this.emitError("TOKEN_UNAVAILABLE", "\u65E0\u6CD5\u83B7\u53D6\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u3002", false);
|
|
2923
|
+
this.closeCurrentSocket();
|
|
2924
|
+
this.setState("stopped");
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
const cursors = {};
|
|
2928
|
+
for (const channel of this.requestedChannels)
|
|
2929
|
+
cursors[channel] = this.cursors.get(channel) ?? null;
|
|
2930
|
+
socket.send(
|
|
2931
|
+
JSON.stringify({
|
|
2932
|
+
type: "authenticate",
|
|
2933
|
+
protocol_version: 1,
|
|
2934
|
+
access_token: accessToken,
|
|
2935
|
+
requested_channels: [...this.requestedChannels],
|
|
2936
|
+
cursors
|
|
2937
|
+
})
|
|
2938
|
+
);
|
|
2939
|
+
accessToken = "";
|
|
2940
|
+
}
|
|
2941
|
+
async handleSocketData(data, socket, generation) {
|
|
2942
|
+
if (!this.isCurrentSocket(socket, generation)) return;
|
|
2943
|
+
let raw = "";
|
|
2944
|
+
if (typeof data === "string") raw = data;
|
|
2945
|
+
else if (typeof Blob !== "undefined" && data instanceof Blob) raw = await data.text();
|
|
2946
|
+
else if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
|
|
2947
|
+
raw = new TextDecoder().decode(data);
|
|
2948
|
+
} else {
|
|
2949
|
+
this.emitError("PROTOCOL_ERROR", "\u6536\u5230\u65E0\u6CD5\u8BC6\u522B\u7684\u7EDF\u4E00\u5B9E\u65F6\u6D88\u606F\u3002", true);
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
if (!this.isCurrentSocket(socket, generation)) return;
|
|
2953
|
+
const message = parseJsonRecord(raw);
|
|
2954
|
+
if (!message) {
|
|
2955
|
+
this.emitError("PROTOCOL_ERROR", "\u6536\u5230\u65E0\u6CD5\u89E3\u6790\u7684\u7EDF\u4E00\u5B9E\u65F6\u6D88\u606F\u3002", true);
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
this.handleServerMessage(message, socket, generation);
|
|
2959
|
+
}
|
|
2960
|
+
handleServerMessage(message, socket, generation) {
|
|
2961
|
+
const type = text2(message.type);
|
|
2962
|
+
if (type === "realtime_ready") {
|
|
2963
|
+
this.handleReady(message, socket, generation);
|
|
2964
|
+
return;
|
|
2965
|
+
}
|
|
2966
|
+
if (type === "realtime_event") {
|
|
2967
|
+
this.handleRealtimeEvent(message);
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2970
|
+
if (type === "channel_resync_required") {
|
|
2971
|
+
this.handleResyncRequired(message);
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
if (type === "channel_error") {
|
|
2975
|
+
this.handleChannelError(message, socket);
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
if (type === "authentication_error") {
|
|
2979
|
+
this.handleAuthenticationError(message, socket, generation);
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
if (type === "protocol_error") {
|
|
2983
|
+
this.shouldReconnect = false;
|
|
2984
|
+
this.emitError("PROTOCOL_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u5F02\u5E38\u3002", false);
|
|
2985
|
+
this.closeCurrentSocket();
|
|
2986
|
+
this.setState("stopped");
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
handleReady(message, socket, generation) {
|
|
2990
|
+
const channels = record3(message.channels);
|
|
2991
|
+
const unavailable = this.requestedChannels.find(
|
|
2992
|
+
(channel) => record3(channels[channel]).available !== true
|
|
2993
|
+
);
|
|
2994
|
+
if (unavailable) {
|
|
2995
|
+
this.shouldReconnect = false;
|
|
2996
|
+
this.emitError("CHANNEL_UNAVAILABLE", "\u8BF7\u6C42\u7684\u5B9E\u65F6\u901A\u9053\u4E0D\u53EF\u7528\u3002", false, unavailable);
|
|
2997
|
+
this.closeCurrentSocket();
|
|
2998
|
+
this.setState("stopped");
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
this.reconnectAttempt = 0;
|
|
3002
|
+
this.startHeartbeat(numberValue3(message.heartbeat_interval_seconds), socket, generation);
|
|
3003
|
+
this.setState("connected");
|
|
3004
|
+
this.emit("ready", { channels: [...this.requestedChannels] });
|
|
3005
|
+
}
|
|
3006
|
+
handleRealtimeEvent(message) {
|
|
3007
|
+
const channel = text2(message.channel);
|
|
3008
|
+
if (!this.requestedChannels.includes(channel)) return;
|
|
3009
|
+
const event = record3(message.event);
|
|
3010
|
+
const eventId = normalizedEventId(event.event_id) ?? normalizedEventId(message.event_id) ?? "";
|
|
3011
|
+
if (!eventId) {
|
|
3012
|
+
this.emitError("PROTOCOL_ERROR", "\u5B9E\u65F6\u4E8B\u4EF6\u7F3A\u5C11\u7A33\u5B9A\u4E8B\u4EF6\u6807\u8BC6\u3002", true, channel);
|
|
3013
|
+
return;
|
|
3014
|
+
}
|
|
3015
|
+
const cursor = normalizedCursor(event.cursor) ?? normalizedCursor(message.cursor) ?? "";
|
|
3016
|
+
if (this.hasSeenEvent(channel, eventId)) return;
|
|
3017
|
+
const previousChannelSequence = this.channelSequences.get(channel) ?? 0;
|
|
3018
|
+
const sequence = previousChannelSequence + 1;
|
|
3019
|
+
this.channelSequences.set(channel, sequence);
|
|
3020
|
+
this.deliverySequence += 1;
|
|
3021
|
+
const detail = {
|
|
3022
|
+
channel,
|
|
3023
|
+
event: message.event,
|
|
3024
|
+
eventId,
|
|
3025
|
+
cursor,
|
|
3026
|
+
sequence
|
|
3027
|
+
};
|
|
3028
|
+
if (!this.emitSafely("channel-event", detail)) {
|
|
3029
|
+
this.channelSequences.set(channel, previousChannelSequence);
|
|
3030
|
+
this.deliverySequence -= 1;
|
|
3031
|
+
this.emitError("EVENT_HANDLER_FAILED", "\u5B9E\u65F6\u4E8B\u4EF6\u5904\u7406\u5931\u8D25\uFF0C\u5C06\u7B49\u5F85\u670D\u52A1\u7AEF\u91CD\u653E\u3002", true, channel);
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
this.rememberEventId(channel, eventId);
|
|
3035
|
+
if (cursor) this.rememberCursor(channel, cursor);
|
|
3036
|
+
else this.emitError("PROTOCOL_ERROR", "\u5B9E\u65F6\u4E8B\u4EF6\u7F3A\u5C11\u53EF\u7EED\u4F20\u6E38\u6807\u3002", true, channel);
|
|
3037
|
+
}
|
|
3038
|
+
handleResyncRequired(message) {
|
|
3039
|
+
const channel = text2(message.channel);
|
|
3040
|
+
if (!this.requestedChannels.includes(channel)) return;
|
|
3041
|
+
const generation = (this.resyncGenerations.get(channel) ?? 0) + 1;
|
|
3042
|
+
this.resyncGenerations.set(channel, generation);
|
|
3043
|
+
const detail = {
|
|
3044
|
+
channel,
|
|
3045
|
+
reason: text2(message.reason) || "cursor_expired",
|
|
3046
|
+
latestEventId: normalizedCursor(message.latest_event_id) ?? "",
|
|
3047
|
+
sequence: this.channelSequences.get(channel) ?? 0,
|
|
3048
|
+
generation
|
|
3049
|
+
};
|
|
3050
|
+
this.pendingResyncs.set(channel, { detail });
|
|
3051
|
+
this.emit("resync-required", detail);
|
|
3052
|
+
}
|
|
3053
|
+
handleChannelError(message, socket) {
|
|
3054
|
+
const channel = text2(message.channel);
|
|
3055
|
+
if (!this.requestedChannels.includes(channel)) return;
|
|
3056
|
+
this.emitError("CHANNEL_UNAVAILABLE", "\u5B9E\u65F6\u901A\u9053\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u3002", true, channel);
|
|
3057
|
+
socket.close();
|
|
3058
|
+
}
|
|
3059
|
+
handleAuthenticationError(message, socket, generation) {
|
|
3060
|
+
const failure = authenticationFailure3(text2(message.code));
|
|
3061
|
+
this.socketAuthenticationFailure = { generation, retryMode: failure.retryMode };
|
|
3062
|
+
this.emitError(failure.code, failure.message, failure.retryMode !== "never");
|
|
3063
|
+
socket.close();
|
|
3064
|
+
}
|
|
3065
|
+
startHeartbeat(intervalSeconds, socket, generation) {
|
|
3066
|
+
this.clearHeartbeatTimer();
|
|
3067
|
+
const requestedMs = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds * 1e3 : DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
3068
|
+
const intervalMs = Math.max(this.minimumHeartbeatMs, requestedMs);
|
|
3069
|
+
this.heartbeatTimer = setInterval(() => {
|
|
3070
|
+
if (!this.isCurrentSocket(socket, generation) || socket.readyState !== 1) return;
|
|
3071
|
+
socket.send(JSON.stringify(createPingFrame()));
|
|
3072
|
+
}, intervalMs);
|
|
3073
|
+
}
|
|
3074
|
+
handleSocketClose(socket, generation, code) {
|
|
3075
|
+
if (!this.isCurrentSocket(socket, generation)) return;
|
|
3076
|
+
this.socket = null;
|
|
3077
|
+
this.clearHeartbeatTimer();
|
|
3078
|
+
const failure = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
|
|
3079
|
+
this.socketAuthenticationFailure = null;
|
|
3080
|
+
if (!this.shouldReconnect || this.destroyed) return;
|
|
3081
|
+
if (failure?.retryMode === "never" || code === 4400 || code === 4401 || code === 4403) {
|
|
3082
|
+
this.shouldReconnect = false;
|
|
3083
|
+
if (!failure) {
|
|
3084
|
+
if (code === 4401) {
|
|
3085
|
+
this.emitError("AUTHENTICATION_FAILED", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\u3002", false);
|
|
3086
|
+
} else if (code === 4403) {
|
|
3087
|
+
this.emitError("AUTHENTICATION_FAILED", "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u5EFA\u7ACB\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002", false);
|
|
3088
|
+
} else {
|
|
3089
|
+
this.emitError("PROTOCOL_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002", false);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
this.setState("stopped");
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
if (code === 1013 && !failure) {
|
|
3096
|
+
this.emitError("REALTIME_UNAVAILABLE", "\u7EDF\u4E00\u5B9E\u65F6\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002", true);
|
|
3097
|
+
}
|
|
3098
|
+
this.scheduleReconnect();
|
|
3099
|
+
}
|
|
3100
|
+
scheduleReconnect() {
|
|
3101
|
+
if (this.destroyed || !this.shouldReconnect || this.reconnectTimer) return;
|
|
3102
|
+
this.setState("reconnecting");
|
|
3103
|
+
const base = Math.min(
|
|
3104
|
+
this.reconnectOptions.initialDelayMs * this.reconnectOptions.factor ** this.reconnectAttempt,
|
|
3105
|
+
this.reconnectOptions.maximumDelayMs
|
|
3106
|
+
);
|
|
3107
|
+
const jitter = base * this.reconnectOptions.jitter * (Math.random() * 2 - 1);
|
|
3108
|
+
this.reconnectAttempt += 1;
|
|
3109
|
+
const lifecycle = this.lifecycleGeneration;
|
|
3110
|
+
this.reconnectTimer = setTimeout(
|
|
3111
|
+
() => {
|
|
3112
|
+
this.reconnectTimer = null;
|
|
3113
|
+
if (!this.isCurrentLifecycle(lifecycle) || !this.shouldReconnect) return;
|
|
3114
|
+
this.openSocket(true);
|
|
3115
|
+
},
|
|
3116
|
+
Math.max(0, base + jitter)
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
rememberEventId(channel, eventId) {
|
|
3120
|
+
const seen = this.seenEventIds.get(channel) ?? /* @__PURE__ */ new Set();
|
|
3121
|
+
seen.add(eventId);
|
|
3122
|
+
while (seen.size > this.maximumSeenEventIds) {
|
|
3123
|
+
const oldest = seen.values().next().value;
|
|
3124
|
+
if (typeof oldest !== "string") break;
|
|
3125
|
+
seen.delete(oldest);
|
|
3126
|
+
}
|
|
3127
|
+
this.seenEventIds.set(channel, seen);
|
|
3128
|
+
}
|
|
3129
|
+
hasSeenEvent(channel, eventId) {
|
|
3130
|
+
return this.seenEventIds.get(channel)?.has(eventId) === true;
|
|
3131
|
+
}
|
|
3132
|
+
rememberCursor(channel, cursor) {
|
|
3133
|
+
this.cursors.set(channel, cursor);
|
|
3134
|
+
if (!this.cursorStore) return;
|
|
3135
|
+
const previous = this.cursorWriteChains.get(channel) ?? Promise.resolve();
|
|
3136
|
+
const next = previous.catch(() => void 0).then(() => this.cursorStore?.save(channel, cursor)).then(() => void 0).catch(() => {
|
|
3137
|
+
this.emitError("CURSOR_STORE_FAILED", "\u5B9E\u65F6\u6E38\u6807\u4FDD\u5B58\u5931\u8D25\u3002", true, channel);
|
|
3138
|
+
});
|
|
3139
|
+
this.cursorWriteChains.set(channel, next);
|
|
3140
|
+
void next.finally(() => {
|
|
3141
|
+
if (this.cursorWriteChains.get(channel) === next) this.cursorWriteChains.delete(channel);
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
async flushCursorWrite(channel) {
|
|
3145
|
+
await this.cursorWriteChains.get(channel);
|
|
3146
|
+
}
|
|
3147
|
+
async clearStoredCursor(channel) {
|
|
3148
|
+
try {
|
|
3149
|
+
await this.cursorWriteChains.get(channel);
|
|
3150
|
+
await this.cursorStore?.clear(channel);
|
|
3151
|
+
} catch {
|
|
3152
|
+
this.emitError("CURSOR_STORE_FAILED", "\u5B9E\u65F6\u6E38\u6807\u6E05\u7406\u5931\u8D25\u3002", true, channel);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
stopConnection() {
|
|
3156
|
+
this.shouldReconnect = false;
|
|
3157
|
+
this.startPromise = null;
|
|
3158
|
+
this.lifecycleGeneration += 1;
|
|
3159
|
+
this.clearReconnectTimer();
|
|
3160
|
+
this.clearHeartbeatTimer();
|
|
3161
|
+
this.closeCurrentSocket();
|
|
3162
|
+
this.socketAuthenticationFailure = null;
|
|
3163
|
+
}
|
|
3164
|
+
closeCurrentSocket() {
|
|
3165
|
+
const socket = this.socket;
|
|
3166
|
+
this.socket = null;
|
|
3167
|
+
this.socketGeneration += 1;
|
|
3168
|
+
if (socket && socket.readyState < 2) socket.close();
|
|
3169
|
+
}
|
|
3170
|
+
clearReconnectTimer() {
|
|
3171
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
3172
|
+
this.reconnectTimer = null;
|
|
3173
|
+
}
|
|
3174
|
+
clearHeartbeatTimer() {
|
|
3175
|
+
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
3176
|
+
this.heartbeatTimer = null;
|
|
3177
|
+
}
|
|
3178
|
+
clearDeliveryState() {
|
|
3179
|
+
this.seenEventIds.clear();
|
|
3180
|
+
this.channelSequences.clear();
|
|
3181
|
+
this.pendingResyncs.clear();
|
|
3182
|
+
this.resyncGenerations.clear();
|
|
3183
|
+
this.deliverySequence = 0;
|
|
3184
|
+
}
|
|
3185
|
+
isCurrentSocket(socket, generation) {
|
|
3186
|
+
return !this.destroyed && this.shouldReconnect && this.socket === socket && this.socketGeneration === generation;
|
|
3187
|
+
}
|
|
3188
|
+
isCurrentLifecycle(generation) {
|
|
3189
|
+
return !this.destroyed && this.lifecycleGeneration === generation;
|
|
3190
|
+
}
|
|
3191
|
+
setState(current) {
|
|
3192
|
+
if (this.currentState === current) return;
|
|
3193
|
+
const previous = this.currentState;
|
|
3194
|
+
this.currentState = current;
|
|
3195
|
+
this.emit("state-change", { previous, current });
|
|
3196
|
+
}
|
|
3197
|
+
emitError(code, message, recoverable, channel) {
|
|
3198
|
+
const detail = {
|
|
3199
|
+
code,
|
|
3200
|
+
message,
|
|
3201
|
+
recoverable,
|
|
3202
|
+
...channel ? { channel } : {}
|
|
3203
|
+
};
|
|
3204
|
+
this.emit("error", detail);
|
|
3205
|
+
return detail;
|
|
3206
|
+
}
|
|
3207
|
+
emit(type, detail) {
|
|
3208
|
+
for (const listener of this.listeners.get(type) ?? []) {
|
|
3209
|
+
try {
|
|
3210
|
+
listener(detail);
|
|
3211
|
+
} catch {
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
emitSafely(type, detail) {
|
|
3216
|
+
let success = true;
|
|
3217
|
+
for (const listener of this.listeners.get(type) ?? []) {
|
|
3218
|
+
try {
|
|
3219
|
+
listener(detail);
|
|
3220
|
+
} catch {
|
|
3221
|
+
success = false;
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
return success;
|
|
3225
|
+
}
|
|
3226
|
+
assertUsable() {
|
|
3227
|
+
if (this.destroyed) {
|
|
3228
|
+
throw new UserRealtimeClientError("CLIENT_DESTROYED", "\u7EDF\u4E00\u5B9E\u65F6\u5BA2\u6237\u7AEF\u5DF2\u7ECF\u9500\u6BC1\u3002");
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
};
|
|
3232
|
+
function validateRealtimeUrl3(rawUrl) {
|
|
3233
|
+
let url;
|
|
3234
|
+
try {
|
|
3235
|
+
url = new URL(rawUrl);
|
|
3236
|
+
} catch {
|
|
3237
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "realtimeUrl \u4E0D\u662F\u6709\u6548\u5730\u5740\u3002");
|
|
3238
|
+
}
|
|
3239
|
+
if (!["ws:", "wss:"].includes(url.protocol)) {
|
|
3240
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "realtimeUrl \u53EA\u5141\u8BB8 ws: \u6216 wss:\u3002");
|
|
3241
|
+
}
|
|
3242
|
+
if (url.search || url.hash || url.username || url.password) {
|
|
3243
|
+
throw new UserRealtimeClientError(
|
|
3244
|
+
"INVALID_CONFIGURATION",
|
|
3245
|
+
"realtimeUrl \u4E0D\u5141\u8BB8\u5305\u542B\u67E5\u8BE2\u53C2\u6570\u3001\u7247\u6BB5\u6216\u7528\u6237\u51ED\u636E\u3002"
|
|
3246
|
+
);
|
|
3247
|
+
}
|
|
3248
|
+
return url.toString();
|
|
3249
|
+
}
|
|
3250
|
+
function normalizeChannels(channels) {
|
|
3251
|
+
if (!Array.isArray(channels)) {
|
|
3252
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "requestedChannels \u4E0D\u80FD\u4E3A\u7A7A\u3002");
|
|
3253
|
+
}
|
|
3254
|
+
const normalized = [...new Set(channels.map((channel) => text2(channel).trim()))];
|
|
3255
|
+
if (!normalized.length || normalized.some((channel) => !CHANNEL_PATTERN.test(channel))) {
|
|
3256
|
+
throw new UserRealtimeClientError("INVALID_CONFIGURATION", "requestedChannels \u5305\u542B\u65E0\u6548\u901A\u9053\u3002");
|
|
3257
|
+
}
|
|
3258
|
+
return Object.freeze(normalized);
|
|
3259
|
+
}
|
|
3260
|
+
function normalizeReconnect2(value) {
|
|
3261
|
+
const jitter = value?.jitter;
|
|
3262
|
+
return {
|
|
3263
|
+
initialDelayMs: positiveNumber2(value?.initialDelayMs, DEFAULT_RECONNECT3.initialDelayMs),
|
|
3264
|
+
maximumDelayMs: positiveNumber2(value?.maximumDelayMs, DEFAULT_RECONNECT3.maximumDelayMs),
|
|
3265
|
+
factor: Math.max(1, positiveNumber2(value?.factor, DEFAULT_RECONNECT3.factor)),
|
|
3266
|
+
jitter: typeof jitter === "number" && Number.isFinite(jitter) ? Math.min(1, Math.max(0, jitter)) : DEFAULT_RECONNECT3.jitter
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
function authenticationFailure3(code) {
|
|
3270
|
+
if (isRealtimeAvailabilityFailure2(code)) {
|
|
3271
|
+
return {
|
|
3272
|
+
code: "REALTIME_UNAVAILABLE",
|
|
3273
|
+
message: "\u7EDF\u4E00\u5B9E\u65F6\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002",
|
|
3274
|
+
retryMode: "availability"
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
if (code === "INVALID_ARGUMENT") {
|
|
3278
|
+
return {
|
|
3279
|
+
code: "PROTOCOL_ERROR",
|
|
3280
|
+
message: "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002",
|
|
3281
|
+
retryMode: "never"
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
if (code === "ACCESS_DENIED") {
|
|
3285
|
+
return {
|
|
3286
|
+
code: "AUTHENTICATION_FAILED",
|
|
3287
|
+
message: "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u5EFA\u7ACB\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002",
|
|
3288
|
+
retryMode: "never"
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
return {
|
|
3292
|
+
code: "AUTHENTICATION_FAILED",
|
|
3293
|
+
message: "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\u3002",
|
|
3294
|
+
retryMode: "never"
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
3297
|
+
function isRealtimeAvailabilityFailure2(code) {
|
|
3298
|
+
return code === "REALTIME_UNAVAILABLE" || code === "KANBAN_AUTH_FAILED" || code.startsWith("SERVICE_");
|
|
3299
|
+
}
|
|
3300
|
+
function normalizedCursor(value) {
|
|
3301
|
+
const cursor = text2(value).trim();
|
|
3302
|
+
return validCursor(cursor) ? cursor : null;
|
|
3303
|
+
}
|
|
3304
|
+
function normalizedEventId(value) {
|
|
3305
|
+
const eventId = text2(value).trim();
|
|
3306
|
+
if (!eventId || eventId.length > 256) return null;
|
|
3307
|
+
for (const character of eventId) {
|
|
3308
|
+
const code = character.charCodeAt(0);
|
|
3309
|
+
if (code < 32 || code === 127) return null;
|
|
3310
|
+
}
|
|
3311
|
+
return eventId;
|
|
3312
|
+
}
|
|
3313
|
+
function validCursor(value) {
|
|
3314
|
+
return CURSOR_PATTERN.test(value);
|
|
3315
|
+
}
|
|
3316
|
+
function positiveNumber2(value, fallback) {
|
|
3317
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
3318
|
+
}
|
|
3319
|
+
function positiveInteger(value, fallback) {
|
|
3320
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;
|
|
3321
|
+
}
|
|
3322
|
+
function text2(value) {
|
|
3323
|
+
return typeof value === "string" ? value : "";
|
|
3324
|
+
}
|
|
3325
|
+
function record3(value) {
|
|
3326
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
3327
|
+
}
|
|
3328
|
+
function numberValue3(value) {
|
|
3329
|
+
return typeof value === "number" ? value : Number.NaN;
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
export { AiEmployeeClient, AiEmployeeClientError, AiEmployeeTransportError, CustomerSupportClient, CustomerSupportClientError, NotificationCenterClient, NotificationCenterClientError, UserRealtimeClient, UserRealtimeClientError, createAuthenticateFrame, createPingFrame, createSubscriptionFrame, hashText, isUuidV4, parseAiEmployeeAssistantContent, parseAiEmployeeEvent, parseJsonRecord };
|
|
2688
3333
|
//# sourceMappingURL=index.js.map
|
|
2689
3334
|
//# sourceMappingURL=index.js.map
|