@@ -107,6 +107,179 @@ describe('WebSocketFactory', () => {
107
107
} )
108
108
} )
109
109
110
+ describe ( 'Edge Runtime compatibility - dynamic property access' , ( ) => {
111
+ // These tests verify that we use dynamic property access to avoid
112
+ // Next.js Edge Runtime static analysis warnings
113
+
114
+ const originalProcess = global . process
115
+
116
+ afterEach ( ( ) => {
117
+ global . process = originalProcess
118
+ } )
119
+
120
+ test ( 'should handle undefined process.versions' , ( ) => {
121
+ // Process exists but versions is undefined
122
+ global . process = { } as any
123
+ Object . defineProperty ( global . process , 'versions' , {
124
+ value : undefined ,
125
+ configurable : true ,
126
+ } )
127
+ delete global . WebSocket
128
+ delete ( globalThis as any ) . WebSocket
129
+
130
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
131
+ expect ( env . type ) . toBe ( 'unsupported' )
132
+ expect ( env . error ) . toContain ( 'Unknown JavaScript runtime' )
133
+ } )
134
+
135
+ test ( 'should handle null process.versions' , ( ) => {
136
+ // Process exists but versions is null
137
+ global . process = { } as any
138
+ Object . defineProperty ( global . process , 'versions' , {
139
+ value : null ,
140
+ configurable : true ,
141
+ } )
142
+ delete global . WebSocket
143
+ delete ( globalThis as any ) . WebSocket
144
+
145
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
146
+ expect ( env . type ) . toBe ( 'unsupported' )
147
+ expect ( env . error ) . toContain ( 'Unknown JavaScript runtime' )
148
+ } )
149
+
150
+ test ( 'should handle process.versions.node being undefined' , ( ) => {
151
+ global . process = { versions : { } } as any
152
+ delete global . WebSocket
153
+ delete ( globalThis as any ) . WebSocket
154
+
155
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
156
+ expect ( env . type ) . toBe ( 'unsupported' )
157
+ expect ( env . error ) . toContain ( 'Unknown JavaScript runtime' )
158
+ } )
159
+
160
+ test ( 'should handle process.versions.node being null' , ( ) => {
161
+ global . process = { versions : { node : null } } as any
162
+ delete global . WebSocket
163
+ delete ( globalThis as any ) . WebSocket
164
+
165
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
166
+ expect ( env . type ) . toBe ( 'unsupported' )
167
+ expect ( env . error ) . toContain ( 'Unknown JavaScript runtime' )
168
+ } )
169
+
170
+ test ( 'should handle invalid Node.js version string' , ( ) => {
171
+ global . process = { versions : { node : 'invalid-version' } } as any
172
+ delete global . WebSocket
173
+ delete ( globalThis as any ) . WebSocket
174
+
175
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
176
+ expect ( env . type ) . toBe ( 'unsupported' )
177
+ // NaN from parseInt('invalid') makes nodeVersion < 22 true
178
+ expect ( env . error ) . toContain ( 'detected without native WebSocket support' )
179
+ } )
180
+
181
+ test ( 'should handle Node.js version without v prefix' , ( ) => {
182
+ global . process = { versions : { node : '18.0.0' } } as any
183
+ delete global . WebSocket
184
+ delete ( globalThis as any ) . WebSocket
185
+
186
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
187
+ expect ( env . type ) . toBe ( 'unsupported' )
188
+ expect ( env . error ) . toContain (
189
+ 'Node.js 18 detected without native WebSocket support'
190
+ )
191
+ } )
192
+
193
+ test ( 'should correctly detect Node.js 16' , ( ) => {
194
+ global . process = { versions : { node : 'v16.14.0' } } as any
195
+ delete global . WebSocket
196
+ delete ( globalThis as any ) . WebSocket
197
+
198
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
199
+ expect ( env . type ) . toBe ( 'unsupported' )
200
+ expect ( env . error ) . toContain (
201
+ 'Node.js 16 detected without native WebSocket support'
202
+ )
203
+ expect ( env . workaround ) . toContain ( 'ws' )
204
+ } )
205
+
206
+ test ( 'should correctly detect Node.js 18' , ( ) => {
207
+ global . process = { versions : { node : 'v18.0.0' } } as any
208
+ delete global . WebSocket
209
+ delete ( globalThis as any ) . WebSocket
210
+
211
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
212
+ expect ( env . type ) . toBe ( 'unsupported' )
213
+ expect ( env . error ) . toContain (
214
+ 'Node.js 18 detected without native WebSocket support'
215
+ )
216
+ expect ( env . workaround ) . toContain ( 'ws' )
217
+ } )
218
+
219
+ test ( 'should correctly detect Node.js 20' , ( ) => {
220
+ global . process = { versions : { node : 'v20.0.0' } } as any
221
+ delete global . WebSocket
222
+ delete ( globalThis as any ) . WebSocket
223
+
224
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
225
+ expect ( env . type ) . toBe ( 'unsupported' )
226
+ expect ( env . error ) . toContain (
227
+ 'Node.js 20 detected without native WebSocket support'
228
+ )
229
+ expect ( env . workaround ) . toContain ( 'ws' )
230
+ } )
231
+
232
+ test ( 'should correctly detect Node.js 22 without WebSocket' , ( ) => {
233
+ global . process = { versions : { node : 'v22.0.0' } } as any
234
+ delete global . WebSocket
235
+ delete ( globalThis as any ) . WebSocket
236
+
237
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
238
+ expect ( env . type ) . toBe ( 'unsupported' )
239
+ expect ( env . error ) . toContain (
240
+ 'Node.js 22 detected but native WebSocket not found'
241
+ )
242
+ expect ( env . workaround ) . toContain (
243
+ 'Provide a WebSocket implementation via the transport option'
244
+ )
245
+ } )
246
+
247
+ test ( 'should correctly detect Node.js 22 with WebSocket' , ( ) => {
248
+ global . process = { versions : { node : 'v22.0.0' } } as any
249
+ delete global . WebSocket
250
+ ; ( globalThis as any ) . WebSocket = MockWebSocket
251
+
252
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
253
+ expect ( env . type ) . toBe ( 'native' )
254
+ expect ( env . constructor ) . toBe ( MockWebSocket )
255
+
256
+ delete ( globalThis as any ) . WebSocket
257
+ } )
258
+
259
+ test ( 'ensures process.versions access uses bracket notation' , ( ) => {
260
+ // This test verifies that our implementation doesn't directly access process.versions
261
+ // which would trigger Next.js Edge Runtime warnings.
262
+ // The actual verification happens through static analysis, but we can test the behavior.
263
+
264
+ const processProxy = new Proxy ( { } as any , {
265
+ get ( target , prop ) {
266
+ if ( prop === 'versions' ) {
267
+ return { node : 'v18.0.0' }
268
+ }
269
+ return undefined
270
+ } ,
271
+ } )
272
+
273
+ global . process = processProxy
274
+ delete global . WebSocket
275
+ delete ( globalThis as any ) . WebSocket
276
+
277
+ const env = ( WebSocketFactory as any ) . detectEnvironment ( )
278
+ expect ( env . type ) . toBe ( 'unsupported' )
279
+ expect ( env . error ) . toContain ( 'Node.js 18' )
280
+ } )
281
+ } )
282
+
110
283
describe ( 'Node.js environment' , ( ) => {
111
284
beforeEach ( ( ) => {
112
285
delete global . WebSocket
0 commit comments