@interfere/test-utils 10.0.1 → 10.0.2

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.
@@ -13,6 +13,7 @@ export declare function receiver(): Promise<{
13
13
  forward(handler: (request: Request) => Promise<Response>): void;
14
14
  reset(): void;
15
15
  fail(path: string, status: number, attempts?: number, body?: string): void;
16
+ stall(path: string, ms: number): void;
16
17
  close(): Promise<void>;
17
18
  }>;
18
19
  //#endregion
@@ -1 +1 @@
1
- import{readFile}from"node:fs/promises";import{extname,resolve,sep}from"node:path";import{once}from"node:events";import{HttpResponse,getResponse,http}from"msw";import{buffer}from"node:stream/consumers";import{createServer}from"node:http";import{gunzipSync}from"node:zlib";async function receiver(){let requests=[],failures=new Map,assets,upstream;async function resolveResponse(request,incoming){let url=new URL(request.url),{headers}=request,responseHeaders=new Headers({"access-control-allow-origin":`*`,"access-control-allow-headers":headers.get(`access-control-request-headers`)??`*`,"access-control-allow-methods":`GET, POST, OPTIONS`});try{if(request.method===`OPTIONS`)return new HttpResponse(null,{status:204,headers:responseHeaders});if(assets&&request.method===`GET`&&!url.pathname.startsWith(`/v3/`)){let file=resolve(assets,`.${url.pathname===`/`?`/index.html`:url.pathname}`);return file.startsWith(`${resolve(assets)}${sep}`)?(responseHeaders.set(`content-type`,extname(file)===`.html`?`text/html`:`text/javascript`),new HttpResponse(await readFile(file),{headers:responseHeaders})):new HttpResponse(null,{status:403,headers:responseHeaders})}let raw=await buffer(incoming),body=headers.get(`content-encoding`)===`gzip`?gunzipSync(raw).toString():raw.toString(),failure=[...failures].find(([prefix])=>url.pathname.startsWith(prefix))?.[1],entry={method:request.method,url:url.pathname+url.search,headers,body,status:200};if(requests.push(entry),failure&&failure.remaining>0)return--failure.remaining,entry.status=failure.status,responseHeaders.set(`content-type`,`application/json`),new HttpResponse([204,205,304].includes(failure.status)?null:failure.body,{status:failure.status,headers:responseHeaders});if(upstream){let init={method:entry.method,headers};raw.length>0&&(init.body=raw);let result=await upstream(new Request(url,init));entry.status=result.status;for(let[key,value]of result.headers)responseHeaders.set(key,value);return new HttpResponse(result.body===null?null:await result.arrayBuffer(),{status:result.status,headers:responseHeaders})}return HttpResponse.json(url.pathname.includes(`config`)?{plugins:{errors:!0,logs:!0,device:!0,pageEvents:!0,rageClick:!0,replay:!0}}:{},{headers:responseHeaders})}catch(error){return new HttpResponse(new TextEncoder().encode(String(error)),{status:500,headers:responseHeaders})}}async function handle(request,response){try{let headers=new Headers;for(let[key,value]of Object.entries(request.headers))value!==void 0&&headers.set(key,Array.isArray(value)?value.join(`, `):value);let init={method:request.method??`GET`,headers},resolved=await getResponse([http.all(`*`,async({request:intercepted})=>resolveResponse(intercepted,request))],new Request(new URL(request.url??`/`,`http://localhost`),init));if(!resolved)throw Error(`Missing telemetry receiver response`);for(let[key,value]of resolved.headers)response.setHeader(key,value);response.writeHead(resolved.status).end(Buffer.from(await resolved.arrayBuffer()))}catch(error){response.destroy(error instanceof Error?error:Error(String(error)))}}let server=createServer((request,response)=>{handle(request,response)});server.listen(0,`127.0.0.1`),await once(server,`listening`);let address=server.address();if(!(address instanceof Object))throw Error(`Receiver did not bind a TCP port`);return{url:`http://127.0.0.1:${address.port}`,requests,serve(directory){assets=directory},forward(handler){upstream=handler},reset(){requests.length=0,failures.clear()},fail(path,status,attempts=1,body=`{}`){failures.set(path,{status,remaining:attempts,body})},async close(){server.closeAllConnections(),await new Promise((resolve,reject)=>{server.close(error=>{error?reject(error):resolve()})})}}}export{receiver};
1
+ import{readFile}from"node:fs/promises";import{extname,resolve,sep}from"node:path";import{once}from"node:events";import{HttpResponse,getResponse,http}from"msw";import{buffer}from"node:stream/consumers";import{createServer}from"node:http";import{setTimeout}from"node:timers/promises";import{gunzipSync}from"node:zlib";async function receiver(){let requests=[],failures=new Map,stalls=new Map,assets,upstream;async function resolveResponse(request,incoming){let url=new URL(request.url),{headers}=request,responseHeaders=new Headers({"access-control-allow-origin":`*`,"access-control-allow-headers":headers.get(`access-control-request-headers`)??`*`,"access-control-allow-methods":`GET, POST, OPTIONS`});try{if(request.method===`OPTIONS`)return new HttpResponse(null,{status:204,headers:responseHeaders});if(assets&&request.method===`GET`&&!url.pathname.startsWith(`/v3/`)){let file=resolve(assets,`.${url.pathname===`/`?`/index.html`:url.pathname}`);return file.startsWith(`${resolve(assets)}${sep}`)?(responseHeaders.set(`content-type`,extname(file)===`.html`?`text/html`:`text/javascript`),new HttpResponse(await readFile(file),{headers:responseHeaders})):new HttpResponse(null,{status:403,headers:responseHeaders})}let stall=[...stalls].find(([prefix])=>url.pathname.startsWith(prefix))?.[1];stall!==void 0&&(incoming.pause(),await setTimeout(stall),incoming.resume());let raw=await buffer(incoming),body=headers.get(`content-encoding`)===`gzip`?gunzipSync(raw).toString():raw.toString(),failure=[...failures].find(([prefix])=>url.pathname.startsWith(prefix))?.[1],entry={method:request.method,url:url.pathname+url.search,headers,body,status:200};if(requests.push(entry),failure&&failure.remaining>0)return--failure.remaining,entry.status=failure.status,responseHeaders.set(`content-type`,`application/json`),new HttpResponse([204,205,304].includes(failure.status)?null:failure.body,{status:failure.status,headers:responseHeaders});if(upstream){let init={method:entry.method,headers};raw.length>0&&(init.body=raw);let result=await upstream(new Request(url,init));entry.status=result.status;for(let[key,value]of result.headers)responseHeaders.set(key,value);return new HttpResponse(result.body===null?null:await result.arrayBuffer(),{status:result.status,headers:responseHeaders})}return HttpResponse.json(url.pathname.includes(`config`)?{plugins:{errors:!0,logs:!0,device:!0,pageEvents:!0,rageClick:!0,replay:!0}}:{},{headers:responseHeaders})}catch(error){return new HttpResponse(new TextEncoder().encode(String(error)),{status:500,headers:responseHeaders})}}async function handle(request,response){try{let headers=new Headers;for(let[key,value]of Object.entries(request.headers))value!==void 0&&headers.set(key,Array.isArray(value)?value.join(`, `):value);let init={method:request.method??`GET`,headers},resolved=await getResponse([http.all(`*`,async({request:intercepted})=>resolveResponse(intercepted,request))],new Request(new URL(request.url??`/`,`http://localhost`),init));if(!resolved)throw Error(`Missing telemetry receiver response`);for(let[key,value]of resolved.headers)response.setHeader(key,value);response.writeHead(resolved.status).end(Buffer.from(await resolved.arrayBuffer()))}catch(error){response.destroy(error instanceof Error?error:Error(String(error)))}}let server=createServer((request,response)=>{handle(request,response)});server.listen(0,`127.0.0.1`),await once(server,`listening`);let address=server.address();if(!(address instanceof Object))throw Error(`Receiver did not bind a TCP port`);return{url:`http://127.0.0.1:${address.port}`,requests,serve(directory){assets=directory},forward(handler){upstream=handler},reset(){requests.length=0,failures.clear(),stalls.clear()},fail(path,status,attempts=1,body=`{}`){failures.set(path,{status,remaining:attempts,body})},stall(path,ms){stalls.set(path,ms)},async close(){server.closeAllConnections(),await new Promise((resolve,reject)=>{server.close(error=>{error?reject(error):resolve()})})}}}export{receiver};
package/package.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "name": "@interfere/test-utils",
3
- "version": "10.0.1",
3
+ "version": "10.0.2",
4
4
  "description": "Shared test utilities used across Interfere packages.",
