@fluojs/testing 1.0.6 → 3.0.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.
Files changed (49) hide show
  1. package/README.ko.md +98 -16
  2. package/README.md +100 -16
  3. package/dist/babel-decorators-plugin.d.ts +1 -0
  4. package/dist/babel-decorators-plugin.d.ts.map +1 -1
  5. package/dist/babel-decorators-plugin.js +1 -0
  6. package/dist/byte-range-portability-consumer.test-fixture.d.ts +2 -0
  7. package/dist/byte-range-portability-consumer.test-fixture.d.ts.map +1 -0
  8. package/dist/byte-range-portability-consumer.test-fixture.js +11 -0
  9. package/dist/conformance/fetch-style-websocket-conformance.d.ts +1 -0
  10. package/dist/conformance/fetch-style-websocket-conformance.d.ts.map +1 -1
  11. package/dist/conformance/fetch-style-websocket-conformance.js +1 -1
  12. package/dist/conformance/platform-shell-lifecycle-conformance.d.ts +30 -0
  13. package/dist/conformance/platform-shell-lifecycle-conformance.d.ts.map +1 -0
  14. package/dist/conformance/platform-shell-lifecycle-conformance.js +259 -0
  15. package/dist/http.d.ts +2 -1
  16. package/dist/http.d.ts.map +1 -1
  17. package/dist/http.js +8 -3
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -2
  21. package/dist/mock-types.d.ts +2 -0
  22. package/dist/mock-types.d.ts.map +1 -0
  23. package/dist/mock-types.js +1 -0
  24. package/dist/mock.d.ts +3 -2
  25. package/dist/mock.d.ts.map +1 -1
  26. package/dist/mock.js +1 -1
  27. package/dist/module.d.ts.map +1 -1
  28. package/dist/module.js +103 -113
  29. package/dist/portability/error-representation-abort-portability.d.ts +36 -0
  30. package/dist/portability/error-representation-abort-portability.d.ts.map +1 -0
  31. package/dist/portability/error-representation-abort-portability.js +190 -0
  32. package/dist/portability/error-representation-portability-fixture.d.ts +58 -0
  33. package/dist/portability/error-representation-portability-fixture.d.ts.map +1 -0
  34. package/dist/portability/error-representation-portability-fixture.js +202 -0
  35. package/dist/portability/error-representation-portability.d.ts +50 -0
  36. package/dist/portability/error-representation-portability.d.ts.map +1 -0
  37. package/dist/portability/error-representation-portability.js +153 -0
  38. package/dist/portability/http-adapter-portability.d.ts +25 -8
  39. package/dist/portability/http-adapter-portability.d.ts.map +1 -1
  40. package/dist/portability/http-adapter-portability.js +446 -69
  41. package/dist/portability/response-cookie-portability.d.ts +16 -0
  42. package/dist/portability/response-cookie-portability.d.ts.map +1 -0
  43. package/dist/portability/response-cookie-portability.js +81 -0
  44. package/dist/portability/web-runtime-adapter-portability.d.ts +24 -8
  45. package/dist/portability/web-runtime-adapter-portability.d.ts.map +1 -1
  46. package/dist/portability/web-runtime-adapter-portability.js +356 -31
  47. package/dist/types.d.ts +68 -4
  48. package/dist/types.d.ts.map +1 -1
  49. package/package.json +19 -15
@@ -3,9 +3,11 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
3
3
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4
4
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
5
5
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
6
- import { request as httpsRequest } from 'node:https';
7
- import { Controller, Get, Post, SseResponse } from '@fluojs/http';
6
+ import { Controller, Get, Head, Post, Produces, Query, Route, SseResponse } from '@fluojs/http';
8
7
  import { defineModule } from '@fluojs/runtime';
8
+ import { assertNetworkHttpErrorRepresentationAbortPortability } from './error-representation-abort-portability.js';
9
+ import { assertNetworkHttpErrorRepresentationPortability } from './error-representation-portability.js';
10
+ import { assertPortableResponseCookies, createResponseCookiePortabilityModule } from './response-cookie-portability.js';
9
11
 
