@fluojs/testing 2.0.0 → 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 (41) hide show
  1. package/README.ko.md +94 -12
  2. package/README.md +96 -12
  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/mock.js +1 -1
  19. package/dist/module.d.ts.map +1 -1
  20. package/dist/module.js +45 -73
  21. package/dist/portability/error-representation-abort-portability.d.ts +36 -0
  22. package/dist/portability/error-representation-abort-portability.d.ts.map +1 -0
  23. package/dist/portability/error-representation-abort-portability.js +190 -0
  24. package/dist/portability/error-representation-portability-fixture.d.ts +58 -0
  25. package/dist/portability/error-representation-portability-fixture.d.ts.map +1 -0
  26. package/dist/portability/error-representation-portability-fixture.js +202 -0
  27. package/dist/portability/error-representation-portability.d.ts +50 -0
  28. package/dist/portability/error-representation-portability.d.ts.map +1 -0
  29. package/dist/portability/error-representation-portability.js +153 -0
  30. package/dist/portability/http-adapter-portability.d.ts +24 -1
  31. package/dist/portability/http-adapter-portability.d.ts.map +1 -1
  32. package/dist/portability/http-adapter-portability.js +441 -68
  33. package/dist/portability/response-cookie-portability.d.ts +16 -0
  34. package/dist/portability/response-cookie-portability.d.ts.map +1 -0
  35. package/dist/portability/response-cookie-portability.js +81 -0
  36. package/dist/portability/web-runtime-adapter-portability.d.ts +23 -1
  37. package/dist/portability/web-runtime-adapter-portability.d.ts.map +1 -1
  38. package/dist/portability/web-runtime-adapter-portability.js +356 -31
  39. package/dist/types.d.ts +5 -1
  40. package/dist/types.d.ts.map +1 -1
  41. package/package.json +19 -15
@@ -3,8 +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 { Controller, Get, Post, SseResponse } from '@fluojs/http';
6
+ import { Controller, Get, Head, Post, Produces, Query, Route, SseResponse } from '@fluojs/http';
7
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';
8
11
 
9
12
  /**
10
13
  * Options for configuring the HTTP adapter portability harness.
@@ -61,6 +64,41 @@ async function requestHttps(url) {
61
64
  request.end();
62
65
  });
63
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
+ }
64
102
  function createLogCaptureLogger() {
65
103
  const messages = [];
66
104
  const capture = (...args) => messages.push(args.map(arg => String(arg)).join(' '));
@@ -145,28 +183,289 @@ export class HttpAdapterPortabilityHarness {
145
183
  this.options = options;
146
184
  }
147
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
+
148
447
  /**
149
448
  * Asserts that the adapter preserves malformed cookie values without crashing
150
449
  * or incorrectly normalizing them.
151
450
  */
152
451
  async assertPreservesMalformedCookieValues() {
153
- let _initProto, _initClass;
452
+ let _initProto3, _initClass3;
154
453
  let _CookieController;
155
454
  class CookieController {
156
455
  static {
157
456
  ({
158
- e: [_initProto],
159
- c: [_CookieController, _initClass]
457
+ e: [_initProto3],
458
+ c: [_CookieController, _initClass3]
160
459
  } = _applyDecs(this, [Controller('/cookies')], [[Get('/'), 2, "readCookies"]]));
161
460
  }
162
461
  constructor() {
163
- _initProto(this);
462
+ _initProto3(this);
164
463
  }
165
464
  readCookies(_input, context) {
166
465
  return context.request.cookies;
167
466
  }
168
467
  static {
169
- _initClass();
468
+ _initClass3();
170
469
  }
171
470
  }
172
471
  class AppModule {}
@@ -193,18 +492,78 @@ export class HttpAdapterPortabilityHarness {
193
492
  }
194
493
  });
