@gjsify/http 0.0.3 → 0.1.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.
@@ -0,0 +1,538 @@
1
+ // Ported from refs/node-test/parallel/test-http-outgoing-properties.js,
2
+ // test-http-agent.js, test-http-methods.js and others
3
+ // Original: MIT license, Node.js contributors
4
+ import { describe, it, expect } from '@gjsify/unit';
5
+ import * as http from 'node:http';
6
+
7
+ export default async () => {
8
+ await describe('http.ClientRequest', async () => {
9
+ await it('should export ClientRequest class', async () => {
10
+ expect(typeof http.ClientRequest).toBe('function');
11
+ });
12
+
13
+ await it('should export request function', async () => {
14
+ expect(typeof http.request).toBe('function');
15
+ });
16
+
17
+ await it('should export get function', async () => {
18
+ expect(typeof http.get).toBe('function');
19
+ });
20
+
21
+ await it('request() should return a ClientRequest instance', async () => {
22
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
23
+ expect(req).toBeDefined();
24
+ expect(typeof req.setHeader).toBe('function');
25
+ expect(typeof req.getHeader).toBe('function');
26
+ expect(typeof req.removeHeader).toBe('function');
27
+ expect(typeof req.end).toBe('function');
28
+ expect(typeof req.write).toBe('function');
29
+ expect(typeof req.abort).toBe('function');
30
+ // Clean up — abort to avoid hanging
31
+ req.on('error', () => {});
32
+ req.abort();
33
+ });
34
+
35
+ await it('request() should accept URL string', async () => {
36
+ const req = http.request('http://localhost:1/test');
37
+ expect(req).toBeDefined();
38
+ expect(req.method).toBe('GET');
39
+ expect(req.path).toBe('/test');
40
+ req.on('error', () => {});
41
+ req.abort();
42
+ });
43
+
44
+ await it('request() should accept options with method', async () => {
45
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/data', method: 'POST' });
46
+ expect(req.method).toBe('POST');
47
+ req.on('error', () => {});
48
+ req.abort();
49
+ });
50
+
51
+ await it('ClientRequest should support setHeader/getHeader/removeHeader', async () => {
52
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
53
+ req.setHeader('X-Custom', 'test');
54
+ expect(req.getHeader('x-custom')).toBe('test');
55
+ expect(req.hasHeader('x-custom')).toBe(true);
56
+ req.removeHeader('x-custom');
57
+ expect(req.hasHeader('x-custom')).toBe(false);
58
+ req.on('error', () => {});
59
+ req.abort();
60
+ });
61
+
62
+ await it('ClientRequest should have default properties', async () => {
63
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/api' });
64
+ expect(req.method).toBe('GET');
65
+ expect(req.path).toBe('/api');
66
+ req.on('error', () => {});
67
+ req.abort();
68
+ });
69
+
70
+ await it('get() should set method to GET', async () => {
71
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/', method: 'GET' });
72
+ expect(req.method).toBe('GET');
73
+ // Don't call end() — just verify the method is set
74
+ req.on('error', () => {});
75
+ req.abort();
76
+ });
77
+
78
+ await it('request() should default method to GET', async () => {
79
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
80
+ expect(req.method).toBe('GET');
81
+ req.on('error', () => {});
82
+ req.abort();
83
+ });
84
+
85
+ await it('request() should uppercase the method', async () => {
86
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/', method: 'post' });
87
+ expect(req.method).toBe('POST');
88
+ req.on('error', () => {});
89
+ req.abort();
90
+ });
91
+
92
+ await it('request() should default path to /', async () => {
93
+ const req = http.request({ hostname: 'localhost', port: 1 });
94
+ expect(req.path).toBe('/');
95
+ req.on('error', () => {});
96
+ req.abort();
97
+ });
98
+
99
+ await it('request() should default protocol to http:', async () => {
100
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
101
+ expect(req.protocol).toBe('http:');
102
+ req.on('error', () => {});
103
+ req.abort();
104
+ });
105
+
106
+ await it('request() should parse path from URL string', async () => {
107
+ const req = http.request('http://localhost:8080/path');
108
+ expect(req.path).toBe('/path');
109
+ req.on('error', () => {});
110
+ req.abort();
111
+ });
112
+
113
+ await it('request() should set host header', async () => {
114
+ const req = http.request({ hostname: 'example.com', port: 1, path: '/' });
115
+ const hostHeader = req.getHeader('host');
116
+ expect(hostHeader).toBeDefined();
117
+ req.on('error', () => {});
118
+ req.abort();
119
+ });
120
+
121
+ await it('request() should parse path and query from URL', async () => {
122
+ const req = http.request('http://example.com:3000/path?query=1');
123
+ expect(req.path).toBe('/path?query=1');
124
+ req.on('error', () => {});
125
+ req.abort();
126
+ });
127
+
128
+ await it('request() should set custom headers from options', async () => {
129
+ const req = http.request({
130
+ hostname: 'localhost',
131
+ port: 1,
132
+ path: '/',
133
+ headers: { 'X-Custom': 'value', 'Accept': 'text/html' }
134
+ });
135
+ expect(req.getHeader('x-custom')).toBe('value');
136
+ expect(req.getHeader('accept')).toBe('text/html');
137
+ req.on('error', () => {});
138
+ req.abort();
139
+ });
140
+
141
+ await it('ClientRequest should have aborted default to false', async () => {
142
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
143
+ expect(req.aborted).toBe(false);
144
+ req.on('error', () => {});
145
+ req.abort();
146
+ });
147
+
148
+ await it('ClientRequest should set aborted after abort()', async () => {
149
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
150
+ req.on('error', () => {});
151
+ expect(req.aborted).toBe(false);
152
+ req.abort();
153
+ expect(req.aborted).toBe(true);
154
+ });
155
+
156
+ await it('ClientRequest should have reusedSocket default to false', async () => {
157
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
158
+ expect(req.reusedSocket).toBe(false);
159
+ req.on('error', () => {});
160
+ req.abort();
161
+ });
162
+
163
+ await it('ClientRequest should have maxHeadersCount property', async () => {
164
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
165
+ // Node.js defaults to null, GJS defaults to a number — both are valid
166
+ expect(req.maxHeadersCount !== undefined).toBe(true);
167
+ req.on('error', () => {});
168
+ req.abort();
169
+ });
170
+
171
+ await it('ClientRequest should support multiple setHeader calls', async () => {
172
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
173
+ req.setHeader('X-First', 'one');
174
+ req.setHeader('X-Second', 'two');
175
+ req.setHeader('X-Third', 'three');
176
+ expect(req.getHeader('x-first')).toBe('one');
177
+ expect(req.getHeader('x-second')).toBe('two');
178
+ expect(req.getHeader('x-third')).toBe('three');
179
+ req.on('error', () => {});
180
+ req.abort();
181
+ });
182
+
183
+ await it('ClientRequest setHeader should overwrite existing', async () => {
184
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
185
+ req.setHeader('X-Test', 'first');
186
+ req.setHeader('X-Test', 'second');
187
+ expect(req.getHeader('x-test')).toBe('second');
188
+ req.on('error', () => {});
189
+ req.abort();
190
+ });
191
+
192
+ await it('ClientRequest should have getHeaderNames method', async () => {
193
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
194
+ req.setHeader('X-A', 'a');
195
+ req.setHeader('X-B', 'b');
196
+ const names = req.getHeaderNames();
197
+ expect(Array.isArray(names)).toBe(true);
198
+ expect(names.length >= 2).toBe(true);
199
+ req.on('error', () => {});
200
+ req.abort();
201
+ });
202
+
203
+ await it('ClientRequest should have getHeaders method', async () => {
204
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
205
+ req.setHeader('X-Test', 'value');
206
+ const headers = req.getHeaders();
207
+ expect(typeof headers).toBe('object');
208
+ expect(headers['x-test']).toBe('value');
209
+ req.on('error', () => {});
210
+ req.abort();
211
+ });
212
+
213
+ await it('ClientRequest should support setTimeout', async () => {
214
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
215
+ expect(typeof req.setTimeout).toBe('function');
216
+ const result = req.setTimeout(5000);
217
+ expect(result).toBe(req);
218
+ req.on('error', () => {});
219
+ req.abort();
220
+ });
221
+
222
+ await it('ClientRequest should support flushHeaders', async () => {
223
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
224
+ expect(typeof req.flushHeaders).toBe('function');
225
+ req.on('error', () => {});
226
+ req.abort();
227
+ });
228
+
229
+ await it('ClientRequest should have headersSent default to false', async () => {
230
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
231
+ expect(req.headersSent).toBe(false);
232
+ req.on('error', () => {});
233
+ req.abort();
234
+ });
235
+
236
+ await it('ClientRequest should be a Writable stream', async () => {
237
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
238
+ expect(typeof req.write).toBe('function');
239
+ expect(typeof req.end).toBe('function');
240
+ expect(typeof req.on).toBe('function');
241
+ req.on('error', () => {});
242
+ req.abort();
243
+ });
244
+
245
+ await it('request() should handle host option', async () => {
246
+ const req = http.request({ host: 'example.com:3000', path: '/' });
247
+ // host property is set in both Node.js and GJS
248
+ expect(req.host).toBeDefined();
249
+ req.on('error', () => {});
250
+ req.abort();
251
+ });
252
+
253
+ await it('request() with DELETE method', async () => {
254
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/item', method: 'DELETE' });
255
+ expect(req.method).toBe('DELETE');
256
+ req.on('error', () => {});
257
+ req.abort();
258
+ });
259
+
260
+ await it('request() with PUT method', async () => {
261
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/item', method: 'PUT' });
262
+ expect(req.method).toBe('PUT');
263
+ req.on('error', () => {});
264
+ req.abort();
265
+ });
266
+
267
+ await it('request() with PATCH method', async () => {
268
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/item', method: 'PATCH' });
269
+ expect(req.method).toBe('PATCH');
270
+ req.on('error', () => {});
271
+ req.abort();
272
+ });
273
+
274
+ await it('request() with HEAD method', async () => {
275
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/', method: 'HEAD' });
276
+ expect(req.method).toBe('HEAD');
277
+ req.on('error', () => {});
278
+ req.abort();
279
+ });
280
+
281
+ await it('request() with OPTIONS method', async () => {
282
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/', method: 'OPTIONS' });
283
+ expect(req.method).toBe('OPTIONS');
284
+ req.on('error', () => {});
285
+ req.abort();
286
+ });
287
+
288
+ await it('double abort should not throw', async () => {
289
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' });
290
+ req.on('error', () => {});
291
+ req.abort();
292
+ // Second abort should not throw
293
+ expect(() => req.abort()).not.toThrow();
294
+ });
295
+
296
+ await it('ClientRequest should have host property', async () => {
297
+ const req = http.request({ hostname: 'myhost', port: 1234, path: '/' });
298
+ expect(typeof req.host).toBe('string');
299
+ req.on('error', () => {});
300
+ req.abort();
301
+ });
302
+
303
+ await it('request() should handle URL with default port', async () => {
304
+ const req = http.request('http://example.com/path');
305
+ expect(req.host).toBeDefined();
306
+ expect(req.path).toBe('/path');
307
+ req.on('error', () => {});
308
+ req.abort();
309
+ });
310
+ });
311
+
312
+ await describe('http.IncomingMessage', async () => {
313
+ await it('should be exported', async () => {
314
+ expect(typeof http.IncomingMessage).toBe('function');
315
+ });
316
+
317
+ await it('should be constructable as standalone', async () => {
318
+ const msg = new http.IncomingMessage();
319
+ expect(msg).toBeDefined();
320
+ });
321
+
322
+ await it('should inherit from Readable', async () => {
323
+ const msg = new http.IncomingMessage();
324
+ expect(typeof msg.pipe).toBe('function');
325
+ expect(typeof msg.read).toBe('function');
326
+ });
327
+ });
328
+
329
+ await describe('http module exports', async () => {
330
+ await it('should export STATUS_CODES', async () => {
331
+ expect(http.STATUS_CODES).toBeDefined();
332
+ expect(http.STATUS_CODES[200]).toBe('OK');
333
+ expect(http.STATUS_CODES[404]).toBe('Not Found');
334
+ });
335
+
336
+ await it('should export METHODS', async () => {
337
+ expect(http.METHODS).toBeDefined();
338
+ expect(http.METHODS).toContain('GET');
339
+ expect(http.METHODS).toContain('POST');
340
+ });
341
+
342
+ await it('should export maxHeaderSize', async () => {
343
+ expect(http.maxHeaderSize).toBe(16384);
344
+ });
345
+
346
+ await it('should export Agent', async () => {
347
+ expect(typeof http.Agent).toBe('function');
348
+ });
349
+
350
+ await it('should export globalAgent', async () => {
351
+ expect(http.globalAgent).toBeDefined();
352
+ expect(http.globalAgent.defaultPort).toBe(80);
353
+ });
354
+
355
+ await it('should export createServer', async () => {
356
+ expect(typeof http.createServer).toBe('function');
357
+ });
358
+ });
359
+
360
+ await describe('http.Agent extended', async () => {
361
+ await it('should accept options in constructor', async () => {
362
+ const agent = new http.Agent({});
363
+ expect(agent).toBeDefined();
364
+ });
365
+
366
+ await it('multiple agents should be independent', async () => {
367
+ const agent1 = new http.Agent();
368
+ const agent2 = new http.Agent();
369
+ expect(agent1).toBeDefined();
370
+ expect(agent2).toBeDefined();
371
+ expect(agent1 !== agent2).toBe(true);
372
+ });
373
+
374
+ await it('globalAgent should not be the same as a new Agent', async () => {
375
+ const agent = new http.Agent();
376
+ expect(http.globalAgent !== agent).toBe(true);
377
+ });
378
+
379
+ await it('should have all expected properties', async () => {
380
+ const agent = new http.Agent();
381
+ expect(typeof agent.defaultPort).toBe('number');
382
+ expect(typeof agent.maxSockets).toBe('number');
383
+ expect(typeof (agent as any).maxFreeSockets).toBe('number');
384
+ expect(typeof (agent as any).keepAliveMsecs).toBe('number');
385
+ expect(typeof (agent as any).keepAlive).toBe('boolean');
386
+ expect(typeof (agent as any).protocol).toBe('string');
387
+ });
388
+ });
389
+
390
+ await describe('http.request URL parsing', async () => {
391
+ await it('should parse simple URL', async () => {
392
+ const req = http.request('http://localhost:9999/foo');
393
+ expect(req.path).toBe('/foo');
394
+ expect(req.method).toBe('GET');
395
+ req.on('error', () => {});
396
+ req.abort();
397
+ });
398
+
399
+ await it('should parse URL with query string', async () => {
400
+ const req = http.request('http://localhost:1/search?q=test&page=1');
401
+ expect(req.path).toBe('/search?q=test&page=1');
402
+ req.on('error', () => {});
403
+ req.abort();
404
+ });
405
+
406
+ await it('should parse URL with path only', async () => {
407
+ const req = http.request('http://localhost:1/a/b/c');
408
+ expect(req.path).toBe('/a/b/c');
409
+ req.on('error', () => {});
410
+ req.abort();
411
+ });
412
+
413
+ await it('should parse URL with no path', async () => {
414
+ const req = http.request('http://localhost:1');
415
+ expect(req.path).toBe('/');
416
+ req.on('error', () => {});
417
+ req.abort();
418
+ });
419
+
420
+ await it('should handle options with callback', async () => {
421
+ let cbCalled = false;
422
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/' }, () => {
423
+ cbCalled = true;
424
+ });
425
+ expect(req).toBeDefined();
426
+ req.on('error', () => {});
427
+ req.abort();
428
+ });
429
+
430
+ await it('should handle URL string with callback', async () => {
431
+ let cbCalled = false;
432
+ const req = http.request('http://localhost:1/', () => {
433
+ cbCalled = true;
434
+ });
435
+ expect(req).toBeDefined();
436
+ req.on('error', () => {});
437
+ req.abort();
438
+ });
439
+ });
440
+
441
+ // ==================== auth option ====================
442
+ await describe('http.request auth option', async () => {
443
+ await it('should set Authorization header from auth option', async () => {
444
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/', auth: 'user:pass' });
445
+ const authHeader = req.getHeader('authorization');
446
+ expect(authHeader).toBeDefined();
447
+ // Basic base64('user:pass') = 'dXNlcjpwYXNz'
448
+ expect(authHeader).toBe('Basic dXNlcjpwYXNz');
449
+ req.on('error', () => {});
450
+ req.abort();
451
+ });
452
+
453
+ await it('should not override explicit Authorization header', async () => {
454
+ const req = http.request({
455
+ hostname: 'localhost', port: 1, path: '/',
456
+ auth: 'user:pass',
457
+ headers: { 'Authorization': 'Bearer token123' },
458
+ });
459
+ expect(req.getHeader('authorization')).toBe('Bearer token123');
460
+ req.on('error', () => {});
461
+ req.abort();
462
+ });
463
+
464
+ await it('should handle auth with special characters', async () => {
465
+ const req = http.request({ hostname: 'localhost', port: 1, path: '/', auth: 'user:p@ss:word' });
466
+ const authHeader = req.getHeader('authorization');
467
+ expect(authHeader).toBeDefined();
468
+ expect((authHeader as string).startsWith('Basic ')).toBe(true);
469
+ req.on('error', () => {});
470
+ req.abort();
471
+ });
472
+ });
473
+
474
+ // ==================== Agent improvements ====================
475
+ await describe('http.Agent constructor options', async () => {
476
+ await it('should accept keepAlive option', async () => {
477
+ const agent = new http.Agent({ keepAlive: true });
478
+ expect(agent.keepAlive).toBe(true);
479
+ });
480
+
481
+ await it('should accept maxSockets option', async () => {
482
+ const agent = new http.Agent({ maxSockets: 10 });
483
+ expect(agent.maxSockets).toBe(10);
484
+ });
485
+
486
+ await it('should accept maxTotalSockets option', async () => {
487
+ const agent = new http.Agent({ maxTotalSockets: 50 });
488
+ expect(agent.maxTotalSockets).toBe(50);
489
+ });
490
+
491
+ await it('should accept maxFreeSockets option', async () => {
492
+ const agent = new http.Agent({ maxFreeSockets: 100 });
493
+ expect(agent.maxFreeSockets).toBe(100);
494
+ });
495
+
496
+ await it('should accept scheduling option', async () => {
497
+ const agent = new http.Agent({ scheduling: 'fifo' });
498
+ expect(agent.scheduling).toBe('fifo');
499
+ });
500
+
501
+ await it('should have default values', async () => {
502
+ const agent = new http.Agent();
503
+ expect(agent.keepAlive).toBe(false);
504
+ expect(agent.maxSockets).toBe(Infinity);
505
+ expect(agent.maxFreeSockets).toBe(256);
506
+ expect(agent.keepAliveMsecs).toBe(1000);
507
+ expect(agent.scheduling).toBe('lifo');
508
+ });
509
+
510
+ await it('should have requests/sockets/freeSockets objects', async () => {
511
+ const agent = new http.Agent();
512
+ expect(typeof agent.requests).toBe('object');
513
+ expect(typeof agent.sockets).toBe('object');
514
+ expect(typeof agent.freeSockets).toBe('object');
515
+ });
516
+
517
+ await it('destroy() should not throw', async () => {
518
+ const agent = new http.Agent({ keepAlive: true });
519
+ agent.destroy();
520
+ });
521
+ });
522
+
523
+ // ==================== signal option ====================
524
+ await describe('http.request signal option', async () => {
525
+ await it('should support signal option in request', async () => {
526
+ if (typeof AbortController === 'undefined') return; // skip if not available
527
+ const controller = new AbortController();
528
+ const req = http.request({
529
+ hostname: 'localhost', port: 1, path: '/',
530
+ signal: controller.signal,
531
+ });
532
+ expect(req).toBeDefined();
533
+ req.on('error', () => {});
534
+ controller.abort();
535
+ req.destroy();
536
+ });
537
+ });
538
+ };
@@ -0,0 +1,33 @@
1
+ // HTTP status codes and methods
2
+ // Reference: Node.js lib/_http_server.js
3
+
4
+ export const STATUS_CODES: Record<number, string> = {
5
+ 100: 'Continue', 101: 'Switching Protocols', 102: 'Processing', 103: 'Early Hints',
6
+ 200: 'OK', 201: 'Created', 202: 'Accepted', 203: 'Non-Authoritative Information',
7
+ 204: 'No Content', 205: 'Reset Content', 206: 'Partial Content', 207: 'Multi-Status',
8
+ 208: 'Already Reported', 226: 'IM Used',
9
+ 300: 'Multiple Choices', 301: 'Moved Permanently', 302: 'Found', 303: 'See Other',
10
+ 304: 'Not Modified', 305: 'Use Proxy', 307: 'Temporary Redirect', 308: 'Permanent Redirect',
11
+ 400: 'Bad Request', 401: 'Unauthorized', 402: 'Payment Required', 403: 'Forbidden',
12
+ 404: 'Not Found', 405: 'Method Not Allowed', 406: 'Not Acceptable',
13
+ 407: 'Proxy Authentication Required', 408: 'Request Timeout', 409: 'Conflict',
14
+ 410: 'Gone', 411: 'Length Required', 412: 'Precondition Failed',
15
+ 413: 'Payload Too Large', 414: 'URI Too Long', 415: 'Unsupported Media Type',
16
+ 416: 'Range Not Satisfiable', 417: 'Expectation Failed', 418: "I'm a Teapot",
17
+ 421: 'Misdirected Request', 422: 'Unprocessable Entity', 423: 'Locked',
18
+ 424: 'Failed Dependency', 425: 'Too Early', 426: 'Upgrade Required',
19
+ 428: 'Precondition Required', 429: 'Too Many Requests', 431: 'Request Header Fields Too Large',
20
+ 451: 'Unavailable For Legal Reasons',
21
+ 500: 'Internal Server Error', 501: 'Not Implemented', 502: 'Bad Gateway',
22
+ 503: 'Service Unavailable', 504: 'Gateway Timeout', 505: 'HTTP Version Not Supported',
23
+ 506: 'Variant Also Negotiates', 507: 'Insufficient Storage', 508: 'Loop Detected',
24
+ 510: 'Not Extended', 511: 'Network Authentication Required',
25
+ };
26
+
27
+ export const METHODS = [
28
+ 'ACL', 'BIND', 'CHECKOUT', 'CONNECT', 'COPY', 'DELETE', 'GET', 'HEAD',
29
+ 'LINK', 'LOCK', 'M-SEARCH', 'MERGE', 'MKACTIVITY', 'MKCALENDAR', 'MKCOL',
30
+ 'MOVE', 'NOTIFY', 'OPTIONS', 'PATCH', 'POST', 'PRI', 'PROPFIND', 'PROPPATCH',
31
+ 'PURGE', 'PUT', 'REBIND', 'REPORT', 'SEARCH', 'SOURCE', 'SUBSCRIBE',
32
+ 'TRACE', 'UNBIND', 'UNLINK', 'UNLOCK', 'UNSUBSCRIBE',
33
+ ];