10
12
  /**
11
13
  * Options for configuring the HTTP adapter portability harness.
@@ -37,6 +39,11 @@ function resolveListeningUrl(app, adapterName) {
37
39
  return target.url;
38
40
  }
39
41
  async function requestHttps(url) {
42
+ const [{
43
+ Buffer
44
+ }, {
45
+ request: httpsRequest
46
+ }] = await Promise.all([import('node:buffer'), import('node:https')]);
40
47
  return await new Promise((resolve, reject) => {
41
48
  const request = httpsRequest(url, {
42
49
  rejectUnauthorized: false
@@ -57,6 +64,41 @@ async function requestHttps(url) {
57
64
  request.end();
58
65
  });
59
66
  }
67
+ async function requestCustomHttpMethod(url, method, body) {
68
+ const [{
69
+ Buffer
70
+ }, {
71
+ request: httpRequest
72
+ }] = await Promise.all([import('node:buffer'), import('node:http')]);
73
+ const target = new URL(url);
74
+ const hostname = target.hostname.startsWith('[') && target.hostname.endsWith(']') ? target.hostname.slice(1, -1) : target.hostname;
75
+ return await new Promise((resolve, reject) => {
76
+ const request = httpRequest({
77
+ headers: {
78
+ 'content-length': String(Buffer.byteLength(body)),
79
+ 'content-type': 'application/json'
80
+ },
81
+ hostname,
82
+ method,
83
+ path: `${target.pathname}${target.search}`,
84
+ port: target.port
85
+ }, response => {
86
+ const chunks = [];
87
+ response.on('data', chunk => {
88
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
89
+ });
90
+ response.on('end', () => {
91
+ resolve({
92
+ body: Buffer.concat(chunks).toString('utf8'),
93
+ statusCode: response.statusCode ?? 0
94
+ });
95
+ });
96
+ response.on('error', reject);
97
+ });
98
+ request.on('error', reject);
99
+ request.end(body);
100
+ });
101
+ }
60
102
  function createLogCaptureLogger() {
61
103
  const messages = [];
62
104
  const capture = (...args) => messages.push(args.map(arg => String(arg)).join(' '));
@@ -141,28 +183,289 @@ export class HttpAdapterPortabilityHarness {
141
183
  this.options = options;
142
184
  }
143
185
 
186
+ /** Verifies JSON, HTML, HEAD, 406, and committed error-response portability. */
187
+ async assertSupportsHttpErrorRepresentations() {
188
+ const createBootstrapOptions = this.options.createErrorRepresentationBootstrapOptions;
189
+ if (createBootstrapOptions === undefined) {
190
+ throw new Error(`${this.options.name} adapter portability harness requires createErrorRepresentationBootstrapOptions.`);
191
+ }
192
+ await assertNetworkHttpErrorRepresentationPortability({
193
+ bootstrap: this.options.bootstrap,
194
+ createBootstrapOptions,
195
+ name: this.options.name
196
+ });
197
+ }
198
+
199
+ /** Verifies client-disconnect abort propagation without an HTML or JSON fallback commit. */
200
+ async assertDoesNotCommitAbortedHttpErrorRepresentations() {
201
+ const createBootstrapOptions = this.options.createErrorRepresentationBootstrapOptions;
202
+ if (createBootstrapOptions === undefined) {
203
+ throw new Error(`${this.options.name} adapter portability harness requires createErrorRepresentationBootstrapOptions.`);
204
+ }
205
+ await assertNetworkHttpErrorRepresentationAbortPortability({
206
+ bootstrap: this.options.bootstrap,
207
+ createBootstrapOptions,
208
+ name: this.options.name
209
+ });
210
+ }
211
+
212
+ /** Verifies ordered, non-folded portable response cookies over a real listener. */
213
+ async assertSupportsPortableResponseCookies() {
214
+ const app = await this.options.bootstrap(createResponseCookiePortabilityModule(), {
215
+ cors: false,
216
+ port: 0
217
+ });
218
+ await prepareAndListenWithCleanup(app, this.options.name);
219
+ await runWithListeningUrlCleanup(app, this.options.name, async url => {
220
+ assertPortableResponseCookies(await fetch(`${url}/response-cookies`), this.options.name);
221
+ });
222
+ }
223
+
224
+ /** Verifies 304/412 metadata and body suppression through a real network listener. */
225
+ async assertSupportsConditionalRequests() {
226
+ let _initProto, _initClass;
227
+ const createBootstrapOptions = this.options.createConditionalRequestBootstrapOptions;
228
+ if (createBootstrapOptions === undefined) {
229
+ throw new Error(`${this.options.name} adapter portability harness requires createConditionalRequestBootstrapOptions.`);
230
+ }
231
+ let _ValidatorsController;
232
+ class ValidatorsController {
233
+ static {
234
+ ({
235
+ e: [_initProto],
236
+ c: [_ValidatorsController, _initClass]
237
+ } = _applyDecs(this, [Controller('/validators')], [[[Produces('application/json'), Get('/resource')], 2, "getResource"], [[Produces('application/json'), Head('/resource')], 2, "headResource"], [Post('/resource'), 2, "updateResource"]]));
238
+ }
239
+ constructor() {
240
+ _initProto(this);
241
+ }
242
+ getResource() {
243
+ return {
244
+ id: 'resource'
245
+ };
246
+ }
247
+ headResource() {
248
+ return {
249
+ id: 'resource'
250
+ };
251
+ }
252
+ updateResource() {
253
+ return {
254
+ id: 'resource'
255
+ };
256
+ }
257
+ static {
258
+ _initClass();
259
+ }
260
+ }
261
+ class AppModule {}
262
+ defineModule(AppModule, {
263
+ controllers: [_ValidatorsController]
264
+ });
265
+ const app = await this.options.bootstrap(AppModule, createBootstrapOptions({
266
+ conditionalRequest: {
267
+ resolve() {
268
+ return {
269
+ exists: true,
270
+ validators: {
271
+ etag: {
272
+ opaqueValue: 'resource-v1',
273
+ strength: 'strong'
274
+ },
275
+ lastModified: new Date('2026-01-01T00:00:00.750Z')
276
+ }
277
+ };
278
+ }
279
+ },
280
+ contentNegotiation: {
281
+ formatters: [{
282
+ format(body) {
283
+ return JSON.stringify(body);
284
+ },
285
+ mediaType: 'application/json'
286
+ }]
287
+ },
288
+ cors: false,
289
+ port: 0
290
+ }));
291
+ await prepareAndListenWithCleanup(app, this.options.name);
292
+ await runWithListeningUrlCleanup(app, this.options.name, async baseUrl => {
293
+ const [notModified, preconditionFailed, head] = await Promise.all([fetch(`${baseUrl}/validators/resource`, {
294
+ headers: {
295
+ 'if-none-match': '"resource-v1"'
296
+ }
297
+ }), fetch(`${baseUrl}/validators/resource`, {
298
+ headers: {
299
+ 'if-match': '"different-resource"'
300
+ },
301
+ method: 'POST'
302
+ }), fetch(`${baseUrl}/validators/resource`, {
303
+ headers: {
304
+ 'if-none-match': '"resource-v1"'
305
+ },
306
+ method: 'HEAD'
307
+ })]);
308
+ if (notModified.status !== 304 || preconditionFailed.status !== 412 || head.status !== 304 || (await notModified.text()) !== '' || (await preconditionFailed.text()) !== '' || (await head.text()) !== '' || notModified.headers.get('etag') !== '"resource-v1"' || notModified.headers.get('last-modified') !== 'Thu, 01 Jan 2026 00:00:00 GMT' || notModified.headers.get('vary') !== 'Accept' || preconditionFailed.headers.get('etag') !== '"resource-v1"' || preconditionFailed.headers.get('last-modified') !== 'Thu, 01 Jan 2026 00:00:00 GMT' || head.headers.get('etag') !== '"resource-v1"' || head.headers.get('last-modified') !== 'Thu, 01 Jan 2026 00:00:00 GMT' || head.headers.get('vary') !== 'Accept') {
309
+ throw new Error(`${this.options.name} adapter changed conditional request response semantics.`);
310
+ }
311
+ });
312
+ }
313
+
314
+ /** Verifies single-byte-range metadata and payload slicing through a real network listener. */
315
+ async assertSupportsSingleByteRanges() {
316
+ let _initProto2, _initClass2;
317
+ const createBootstrapOptions = this.options.createConditionalRequestBootstrapOptions;
318
+ if (createBootstrapOptions === undefined) {
319
+ throw new Error(`${this.options.name} adapter portability harness requires createConditionalRequestBootstrapOptions.`);
320
+ }
321
+ let _AssetController;
322
+ class AssetController {
323
+ static {
324
+ ({
325
+ e: [_initProto2],
326
+ c: [_AssetController, _initClass2]
327
+ } = _applyDecs(this, [Controller('/assets')], [[[Produces('application/octet-stream'), Get('/logo')], 2, "getLogo"], [[Produces('application/octet-stream'), Head('/logo')], 2, "headLogo"], [[Produces('application/octet-stream'), Post('/logo')], 2, "postLogo"]]));
328
+ }
329
+ constructor() {
330
+ _initProto2(this);
331
+ }
332
+ getLogo() {
333
+ return Uint8Array.from([0, 1, 2, 3, 4, 5]);
334
+ }
335
+ headLogo() {
336
+ return Uint8Array.from([0, 1, 2, 3, 4, 5]);
337
+ }
338
+ postLogo() {
339
+ return Uint8Array.from([0, 1, 2, 3, 4, 5]);
340
+ }
341
+ static {
342
+ _initClass2();
343
+ }
344
+ }
345
+ class AppModule {}
346
+ defineModule(AppModule, {
347
+ controllers: [_AssetController]
348
+ });
349
+ const app = await this.options.bootstrap(AppModule, createBootstrapOptions({
350
+ conditionalRequest: {
351
+ resolve() {
352
+ return {
353
+ exists: true,
354
+ validators: {
355
+ etag: {
356
+ opaqueValue: 'asset-v1',
357
+ strength: 'strong'
358
+ },
359
+ lastModified: new Date('2026-01-01T00:00:00.750Z')
360
+ }
361
+ };
362
+ }
363
+ },
364
+ contentNegotiation: {
365
+ formatters: [{
366
+ format(body) {
367
+ return JSON.stringify(body);
368
+ },
369
+ mediaType: 'application/json'
370
+ }, {
371
+ format(body) {
372
+ if (!(body instanceof Uint8Array)) {
373
+ throw new Error('Expected byte-range formatter to receive a Uint8Array.');
374
+ }
375
+ return body;
376
+ },
377
+ mediaType: 'application/octet-stream'
378
+ }]
379
+ },
380
+ cors: false,
381
+ port: 0
382
+ }));
383
+ await prepareAndListenWithCleanup(app, this.options.name);
384
+ await runWithListeningUrlCleanup(app, this.options.name, async baseUrl => {
385
+ const [bounded, suffix, openEnded, malformed, multiple, unsatisfiable, head, post, matchingEtag, nonmatchingEtag, matchingDate, nonmatchingDate] = await Promise.all([fetch(`${baseUrl}/assets/logo`, {
386
+ headers: {
387
+ range: 'bytes=2-4'
388
+ }
389
+ }), fetch(`${baseUrl}/assets/logo`, {
390
+ headers: {
391
+ range: 'bytes=-2'
392
+ }
393
+ }), fetch(`${baseUrl}/assets/logo`, {
394
+ headers: {
395
+ range: 'bytes=3-'
396
+ }
397
+ }), fetch(`${baseUrl}/assets/logo`, {
398
+ headers: {
399
+ range: 'items=2-4'
400
+ }
401
+ }), fetch(`${baseUrl}/assets/logo`, {
402
+ headers: {
403
+ range: 'bytes=0-1,3-4'
404
+ }
405
+ }), fetch(`${baseUrl}/assets/logo`, {
406
+ headers: {
407
+ range: 'bytes=9-'
408
+ }
409
+ }), fetch(`${baseUrl}/assets/logo`, {
410
+ headers: {
411
+ range: 'bytes=2-4'
412
+ },
413
+ method: 'HEAD'
414
+ }), fetch(`${baseUrl}/assets/logo`, {
415
+ headers: {
416
+ range: 'bytes=2-4'
417
+ },
418
+ method: 'POST'
419
+ }), fetch(`${baseUrl}/assets/logo`, {
420
+ headers: {
421
+ 'if-range': '"asset-v1"',
422
+ range: 'bytes=2-4'
423
+ }
424
+ }), fetch(`${baseUrl}/assets/logo`, {
425
+ headers: {
426
+ 'if-range': '"different-asset"',
427
+ range: 'bytes=2-4'
428
+ }
429
+ }), fetch(`${baseUrl}/assets/logo`, {
430
+ headers: {
431
+ 'if-range': 'Thu, 01 Jan 2026 00:00:00 GMT',
432
+ range: 'bytes=2-4'
433
+ }
434
+ }), fetch(`${baseUrl}/assets/logo`, {
435
+ headers: {
436
+ 'if-range': 'Wed, 31 Dec 2025 23:59:59 GMT',
437
+ range: 'bytes=2-4'
438
+ }
439
+ })]);
440
+ const [boundedBytes, suffixBytes, openEndedBytes, malformedBytes, multipleBytes, unsatisfiableBody, headBody, postBytes, matchingEtagBytes, nonmatchingEtagBytes, matchingDateBytes, nonmatchingDateBytes] = await Promise.all([bounded.bytes(), suffix.bytes(), openEnded.bytes(), malformed.bytes(), multiple.bytes(), unsatisfiable.text(), head.text(), post.bytes(), matchingEtag.bytes(), nonmatchingEtag.bytes(), matchingDate.bytes(), nonmatchingDate.bytes()]);
441
+ if (bounded.status !== 206 || suffix.status !== 206 || openEnded.status !== 206 || malformed.status !== 200 || multiple.status !== 200 || unsatisfiable.status !== 416 || head.status !== 206 || post.status !== 201 || matchingEtag.status !== 206 || nonmatchingEtag.status !== 200 || matchingDate.status !== 206 || nonmatchingDate.status !== 200 || bounded.headers.get('accept-ranges') !== 'bytes' || bounded.headers.get('content-range') !== 'bytes 2-4/6' || bounded.headers.get('content-length') !== '3' || suffix.headers.get('content-range') !== 'bytes 4-5/6' || openEnded.headers.get('content-range') !== 'bytes 3-5/6' || openEnded.headers.get('content-length') !== '3' || unsatisfiable.headers.get('accept-ranges') !== 'bytes' || unsatisfiable.headers.get('content-range') !== 'bytes */6' || unsatisfiable.headers.get('content-length') !== '0' || unsatisfiableBody !== '' || head.headers.get('content-range') !== bounded.headers.get('content-range') || head.headers.get('content-length') !== bounded.headers.get('content-length') || headBody !== '' || matchingEtag.headers.get('content-range') !== 'bytes 2-4/6' || matchingEtag.headers.get('etag') !== '"asset-v1"' || matchingDate.headers.get('content-range') !== 'bytes 2-4/6' || matchingDate.headers.get('last-modified') !== 'Thu, 01 Jan 2026 00:00:00 GMT' || !equalByteArrays(boundedBytes, Uint8Array.from([2, 3, 4])) || !equalByteArrays(suffixBytes, Uint8Array.from([4, 5])) || !equalByteArrays(openEndedBytes, Uint8Array.from([3, 4, 5])) || !equalByteArrays(malformedBytes, Uint8Array.from([0, 1, 2, 3, 4, 5])) || !equalByteArrays(multipleBytes, Uint8Array.from([0, 1, 2, 3, 4, 5])) || !equalByteArrays(postBytes, Uint8Array.from([0, 1, 2, 3, 4, 5])) || !equalByteArrays(matchingEtagBytes, Uint8Array.from([2, 3, 4])) || !equalByteArrays(nonmatchingEtagBytes, Uint8Array.from([0, 1, 2, 3, 4, 5])) || !equalByteArrays(matchingDateBytes, Uint8Array.from([2, 3, 4])) || !equalByteArrays(nonmatchingDateBytes, Uint8Array.from([0, 1, 2, 3, 4, 5]))) {
442
+ throw new Error(`${this.options.name} adapter changed single byte-range or If-Range response semantics.`);
443
+ }
444
+ });
445
+ }
446
+
144
447
  /**
145
448
  * Asserts that the adapter preserves malformed cookie values without crashing
146
449
  * or incorrectly normalizing them.
147
450
  */