5
5
  "homepage": "https://interfere.com",
6
- "bugs": {
7
- "url": "mailto:support@interfere.com"
8
- },
6
+ "bugs": "mailto:support@interfere.com",
9
7
  "license": "MIT",
10
8
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
11
9
  "repository": {
@@ -35,20 +33,20 @@
35
33
  },
36
34
  "scripts": {
37
35
  "build": "tsdown",
38
- "test": "vitest run --config ../../../../vitest.config.ts",
36
+ "test": "node ../../private/vitest-config/src/cli.ts run --config ../../../../vitest.config.ts",
39
37
  "typecheck": "bun check"
40
38
  },
41
39
  "dependencies": {
42
- "@interfere/helpers": "^9.2.1",
43
- "msw": "^2.14.6",
44
- "zod": "^4.5.4"
40
+ "@interfere/helpers": "^9.3.0",
41
+ "msw": "2.15.0",
42
+ "zod": "4.6.6"
45
43
  },
46
44
  "devDependencies": {
47
45
  "@interfere/typescript-config": "^9.0.2",
48
46
  "@interfere/vitest-config": "0.0.0",
49
- "@types/node": "^26.4.1",
50
- "tsdown": "^0.23.0",
51
- "typescript": "^7.0.2",
47
+ "@types/node": "26.4.1",
48
+ "tsdown": "0.23.0",
49
+ "typescript": "7.0.2",
52
50
  "vitest": "5.0.5"
53
51
  }
54
52
  }