195
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
+ }
196
555
  async assertPreservesRawBodyForJsonAndText() {
197
- let _initProto2, _initClass2;
556
+ let _initProto5, _initClass5;
198
557
  let _WebhookController;
199
558
  class WebhookController {
200
559
  static {
201
560
  ({
202
- e: [_initProto2],
203
- c: [_WebhookController, _initClass2]
561
+ e: [_initProto5],
562
+ c: [_WebhookController, _initClass5]
204
563
  } = _applyDecs(this, [Controller('/webhooks')], [[Post('/json'), 2, "handleJson"], [Post('/text'), 2, "handleText"]]));
205
564
  }
206
565
  constructor() {
207
- _initProto2(this);
566
+ _initProto5(this);
208
567
  }
209
568
  handleJson(_input, context) {
210
569
  return {
@@ -219,7 +578,7 @@ export class HttpAdapterPortabilityHarness {
219
578
  };
220
579
  }
221
580
  static {
222
- _initClass2();
581
+ _initClass5();
223
582
  }
224
583
  }
225
584
  class AppModule {}
@@ -269,17 +628,17 @@ export class HttpAdapterPortabilityHarness {
269
628
  });
270
629
  }
271
630
  async assertPreservesExactRawBodyBytesForByteSensitivePayloads() {
272
- let _initProto3, _initClass3;
631
+ let _initProto6, _initClass6;
273
632
  let _WebhookController2;
274
633
  class WebhookController {
275
634
  static {
276
635
  ({
277
- e: [_initProto3],
278
- c: [_WebhookController2, _initClass3]
636
+ e: [_initProto6],
637
+ c: [_WebhookController2, _initClass6]
279
638
  } = _applyDecs(this, [Controller('/webhooks')], [[Post('/bytes'), 2, "handleBytes"]]));
280
639
  }
281
640
  constructor() {
282
- _initProto3(this);
641
+ _initProto6(this);
283
642
  }
284
643
  handleBytes(_input, context) {
285
644
  return {
@@ -287,7 +646,7 @@ export class HttpAdapterPortabilityHarness {
287
646
  };
288
647
  }
289
648
  static {
290
- _initClass3();
649
+ _initClass6();
291
650
  }
292
651
  }
293
652
  class AppModule {}
@@ -324,17 +683,17 @@ export class HttpAdapterPortabilityHarness {
324
683
  });
325
684
  }