148
451
  async assertPreservesMalformedCookieValues() {
149
- let _initProto, _initClass;
452
+ let _initProto3, _initClass3;
150
453
  let _CookieController;
151
454
  class CookieController {
152
455
  static {
153
456
  ({
154
- e: [_initProto],
155
- c: [_CookieController, _initClass]
457
+ e: [_initProto3],
458
+ c: [_CookieController, _initClass3]
156
459
  } = _applyDecs(this, [Controller('/cookies')], [[Get('/'), 2, "readCookies"]]));
157
460
  }
158
461
  constructor() {
159
- _initProto(this);
462
+ _initProto3(this);
160
463
  }
161
464
  readCookies(_input, context) {
162
465
  return context.request.cookies;
163
466
  }
164
467
  static {
165
- _initClass();
468
+ _initClass3();
166
469
  }
167
470
  }
168
471
  class AppModule {}
@@ -189,18 +492,78 @@ export class HttpAdapterPortabilityHarness {
189
492
  }
190
493
  });
191
494
  }
495
+
496
+ /** Verifies `QUERY` and extension-method execution through the adapter's real listener fallback. */
497
+ async assertSupportsCustomHttpRouteMethods() {
498
+ let _initProto4, _initClass4;
499
+ let _CustomMethodControll;
500
+ class CustomMethodController {
501
+ static {
502
+ ({
503
+ e: [_initProto4],
504
+ c: [_CustomMethodControll, _initClass4]
505
+ } = _applyDecs(this, [Controller('/custom-methods')], [[Query('/query'), 2, "query"], [Route('PURGE', '/purge'), 2, "purge"]]));
506
+ }
507
+ constructor() {
508
+ _initProto4(this);
509
+ }
510
+ query(_input, context) {
511
+ return {
512
+ body: context.request.body,
513
+ method: context.request.method
514
+ };
515
+ }
516
+ purge(_input, context) {
517
+ return {
518
+ body: context.request.body,
519
+ method: context.request.method
520
+ };
521
+ }
522
+ static {
523
+ _initClass4();
524
+ }
525
+ }
526
+ class AppModule {}
527
+ defineModule(AppModule, {
528
+ controllers: [_CustomMethodControll]
529
+ });
530
+ const app = await this.options.bootstrap(AppModule, {
531
+ cors: false,
532
+ port: 0
533
+ });
534
+ await prepareAndListenWithCleanup(app, this.options.name);
535
+ await runWithListeningUrlCleanup(app, this.options.name, async baseUrl => {
536
+ for (const method of ['QUERY', 'PURGE']) {
537
+ const body = JSON.stringify({
538
+ value: method.toLowerCase()
539
+ });
540
+ const response = await requestCustomHttpMethod(`${baseUrl}/custom-methods/${method.toLowerCase()}`, method, body);
541
+ if (response.statusCode !== 200) {
542
+ throw new Error(`${this.options.name} adapter changed ${method} response status semantics: received ${String(response.statusCode)}.`);
543
+ }
544
+ if (JSON.stringify(JSON.parse(response.body)) !== JSON.stringify({
545
+ body: {
546
+ value: method.toLowerCase()
547
+ },
548
+ method
549
+ })) {
550
+ throw new Error(`${this.options.name} adapter changed ${method} method or body semantics.`);
551
+ }
552
+ }
553
+ });
554
+ }
192
555
  async assertPreservesRawBodyForJsonAndText() {
193
- let _initProto2, _initClass2;
556
+ let _initProto5, _initClass5;
194
557
  let _WebhookController;
195
558
  class WebhookController {
196
559
  static {
197
560
  ({
198
- e: [_initProto2],
199
- c: [_WebhookController, _initClass2]
561
+ e: [_initProto5],
562
+ c: [_WebhookController, _initClass5]
200
563
  } = _applyDecs(this, [Controller('/webhooks')], [[Post('/json'), 2, "handleJson"], [Post('/text'), 2, "handleText"]]));
201
564
  }
202
565
  constructor() {
203
- _initProto2(this);
566
+ _initProto5(this);
204
567
  }
205
568
  handleJson(_input, context) {
206
569
  return {
@@ -215,7 +578,7 @@ export class HttpAdapterPortabilityHarness {
215
578
  };
216
579
  }
217
580
  static {
218
- _initClass2();
581
+ _initClass5();
219
582
  }
220
583
  }
221
584
  class AppModule {}
@@ -265,17 +628,17 @@ export class HttpAdapterPortabilityHarness {
265
628
  });
266
629
  }
