nvent 1.0.0-alpha.10 → 1.0.0-alpha.11
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/module.json
CHANGED
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useIiiStream — subscribe to an iii stream group via WebSocket.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Connects to the iii engine's built-in stream WebSocket endpoint:
|
|
5
5
|
* ws://host/_iii/stream/{stream_name}/{group_id}/
|
|
6
6
|
*
|
|
7
7
|
* Nitro proxies `/_iii/stream/**` → `ws://iii-engine:3112/`, so connecting
|
|
8
8
|
* to the current page host is sufficient — no hardcoded ports needed.
|
|
9
9
|
*
|
|
10
|
+
* **Authentication**: The browser WebSocket API does not support custom
|
|
11
|
+
* headers. The iii engine uses cookie-based auth for WebSocket connections
|
|
12
|
+
* (same session cookies as the rest of the app), so auth is handled
|
|
13
|
+
* automatically without any additional configuration.
|
|
14
|
+
*
|
|
15
|
+
* **Reconnection**: The composable implements exponential backoff with jitter.
|
|
16
|
+
* When the connection drops unexpectedly it will retry up to `maxRetries`
|
|
17
|
+
* times (default: 10) before giving up and setting status to `'error'`.
|
|
18
|
+
* Calling `subscribe()` again with new arguments or after an intentional
|
|
19
|
+
* `close()` always resets the retry counter and starts fresh.
|
|
20
|
+
*
|
|
21
|
+
* **No unnecessary reconnects**: Calling `subscribe()` with the same
|
|
22
|
+
* `streamName`/`groupId` while already connected is a no-op.
|
|
23
|
+
*
|
|
24
|
+
*
|
|
10
25
|
* ```ts
|
|
11
26
|
* const { messages, status, subscribe, close } = useIiiStream<MyEvent>()
|
|
12
|
-
* subscribe('pipeline', jobId) // start listening
|
|
27
|
+
* subscribe('pipeline', jobId) // start listening; auto-reconnects on drop
|
|
13
28
|
* // messages.value grows as events arrive
|
|
14
29
|
* ```
|
|
15
30
|
*/
|
|
16
31
|
export type IiiStreamStatus = 'idle' | 'connecting' | 'connected' | 'closed' | 'error';
|
|
17
|
-
export
|
|
32
|
+
export interface IiiStreamOptions {
|
|
33
|
+
/** Maximum number of reconnect attempts before giving up. Default: 10. */
|
|
34
|
+
maxRetries?: number;
|
|
35
|
+
/** Base delay in ms for the first reconnect attempt. Default: 500. */
|
|
36
|
+
baseDelayMs?: number;
|
|
37
|
+
/** Maximum delay in ms between reconnect attempts. Default: 30_000. */
|
|
38
|
+
maxDelayMs?: number;
|
|
39
|
+
}
|
|
40
|
+
export declare function useIiiStream<TMessage = unknown>(options?: IiiStreamOptions): {
|
|
18
41
|
messages: import("vue").Ref<import("@vue/reactivity").UnwrapRefSimple<TMessage>[], TMessage[] | import("@vue/reactivity").UnwrapRefSimple<TMessage>[]>;
|
|
19
42
|
status: import("vue").Ref<IiiStreamStatus, IiiStreamStatus>;
|
|
20
43
|
subscribe: (streamName: string, groupId: string) => void;
|
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
import { ref, onUnmounted } from "vue";
|
|
2
|
-
export function useIiiStream() {
|
|
2
|
+
export function useIiiStream(options = {}) {
|
|
3
|
+
const {
|
|
4
|
+
maxRetries = 10,
|
|
5
|
+
baseDelayMs = 500,
|
|
6
|
+
maxDelayMs = 3e4
|
|
7
|
+
} = options;
|
|
3
8
|
const messages = ref([]);
|
|
4
9
|
const status = ref("idle");
|
|
5
10
|
let ws = null;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
let currentStreamName = null;
|
|
12
|
+
let currentGroupId = null;
|
|
13
|
+
let retryCount = 0;
|
|
14
|
+
let retryTimer = null;
|
|
15
|
+
let intentionalClose = false;
|
|
16
|
+
function buildUrl(streamName, groupId) {
|
|
17
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
18
|
+
const host = window.location.host;
|
|
19
|
+
return `${protocol}//${host}/_iii/stream/${encodeURIComponent(streamName)}/${encodeURIComponent(groupId)}/`;
|
|
20
|
+
}
|
|
21
|
+
function clearRetryTimer() {
|
|
22
|
+
if (retryTimer !== null) {
|
|
23
|
+
clearTimeout(retryTimer);
|
|
24
|
+
retryTimer = null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function connect(streamName, groupId) {
|
|
28
|
+
if (typeof window === "undefined") return;
|
|
9
29
|
status.value = "connecting";
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
30
|
+
const socket = new WebSocket(buildUrl(streamName, groupId));
|
|
31
|
+
ws = socket;
|
|
32
|
+
socket.onopen = () => {
|
|
33
|
+
retryCount = 0;
|
|
14
34
|
status.value = "connected";
|
|
15
35
|
};
|
|
16
|
-
|
|
36
|
+
socket.onmessage = (e) => {
|
|
17
37
|
try {
|
|
18
38
|
const envelope = JSON.parse(e.data);
|
|
19
39
|
let payload;
|
|
@@ -33,20 +53,56 @@ export function useIiiStream() {
|
|
|
33
53
|
messages.value = [...messages.value, e.data];
|
|
34
54
|
}
|
|
35
55
|
};
|
|
36
|
-
|
|
37
|
-
status.value = "error";
|
|
56
|
+
socket.onerror = () => {
|
|
38
57
|
};
|
|
39
|
-
|
|
40
|
-
if (status.value !== "error") status.value = "closed";
|
|
58
|
+
socket.onclose = (event) => {
|
|
41
59
|
ws = null;
|
|
60
|
+
if (intentionalClose) {
|
|
61
|
+
status.value = "closed";
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (retryCount >= maxRetries) {
|
|
65
|
+
status.value = "error";
|
|
66
|
+
currentStreamName = null;
|
|
67
|
+
currentGroupId = null;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const delay = Math.min(baseDelayMs * 2 ** retryCount, maxDelayMs);
|
|
71
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
72
|
+
retryCount++;
|
|
73
|
+
status.value = "connecting";
|
|
74
|
+
retryTimer = setTimeout(() => {
|
|
75
|
+
retryTimer = null;
|
|
76
|
+
if (!intentionalClose && currentStreamName && currentGroupId) {
|
|
77
|
+
connect(currentStreamName, currentGroupId);
|
|
78
|
+
}
|
|
79
|
+
}, Math.max(0, delay + jitter));
|
|
42
80
|
};
|
|
43
81
|
}
|
|
82
|
+
function subscribe(streamName, groupId) {
|
|
83
|
+
if (!intentionalClose && ws !== null && ws.readyState === WebSocket.OPEN && currentStreamName === streamName && currentGroupId === groupId) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
close();
|
|
87
|
+
intentionalClose = false;
|
|
88
|
+
retryCount = 0;
|
|
89
|
+
messages.value = [];
|
|
90
|
+
currentStreamName = streamName;
|
|
91
|
+
currentGroupId = groupId;
|
|
92
|
+
connect(streamName, groupId);
|
|
93
|
+
}
|
|
44
94
|
function close() {
|
|
95
|
+
intentionalClose = true;
|
|
96
|
+
clearRetryTimer();
|
|
45
97
|
if (ws) {
|
|
46
98
|
ws.close();
|
|
47
99
|
ws = null;
|
|
48
100
|
}
|
|
49
|
-
if (status.value !== "idle")
|
|
101
|
+
if (status.value !== "idle") {
|
|
102
|
+
status.value = "closed";
|
|
103
|
+
}
|
|
104
|
+
currentStreamName = null;
|
|
105
|
+
currentGroupId = null;
|
|
50
106
|
}
|
|
51
107
|
onUnmounted(close);
|
|
52
108
|
return { messages, status, subscribe, close };
|