326
685
  async assertExcludesRawBodyForMultipart() {
327
- let _initProto4, _initClass4;
686
+ let _initProto7, _initClass7;
328
687
  let _UploadController;
329
688
  class UploadController {
330
689
  static {
331
690
  ({
332
- e: [_initProto4],
333
- c: [_UploadController, _initClass4]
691
+ e: [_initProto7],
692
+ c: [_UploadController, _initClass7]
334
693
  } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
335
694
  }
336
695
  constructor() {
337
- _initProto4(this);
696
+ _initProto7(this);
338
697
  }
339
698
  upload(_input, context) {
340
699
  return {
@@ -344,7 +703,7 @@ export class HttpAdapterPortabilityHarness {
344
703
  };
345
704
  }
346
705
  static {
347
- _initClass4();
706
+ _initClass7();
348
707
  }
349
708
  }
350
709
  class AppModule {}
@@ -383,17 +742,17 @@ export class HttpAdapterPortabilityHarness {
383
742
  });
384
743
  }
385
744
  async assertDefaultsMultipartTotalLimitToMaxBodySize() {
386
- let _initProto5, _initClass5;
745
+ let _initProto8, _initClass8;
387
746
  let _UploadController2;
388
747
  class UploadController {
389
748
  static {
390
749
  ({
391
- e: [_initProto5],
392
- c: [_UploadController2, _initClass5]
750
+ e: [_initProto8],
751
+ c: [_UploadController2, _initClass8]
393
752
  } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
394
753
  }
395
754
  constructor() {
396
- _initProto5(this);
755
+ _initProto8(this);
397
756
  }
398
757
  upload(_input, context) {
399
758
  return {
@@ -402,7 +761,7 @@ export class HttpAdapterPortabilityHarness {
402
761
  };
403
762
  }
404
763
  static {
405
- _initClass5();
764
+ _initClass8();
406
765
  }
407
766
  }
408
767
  class AppModule {}
@@ -438,17 +797,21 @@ export class HttpAdapterPortabilityHarness {
438
797
  });
439
798
  }
440
799
  async assertSupportsSseStreaming() {
441
- let _initProto6, _initClass6;
800
+ let _initProto9, _initClass9;
801
+ let resolveHandlerReady;
802
+ const handlerReady = new Promise(resolve => {
803
+ resolveHandlerReady = resolve;
804
+ });
442
805
  let _EventsController;
443
806
  class EventsController {
444
807
  static {
445
808
  ({
446
- e: [_initProto6],
447
- c: [_EventsController, _initClass6]
809
+ e: [_initProto9],
810
+ c: [_EventsController, _initClass9]
448
811
  } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
449
812
  }
450
813
  constructor() {
451
- _initProto6(this);
814
+ _initProto9(this);
452
815
  }
453
816
  stream(_input, context) {
454
817
  const stream = new SseResponse(context);
@@ -459,13 +822,11 @@ export class HttpAdapterPortabilityHarness {
459
822
  event: 'ready',
460
823
  id: 'evt-1'
461
824
  });
462
- queueMicrotask(() => {
463
- stream.close();
464
- });
825
+ resolveHandlerReady(stream);
465
826
  return stream;
466
827
  }
467
828
  static {
468
- _initClass6();
829
+ _initClass9();
469
830
  }
470
831
  }
471
832
  class AppModule {}
@@ -478,21 +839,30 @@ export class HttpAdapterPortabilityHarness {
478
839
  });
479
840
  await prepareAndListenWithCleanup(app, this.options.name);
480
841
  await runWithListeningUrlCleanup(app, this.options.name, async baseUrl => {
481
- const response = await fetch(`${baseUrl}/events`, {
482
- headers: {
483
- 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.`);
484
856
  }
485
- });
486
- const body = await response.text();
487
- if (response.status !== 200) {
488
- throw new Error(`${this.options.name} adapter changed SSE response status semantics.`);
489
- }
490
- const contentType = response.headers.get('content-type') ?? '';
491
- if (!contentType.includes('text/event-stream')) {
492
- throw new Error(`${this.options.name} adapter does not expose text/event-stream content-type.`);
493
- }
494
- if (!body.includes('event: ready') || !body.includes('data: {"ready":true}')) {
495
- 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();
496
866
  }
497
867
  });
498
868
  }
@@ -502,7 +872,7 @@ export class HttpAdapterPortabilityHarness {
502
872
  * closes before a `drain` event is emitted.
503
873
  */
504
874
  async assertSettlesStreamDrainWaitOnClose() {
505
- let _initProto7, _initClass7;
875
+ let _initProto0, _initClass0;
506
876
  const adapterName = this.options.name;
507
877
  let resolveDrainWait;
508
878
  const drainWaitSettled = new Promise(resolve => {
@@ -512,12 +882,12 @@ export class HttpAdapterPortabilityHarness {
512
882
  class EventsController {
513
883
  static {
514
884
  ({
515
- e: [_initProto7],
516
- c: [_EventsController2, _initClass7]
885
+ e: [_initProto0],
886
+ c: [_EventsController2, _initClass0]
517
887
  } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
518
888
  }
519
889
  constructor() {
520
- _initProto7(this);
890
+ _initProto0(this);
521
891
  }
522
892
  async stream(_input, context) {
523
893
  const stream = new SseResponse(context);
@@ -532,7 +902,7 @@ export class HttpAdapterPortabilityHarness {
532
902
  return stream;
533
903
  }
534
904
  static {
535
- _initClass7();
905
+ _initClass0();
536
906
  }
537
907
  }
538
908
  class AppModule {}
@@ -558,18 +928,18 @@ export class HttpAdapterPortabilityHarness {
558
928
  });
559
929
  }
560
930
  async assertReportsConfiguredHostInStartupLogs() {
561
- let _initProto8, _initClass8;
931
+ let _initProto1, _initClass1;
562
932
  const logger = createLogCaptureLogger();
563
933
  let _HealthController;
564
934
  class HealthController {
565
935
  static {
566
936
  ({
567
- e: [_initProto8],
568
- c: [_HealthController, _initClass8]
937
+ e: [_initProto1],
938
+ c: [_HealthController, _initClass1]
569
939
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
570
940
  }
571
941
  constructor() {
572
- _initProto8(this);
942
+ _initProto1(this);
573
943
  }
574
944
  getHealth() {
575
945
  return {
@@ -577,7 +947,7 @@ export class HttpAdapterPortabilityHarness {
577
947
  };
578
948
  }
579
949
  static {
580
- _initClass8();
950
+ _initClass1();
581
951
  }
582
952
  }
583
953
  class AppModule {}
@@ -607,18 +977,18 @@ export class HttpAdapterPortabilityHarness {
607
977
  });
608
978
  }
609
979
  async assertReportsHttpsStartupUrl(https) {
610
- let _initProto9, _initClass9;
980
+ let _initProto10, _initClass10;
611
981
  const logger = createLogCaptureLogger();
612
982
  let _HealthController2;
613
983
  class HealthController {
614
984
  static {
615
985
  ({
616
- e: [_initProto9],
617
- c: [_HealthController2, _initClass9]
986
+ e: [_initProto10],
987
+ c: [_HealthController2, _initClass10]
618
988
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
619
989
  }
620
990
  constructor() {
621
- _initProto9(this);
991
+ _initProto10(this);
622
992
  }
623
993
  getHealth() {
624
994
  return {
@@ -626,7 +996,7 @@ export class HttpAdapterPortabilityHarness {
626
996
  };
627
997
  }
628
998
  static {
629
- _initClass9();
999
+ _initClass10();
630
1000
  }
631
1001
  }
632
1002
  class AppModule {}
@@ -656,7 +1026,7 @@ export class HttpAdapterPortabilityHarness {
656
1026
  });
657
1027
  }
658
1028
  async assertRemovesShutdownSignalListenersAfterClose() {
659
- let _initProto0, _initClass0;
1029
+ let _initProto11, _initClass11;
660
1030
  const logger = {
661
1031
  debug() {},
662
1032
  error() {},
@@ -667,12 +1037,12 @@ export class HttpAdapterPortabilityHarness {
667
1037
  class HealthController {
668
1038
  static {
669
1039
  ({
670
- e: [_initProto0],
671
- c: [_HealthController3, _initClass0]
1040
+ e: [_initProto11],
1041
+ c: [_HealthController3, _initClass11]
672
1042
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
673
1043
  }
674
1044
  constructor() {
675
- _initProto0(this);
1045
+ _initProto11(this);
676
1046
  }
677
1047
  getHealth() {
678
1048
  return {
@@ -680,7 +1050,7 @@ export class HttpAdapterPortabilityHarness {
680
1050
  };
681
1051
  }
682
1052
  static {
683
- _initClass0();
1053
+ _initClass11();
684
1054
  }
685
1055
  }
686
1056
  class AppModule {}
@@ -721,6 +1091,9 @@ export class HttpAdapterPortabilityHarness {
721
1091
  export function createHttpAdapterPortabilityHarness(options) {
722
1092
  return new HttpAdapterPortabilityHarness(options);
723
1093
  }
1094
+ function equalByteArrays(left, right) {
1095
+ return left.byteLength === right.byteLength && left.every((value, index) => value === right[index]);
1096
+ }
724
1097
  async function withTimeout(promise, timeoutMs, message) {
725
1098
  let timeout;
726
1099
  const timeoutPromise = new Promise((_resolve, reject) => {