267
630
  async assertPreservesExactRawBodyBytesForByteSensitivePayloads() {
268
- let _initProto3, _initClass3;
631
+ let _initProto6, _initClass6;
269
632
  let _WebhookController2;
270
633
  class WebhookController {
271
634
  static {
272
635
  ({
273
- e: [_initProto3],
274
- c: [_WebhookController2, _initClass3]
636
+ e: [_initProto6],
637
+ c: [_WebhookController2, _initClass6]
275
638
  } = _applyDecs(this, [Controller('/webhooks')], [[Post('/bytes'), 2, "handleBytes"]]));
276
639
  }
277
640
  constructor() {
278
- _initProto3(this);
641
+ _initProto6(this);
279
642
  }
280
643
  handleBytes(_input, context) {
281
644
  return {
@@ -283,7 +646,7 @@ export class HttpAdapterPortabilityHarness {
283
646
  };
284
647
  }
285
648
  static {
286
- _initClass3();
649
+ _initClass6();
287
650
  }
288
651
  }
289
652
  class AppModule {}
@@ -320,17 +683,17 @@ export class HttpAdapterPortabilityHarness {
320
683
  });
321
684
  }
322
685
  async assertExcludesRawBodyForMultipart() {
323
- let _initProto4, _initClass4;
686
+ let _initProto7, _initClass7;
324
687
  let _UploadController;
325
688
  class UploadController {
326
689
  static {
327
690
  ({
328
- e: [_initProto4],
329
- c: [_UploadController, _initClass4]
691
+ e: [_initProto7],
692
+ c: [_UploadController, _initClass7]
330
693
  } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
331
694
  }
332
695
  constructor() {
333
- _initProto4(this);
696
+ _initProto7(this);
334
697
  }
335
698
  upload(_input, context) {
336
699
  return {
@@ -340,7 +703,7 @@ export class HttpAdapterPortabilityHarness {
340
703
  };
341
704
  }
342
705
  static {
343
- _initClass4();
706
+ _initClass7();
344
707
  }
345
708
  }
346
709
  class AppModule {}
@@ -379,17 +742,17 @@ export class HttpAdapterPortabilityHarness {
379
742
  });
380
743
  }
381
744
  async assertDefaultsMultipartTotalLimitToMaxBodySize() {
382
- let _initProto5, _initClass5;
745
+ let _initProto8, _initClass8;
383
746
  let _UploadController2;
384
747
  class UploadController {
385
748
  static {
386
749
  ({
387
- e: [_initProto5],
388
- c: [_UploadController2, _initClass5]
750
+ e: [_initProto8],
751
+ c: [_UploadController2, _initClass8]
389
752
  } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
390
753
  }
391
754
  constructor() {
392
- _initProto5(this);
755
+ _initProto8(this);
393
756
  }
394
757
  upload(_input, context) {
395
758
  return {
@@ -398,7 +761,7 @@ export class HttpAdapterPortabilityHarness {
398
761
  };
399
762
  }
400
763
  static {
401
- _initClass5();
764
+ _initClass8();
402
765
  }
403
766
  }
404
767
  class AppModule {}
@@ -434,17 +797,21 @@ export class HttpAdapterPortabilityHarness {
434
797
  });
435
798
  }
436
799
  async assertSupportsSseStreaming() {
437
- let _initProto6, _initClass6;
800
+ let _initProto9, _initClass9;
801
+ let resolveHandlerReady;
802
+ const handlerReady = new Promise(resolve => {
803
+ resolveHandlerReady = resolve;
804
+ });
438
805
  let _EventsController;
439
806
  class EventsController {
440
807
  static {
441
808
  ({
442
- e: [_initProto6],
443
- c: [_EventsController, _initClass6]
809
+ e: [_initProto9],
810
+ c: [_EventsController, _initClass9]
444
811
  } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
445
812
  }
446
813
  constructor() {
447
- _initProto6(this);
814
+ _initProto9(this);
448
815
  }
449
816
  stream(_input, context) {
450
817
  const stream = new SseResponse(context);
@@ -455,13 +822,11 @@ export class HttpAdapterPortabilityHarness {
455
822
  event: 'ready',
456
823
  id: 'evt-1'
457
824
  });
458
- queueMicrotask(() => {
459
- stream.close();
460
- });
825
+ resolveHandlerReady(stream);
461
826
  return stream;
462
827
  }
463
828
  static {
464
- _initClass6();
829
+ _initClass9();
465
830
  }
466
831
  }
467
832
  class AppModule {}
@@ -474,21 +839,30 @@ export class HttpAdapterPortabilityHarness {
474
839
  });
475
840
  await prepareAndListenWithCleanup(app, this.options.name);
476
841
  await runWithListeningUrlCleanup(app, this.options.name, async baseUrl => {
477
- const response = await fetch(`${baseUrl}/events`, {
478
- headers: {
479
- accept: 'text/event-stream'
842
+ const client = new AbortController();
843
+ try {
844
+ const responsePromise = fetch(`${baseUrl}/events`, {
845
+ headers: {
846
+ accept: 'text/event-stream'
847
+ },
848
+ signal: client.signal
849
+ });
850
+ const stream = await withTimeout(handlerReady, 2_000, `${this.options.name} adapter did not enter the SSE handler.`);
851
+ stream.close();
852
+ const response = await responsePromise;
853
+ const body = await withTimeout(response.text(), 2_000, `${this.options.name} adapter did not close the SSE response stream.`);
854
+ if (response.status !== 200) {
855
+ throw new Error(`${this.options.name} adapter changed SSE response status semantics.`);
480
856
  }
481
- });
482
- const body = await response.text();
483
- if (response.status !== 200) {
484
- throw new Error(`${this.options.name} adapter changed SSE response status semantics.`);
485
- }
486
- const contentType = response.headers.get('content-type') ?? '';
487
- if (!contentType.includes('text/event-stream')) {
488
- throw new Error(`${this.options.name} adapter does not expose text/event-stream content-type.`);
489
- }
490
- if (!body.includes('event: ready') || !body.includes('data: {"ready":true}')) {
491
- throw new Error(`${this.options.name} adapter changed SSE body framing.`);
857
+ const contentType = response.headers.get('content-type') ?? '';
858
+ if (!contentType.includes('text/event-stream')) {
859
+ throw new Error(`${this.options.name} adapter does not expose text/event-stream content-type.`);
860
+ }
861
+ if (!body.includes('event: ready') || !body.includes('data: {"ready":true}')) {
862
+ throw new Error(`${this.options.name} adapter changed SSE body framing.`);
863
+ }
864
+ } finally {
865
+ client.abort();
492
866
  }
493
867
  });
494
868
  }
@@ -498,7 +872,7 @@ export class HttpAdapterPortabilityHarness {
498
872
  * closes before a `drain` event is emitted.
499
873
  */
500
874
  async assertSettlesStreamDrainWaitOnClose() {
501
- let _initProto7, _initClass7;
875
+ let _initProto0, _initClass0;
502
876
  const adapterName = this.options.name;
503
877
  let resolveDrainWait;
504
878
  const drainWaitSettled = new Promise(resolve => {
@@ -508,12 +882,12 @@ export class HttpAdapterPortabilityHarness {
508
882
  class EventsController {
509
883
  static {
510
884
  ({
511
- e: [_initProto7],
512
- c: [_EventsController2, _initClass7]
885
+ e: [_initProto0],
886
+ c: [_EventsController2, _initClass0]
513
887
  } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
514
888
  }
515
889
  constructor() {
516
- _initProto7(this);
890
+ _initProto0(this);
517
891
  }
518
892
  async stream(_input, context) {
519
893
  const stream = new SseResponse(context);
@@ -528,7 +902,7 @@ export class HttpAdapterPortabilityHarness {
528
902
  return stream;
529
903
  }
530
904
  static {
531
- _initClass7();
905
+ _initClass0();
532
906
  }
533
907
  }
534
908
  class AppModule {}
@@ -554,18 +928,18 @@ export class HttpAdapterPortabilityHarness {
554
928
  });
555
929
  }
556
930
  async assertReportsConfiguredHostInStartupLogs() {
557
- let _initProto8, _initClass8;
931
+ let _initProto1, _initClass1;
558
932
  const logger = createLogCaptureLogger();
559
933
  let _HealthController;
560
934
  class HealthController {
561
935
  static {
562
936
  ({
563
- e: [_initProto8],
564
- c: [_HealthController, _initClass8]
937
+ e: [_initProto1],
938
+ c: [_HealthController, _initClass1]
565
939
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
566
940
  }
567
941
  constructor() {
568
- _initProto8(this);
942
+ _initProto1(this);
569
943
  }
570
944
  getHealth() {
571
945
  return {
@@ -573,7 +947,7 @@ export class HttpAdapterPortabilityHarness {
573
947
  };
574
948
  }
575
949
  static {
576
- _initClass8();
950
+ _initClass1();
577
951
  }
578
952
  }
579
953
  class AppModule {}
@@ -603,18 +977,18 @@ export class HttpAdapterPortabilityHarness {
603
977
  });
604
978
  }
605
979
  async assertReportsHttpsStartupUrl(https) {
606
- let _initProto9, _initClass9;
980
+ let _initProto10, _initClass10;
607
981
  const logger = createLogCaptureLogger();
608
982
  let _HealthController2;
609
983
  class HealthController {
610
984
  static {
611
985
  ({
612
- e: [_initProto9],
613
- c: [_HealthController2, _initClass9]
986
+ e: [_initProto10],
987
+ c: [_HealthController2, _initClass10]
614
988
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
615
989
  }
616
990
  constructor() {
617
- _initProto9(this);
991
+ _initProto10(this);
618
992
  }
619
993
  getHealth() {
620
994
  return {
@@ -622,7 +996,7 @@ export class HttpAdapterPortabilityHarness {
622
996
  };
623
997
  }
624
998
  static {
625
- _initClass9();
999
+ _initClass10();
626
1000
  }
627
1001
  }
628
1002
  class AppModule {}
@@ -652,7 +1026,7 @@ export class HttpAdapterPortabilityHarness {
652
1026
  });
653
1027
  }
654
1028
  async assertRemovesShutdownSignalListenersAfterClose() {
655
- let _initProto0, _initClass0;
1029
+ let _initProto11, _initClass11;
656
1030
  const logger = {
657
1031
  debug() {},
658
1032
  error() {},
@@ -663,12 +1037,12 @@ export class HttpAdapterPortabilityHarness {
663
1037
  class HealthController {
664
1038
  static {
665
1039
  ({
666
- e: [_initProto0],
667
- c: [_HealthController3, _initClass0]
1040
+ e: [_initProto11],
1041
+ c: [_HealthController3, _initClass11]
668
1042
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
669
1043
  }
670
1044
  constructor() {
671
- _initProto0(this);
1045
+ _initProto11(this);
672
1046
  }
673
1047
  getHealth() {
674
1048
  return {
@@ -676,7 +1050,7 @@ export class HttpAdapterPortabilityHarness {
676
1050
  };
677
1051
  }
678
1052
  static {
679
- _initClass0();
1053
+ _initClass11();
680
1054
  }
681
1055
  }
682
1056
  class AppModule {}
@@ -717,6 +1091,9 @@ export class HttpAdapterPortabilityHarness {
717
1091
  export function createHttpAdapterPortabilityHarness(options) {
718
1092
  return new HttpAdapterPortabilityHarness(options);
719
1093
  }
1094
+ function equalByteArrays(left, right) {
1095
+ return left.byteLength === right.byteLength && left.every((value, index) => value === right[index]);
1096
+ }
720
1097
  async function withTimeout(promise, timeoutMs, message) {
721
1098
  let timeout;
722
1099
  const timeoutPromise = new Promise((_resolve, reject) => {