snapreq 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,154 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Client-side handle for a 1:1 connection opened via
5
+ * `SnapReqWebSocketClient.openConnection()`. Mirrors the server's connection
6
+ * lifecycle — `onConnect` / `onMessage` / `onClose` plus `sendMessage` /
7
+ * `close`.
8
+ */
9
+ export default class SnapReqWebSocketConnection {
10
+ /**
11
+ * @param {object} args - Connection arguments.
12
+ * @param {import("./websocket-client.js").default} args.client - Owning client.
13
+ * @param {string} args.connectionId - Generated id unique within the session.
14
+ * @param {string} args.connectionType - Name the server registered the class under.
15
+ * @param {Record<string, any>} [args.params] - Opaque params forwarded to the server.
16
+ * @param {() => void} [args.onConnect] - Fired after the server confirms `connection-opened`.
17
+ * @param {(body: any) => void} [args.onMessage] - Fired on each `connection-message` from the server.
18
+ * @param {() => void} [args.onDisconnect] - Fired when the socket drops; connection is preserved pending resume.
19
+ * @param {() => void} [args.onResume] - Fired when the session successfully resumes after a drop.
20
+ * @param {(reason: string) => void} [args.onClose] - Fired exactly once when the handle closes permanently.
21
+ */
22
+ constructor({client, connectionId, connectionType, params, onConnect, onMessage, onDisconnect, onResume, onClose}) {
23
+ this.client = client
24
+ this.connectionId = connectionId
25
+ this.connectionType = connectionType
26
+ this.params = params || {}
27
+ this._onConnect = onConnect
28
+ this._onMessage = onMessage
29
+ this._onDisconnect = onDisconnect
30
+ this._onResume = onResume
31
+ this._onClose = onClose
32
+ this._connected = false
33
+ this._closed = false
34
+
35
+ /** @type {Promise<void>} - Resolves once the server sends `connection-opened`. */
36
+ this.ready = new Promise((resolve, reject) => {
37
+ this._resolveReady = resolve
38
+ this._rejectReady = reject
39
+ })
40
+ }
41
+
42
+ /**
43
+ * Called by the client dispatcher when `{type: "connection-opened"}` arrives.
44
+ * Fires the user's `onConnect` and resolves `ready`.
45
+ * @returns {void}
46
+ */
47
+ _handleOpened() {
48
+ if (this._closed || this._connected) return
49
+ this._connected = true
50
+
51
+ try {
52
+ this._onConnect?.()
53
+ } finally {
54
+ this._resolveReady?.()
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Called by the client dispatcher for each `connection-message` targeted at
60
+ * this connection id.
61
+ * @param {any} body - Message payload.
62
+ * @returns {void}
63
+ */
64
+ _handleMessage(body) {
65
+ if (this._closed) return
66
+ this._onMessage?.(body)
67
+ }
68
+
69
+ /**
70
+ * Called by the client when the underlying socket drops. The connection stays
71
+ * alive pending session resume.
72
+ * @returns {void}
73
+ */
74
+ _handleDisconnected() {
75
+ if (this._closed) return
76
+ this._onDisconnect?.()
77
+ }
78
+
79
+ /**
80
+ * Called by the client after `session-resumed` confirms the server still has
81
+ * this connection.
82
+ * @returns {void}
83
+ */
84
+ _handleResumed() {
85
+ if (this._closed) return
86
+ this._onResume?.()
87
+ }
88
+
89
+ /**
90
+ * Called by the client dispatcher when the connection closes for any reason.
91
+ * Fires `onClose(reason)` at most once.
92
+ * @param {string} reason - Why the connection closed.
93
+ * @returns {void}
94
+ */
95
+ _handleClosed(reason) {
96
+ if (this._closed) return
97
+ this._closed = true
98
+
99
+ try {
100
+ this._onClose?.(reason)
101
+ } finally {
102
+ if (!this._connected) {
103
+ this._rejectReady?.(new Error(`Connection closed before open: ${reason}`))
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Sends a message to the server side of this connection.
110
+ * @param {any} body - Message payload.
111
+ * @returns {void}
112
+ */
113
+ sendMessage(body) {
114
+ if (this._closed) {
115
+ throw new Error(`Cannot sendMessage on closed connection ${this.connectionId}`)
116
+ }
117
+
118
+ this.client._sendMessage({
119
+ type: "connection-message",
120
+ connectionId: this.connectionId,
121
+ body
122
+ })
123
+ }
124
+
125
+ /**
126
+ * Closes the connection from the client side. Fires `onClose("client_close")`
127
+ * locally and notifies the server. No-op if already closed.
128
+ * @returns {void}
129
+ */
130
+ close() {
131
+ if (this._closed) return
132
+
133
+ // Send the close frame BEFORE flipping _closed so _sendMessage doesn't
134
+ // refuse — and guard against a socket that's already gone so the local
135
+ // teardown still runs.
136
+ try {
137
+ if (this.client.isOpen()) {
138
+ this.client._sendMessage({type: "connection-close", connectionId: this.connectionId})
139
+ }
140
+ } catch {
141
+ // Socket may have closed between our check and the send; the server will
142
+ // see the session destroy and clean up regardless.
143
+ }
144
+
145
+ this.client._removeConnection(this.connectionId)
146
+ this._handleClosed("client_close")
147
+ }
148
+
149
+ /** @returns {boolean} - Whether the connection is closed. */
150
+ isClosed() { return this._closed }
151
+
152
+ /** @returns {boolean} - Whether the connection is open. */
153
+ isConnected() { return this._connected && !this._closed }
154
+ }