ajax-hooker 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -2,13 +2,19 @@
2
2
 
3
3
  English | [中文](./README.zh-CN.md)
4
4
 
5
- A lightweight AJAX request interceptor that supports intercepting and modifying both XMLHttpRequest and Fetch requests.
5
+ `ajax-hooker` is a browser-side AJAX interception library. It deeply hooks native `XMLHttpRequest` and `fetch`, then normalizes both into one hook lifecycle so interception logic can be written once and reused across request types.
6
+
7
+ ## Highlights
8
+
9
+ - Deep AJAX interception: hooks core request stages of XHR and Fetch instead of only wrapping business-layer request helpers.
10
+ - Unified request lifecycle: flattens XHR/Fetch differences into two main phases: before request (request mutation) and after response (response handling).
11
+ - Stream-aware interception: supports chunk-level interception for streaming responses.
6
12
 
7
13
  ## Features
8
14
 
9
- - Works with both XMLHttpRequest and Fetch API
10
- - Intercepts and modifies request parameters (URL, Method, Headers, Body)
11
- - Captures response data
15
+ - Works with both `XMLHttpRequest` and `fetch`
16
+ - Intercepts and modifies request parameters (`url`, `method`, `headers`, `data`)
17
+ - Captures response data through one unified callback model
12
18
  - Supports streaming response interception (SSE, NDJSON, streaming JSON, etc.)
13
19
  - Chain multiple hook functions
14
20
  - Singleton pattern ensures a single global instance
@@ -46,6 +52,72 @@ interceptor.hook((request) => {
46
52
  });
47
53
  ```
48
54
 
55
+ ## API Updates (v1.1+)
56
+
57
+ ### Unified Before/After Request Lifecycle
58
+
59
+ ```typescript
60
+ interceptor.hook((request) => {
61
+ // Before request: mutate request params
62
+ request.url = request.url.replace('/api/v1', '/api/v2');
63
+ request.headers.set('x-trace-id', crypto.randomUUID());
64
+
65
+ // After response: unified response callback for XHR + Fetch
66
+ request.response = async (response) => {
67
+ console.log('status:', response.status);
68
+ };
69
+
70
+ return request;
71
+ });
72
+ ```
73
+
74
+ ### Fine-Grained Control by Request Type
75
+
76
+ ```typescript
77
+ // Inject only Fetch interception
78
+ interceptor.inject('fetch');
79
+
80
+ // Hook only Fetch requests
81
+ interceptor.hook((request) => {
82
+ request.headers.set('x-from', 'fetch-only');
83
+ return request;
84
+ }, 'fetch');
85
+
86
+ // Remove only Fetch interception
87
+ interceptor.uninject('fetch');
88
+ ```
89
+
90
+ ### Remove Hooks with `unhook`
91
+
92
+ ```typescript
93
+ const authHook = (request) => {
94
+ request.headers.set('Authorization', 'Bearer token');
95
+ return request;
96
+ };
97
+
98
+ interceptor.hook(authHook);
99
+
100
+ // Remove a specific hook
101
+ interceptor.unhook(authHook);
102
+
103
+ // Clear all hooks for xhr only
104
+ interceptor.unhook(undefined, 'xhr');
105
+
106
+ // Clear all hooks for both xhr and fetch
107
+ interceptor.unhook();
108
+ ```
109
+
110
+ ### Deprecated Compatibility Fields (1.x)
111
+
112
+ `interceptor.xhrInterceptor` and `interceptor.fetchInterceptor` are still available in 1.x for backward compatibility, but they are deprecated and planned to become private in 2.x.
113
+
114
+ Prefer the public APIs:
115
+
116
+ - `interceptor.hook(...)`
117
+ - `interceptor.unhook(...)`
118
+ - `interceptor.inject(...)`
119
+ - `interceptor.uninject(...)`
120
+
49
121
  ## API
50
122
 
51
123
  ### AjaxInterceptor.getInstance()
@@ -117,6 +189,32 @@ interceptor.hook((request) => {
117
189
  }, 'fetch');
118
190
  ```
119
191
 
192
+ ### unhook(fn?, type?)
193
+
194
+ Remove one hook or clear hooks.
195
+
196
+ **Parameters:**
197
+ - `fn`: Optional. If provided, remove this specific hook. If omitted, clear all hooks.
198
+ - `type`: Optional. Specify `'xhr'` or `'fetch'` to remove hooks only for one type. If omitted, both are affected.
199
+
200
+ ```typescript
201
+ const loggerHook = (request) => {
202
+ console.log(request.url);
203
+ return request;
204
+ };
205
+
206
+ interceptor.hook(loggerHook);
207
+
208
+ // Remove one hook
209
+ interceptor.unhook(loggerHook);
210
+
211
+ // Clear all fetch hooks
212
+ interceptor.unhook(undefined, 'fetch');
213
+
214
+ // Clear all hooks
215
+ interceptor.unhook();
216
+ ```
217
+
120
218
  ## Request Object (AjaxInterceptorRequest)
121
219
 
122
220
  The request object received by hook functions contains the following properties:
@@ -330,6 +428,16 @@ interceptor.hook((request) => {
330
428
 
331
429
  ## Development
332
430
 
431
+ ### Build Outputs
432
+
433
+ - ESM: `dist/esm/index.js`
434
+ - CJS: `dist/cjs/index.js`
435
+ - IIFE (browser global): `dist/iife/index.js`
436
+ - Shared type declarations: `dist/types/*.d.ts`
437
+ - UMD: not emitted by default (can be added later for legacy loader scenarios)
438
+
439
+ > Type declarations are generated once into `dist/types` and shared by both ESM and CJS consumers.
440
+
333
441
  ```bash
334
442
  # Install dependencies
335
443
  pnpm install
@@ -340,6 +448,12 @@ pnpm dev
340
448
  # Build
341
449
  pnpm build
342
450
 
451
+ # Build JS only
452
+ pnpm build:js
453
+
454
+ # Build types only (single shared declarations in dist/types)
455
+ pnpm build:types
456
+
343
457
  # Test
344
458
  pnpm test
345
459
 
package/README.zh-CN.md CHANGED
@@ -2,13 +2,19 @@
2
2
 
3
3
  [English](./README.md) | 中文
4
4
 
5
- 一个轻量级的 AJAX 请求拦截器,支持拦截和修改 XMLHttpRequest 和 Fetch 请求。
5
+ `ajax-hooker` 是一个浏览器端 AJAX 拦截库。它会对原生 `XMLHttpRequest``fetch` 做深度劫持,并把两者抹平为统一的 Hook 生命周期,让拦截逻辑写一次即可复用。
6
+
7
+ ## 项目亮点
8
+
9
+ - 深度 AJAX 劫持: 直接拦截 XHR 与 Fetch 的关键阶段,而不是只包裹业务层请求函数。
10
+ - 统一请求生命周期: 抹平 XHR/Fetch 差异,聚合为两个核心阶段: 请求前(请求参数改写)与请求后(响应处理)。
11
+ - 流式能力完整: 支持对流式响应进行逐块拦截和改写。
6
12
 
7
13
  ## 特性
8
14
 
9
- - 同时支持 XMLHttpRequest 和 Fetch API
10
- - 可拦截和修改请求参数(URL、Method、Headers、Body)
11
- - 可捕获响应数据
15
+ - 同时支持 `XMLHttpRequest``fetch`
16
+ - 可拦截和修改请求参数(`url`、`method`、`headers`、`data`)
17
+ - 通过统一回调模型捕获响应数据
12
18
  - 支持流式响应拦截(SSE、NDJSON、streaming JSON 等)
13
19
  - 支持多个钩子函数链式执行
14
20
  - 单例模式,确保全局唯一实例
@@ -46,6 +52,72 @@ interceptor.hook((request) => {
46
52
  });
47
53
  ```
48
54
 
55
+ ## API 变动示例 (v1.1+)
56
+
57
+ ### 统一的请求前/请求后生命周期
58
+
59
+ ```typescript
60
+ interceptor.hook((request) => {
61
+ // 请求前: 改写请求参数
62
+ request.url = request.url.replace('/api/v1', '/api/v2');
63
+ request.headers.set('x-trace-id', crypto.randomUUID());
64
+
65
+ // 请求后: XHR + Fetch 统一响应回调
66
+ request.response = async (response) => {
67
+ console.log('status:', response.status);
68
+ };
69
+
70
+ return request;
71
+ });
72
+ ```
73
+
74
+ ### 按请求类型精细控制
75
+
76
+ ```typescript
77
+ // 只注入 Fetch 拦截
78
+ interceptor.inject('fetch');
79
+
80
+ // 只拦截 Fetch 请求
81
+ interceptor.hook((request) => {
82
+ request.headers.set('x-from', 'fetch-only');
83
+ return request;
84
+ }, 'fetch');
85
+
86
+ // 只移除 Fetch 拦截
87
+ interceptor.uninject('fetch');
88
+ ```
89
+
90
+ ### 使用 `unhook` 移除钩子
91
+
92
+ ```typescript
93
+ const authHook = (request) => {
94
+ request.headers.set('Authorization', 'Bearer token');
95
+ return request;
96
+ };
97
+
98
+ interceptor.hook(authHook);
99
+
100
+ // 移除指定钩子
101
+ interceptor.unhook(authHook);
102
+
103
+ // 仅清空 xhr 的全部钩子
104
+ interceptor.unhook(undefined, 'xhr');
105
+
106
+ // 清空 xhr + fetch 的全部钩子
107
+ interceptor.unhook();
108
+ ```
109
+
110
+ ### 废弃兼容字段 (1.x)
111
+
112
+ `interceptor.xhrInterceptor` 和 `interceptor.fetchInterceptor` 在 1.x 中仍保留用于向后兼容,但已标记为废弃,并计划在 2.x 改为私有。
113
+
114
+ 建议使用公开 API:
115
+
116
+ - `interceptor.hook(...)`
117
+ - `interceptor.unhook(...)`
118
+ - `interceptor.inject(...)`
119
+ - `interceptor.uninject(...)`
120
+
49
121
  ## API
50
122
 
51
123
  ### AjaxInterceptor.getInstance()
@@ -117,6 +189,32 @@ interceptor.hook((request) => {
117
189
  }, 'fetch');
118
190
  ```
119
191
 
192
+ ### unhook(fn?, type?)
193
+
194
+ 移除单个钩子或清空钩子。
195
+
196
+ **参数:**
197
+ - `fn`: 可选。传入时移除该钩子; 不传则清空所有钩子。
198
+ - `type`: 可选。指定 `'xhr'` 或 `'fetch'` 只清理对应类型; 不传则两者都处理。
199
+
200
+ ```typescript
201
+ const loggerHook = (request) => {
202
+ console.log(request.url);
203
+ return request;
204
+ };
205
+
206
+ interceptor.hook(loggerHook);
207
+
208
+ // 移除一个钩子
209
+ interceptor.unhook(loggerHook);
210
+
211
+ // 清空所有 fetch 钩子
212
+ interceptor.unhook(undefined, 'fetch');
213
+
214
+ // 清空全部钩子
215
+ interceptor.unhook();
216
+ ```
217
+
120
218
  ## 请求对象 (AjaxInterceptorRequest)
121
219
 
122
220
  钩子函数接收的请求对象包含以下属性:
@@ -330,6 +428,16 @@ interceptor.hook((request) => {
330
428
 
331
429
  ## 开发
332
430
 
431
+ ### 构建产物
432
+
433
+ - ESM: `dist/esm/index.js`
434
+ - CJS: `dist/cjs/index.js`
435
+ - IIFE(浏览器全局): `dist/iife/index.js`
436
+ - 共享类型声明: `dist/types/*.d.ts`
437
+ - UMD: 默认不产出(如需兼容老式加载器可后续追加)
438
+
439
+ > 类型声明只生成一份到 `dist/types`,由 ESM/CJS 共同复用。
440
+
333
441
  ```bash
334
442
  # 安装依赖
335
443
  pnpm install
@@ -340,6 +448,12 @@ pnpm dev
340
448
  # 构建
341
449
  pnpm build
342
450
 
451
+ # 仅构建 JS
452
+ pnpm build:js
453
+
454
+ # 仅构建类型(统一输出到 dist/types)
455
+ pnpm build:types
456
+
343
457
  # 测试
344
458
  pnpm test
345
459
 
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e="xhr",t="fetch",r=Symbol("CycleScheduler");class n{req={};resp={};constructor({req:e={}}={}){this.req=e}async execute(e,t){let r=e;for(const e of t){const t=await e(r);t&&(r=t)}return r}}Object.getOwnPropertyDescriptor.bind(Object);const s=Object.prototype.toString.call.bind(Object.prototype.toString),o=(e="")=>new URL(e,window.location.origin).toString(),a=(e,t)=>{const r=Reflect.get(e,t);return"function"!=typeof r?r:function(...t){return Reflect.apply(r,e,t)}},c=({source:e,target:t,prototype:r})=>{Object.keys(e).forEach(r=>{t[r]=e[r]}),t.prototype=r};class i{nativeXhr=window.XMLHttpRequest;nativeXhrPrototype=window.XMLHttpRequest.prototype;hooks=[];xhrResponseEvents=["readystatechange","load","loadend"];xhrInstanceAttr=["response","responseText","responseXML","status","statusText"];xhrInstanceAttrHandler=this.xhrInstanceAttr.reduce((e,t)=>(e[t]=function(e){const n=e[r];return n.xhrAlreadyReturned?n.resp[t]:e[t]},e),{});xhrMethodsHandler={open:function(t,n){return function(...s){const a=n[r];a.xhrReset(),a.req={type:e,method:s[0]||"GET",url:o(s[1]),headers:new Headers,data:null,response:()=>{}},a.xhrOpenRestArgs=s.slice(2),t.nativeXhrPrototype.open.apply(n,[a.req.method,a.req.url,...a.xhrOpenRestArgs||[]])}},send:function(e,t){return async function(n){const s=t[r];s.req.data=n??null,s.req.responseType=t.responseType,s.req.withCredentials=t.withCredentials,s.req.timeout=t.timeout,s.req.headers=new Headers(s.xhrSetRequestHeadersAfterOpen);const o=s.req;let a={...s.req,headers:new Headers(s.req.headers)};try{a=await s.execute(a,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in xhr request hooks:",e)}s.req=a;const c=o.method!==a.method||o.url!==a.url;c&&e.nativeXhrPrototype.open.apply(t,[s.req.method,s.req.url,...s.xhrOpenRestArgs||[]]);const i=["responseType","withCredentials","timeout"];for(const e of i)a[e]!==o[e]&&(t[e]=a[e]);!c&&e.headersEqual(o.headers,a.headers)||s.req.headers.forEach((e,r)=>{t.setRequestHeader(r,e)}),e.nativeXhrPrototype.send.apply(t,[s.req.data])}},setRequestHeader:function(e,t){return function(n,s){const o=t[r];e.nativeXhrPrototype.setRequestHeader.apply(t,[n,s]),o.xhrSetRequestHeadersAfterOpen.append(n,s)}},addEventListener:function(e,t,r){return function(n,s,...o){const a=e.xhrResponseEvents.includes(n);t.addEventListener(n,async function(...n){a&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(s,r,n)},...o)}}};constructor(){}inject(){window.XMLHttpRequest=this._generateProxyXMLHttpRequest()}uninject(){window.XMLHttpRequest=this.nativeXhr}parseHeaders(e){const t={};if(!e)return t;const r=(e,r)=>{const n=e.toLowerCase();t[n]=n in t?`${t[n]}, ${r}`:r},n=s(e);if("[object String]"===n){const t=e;for(const e of t.trim().split(/[\r\n]+/)){const t=e.indexOf(":");if(-1===t)continue;const n=e.slice(0,t).trim(),s=e.slice(t+1).trim();n&&r(n,s)}}else if("[object Headers]"===n){e.forEach((e,t)=>{r(t,e)})}else if("[object Object]"===n){const t=e;for(const[e,n]of Object.entries(t))null!=n&&r(e,String(n))}return t}async responseProcessor(e){const t=e[r];if(!t.xhrAlreadyReturned){t.xhrAlreadyReturned=!0,t.resp={status:e.status,statusText:e.statusText,response:e.response,headers:new Headers(this.parseHeaders(e.getAllResponseHeaders())),finalUrl:e.responseURL||""};try{await t.req.response(t.resp)}catch(e){console.warn("[AjaxInterceptor] Error in xhr response callback:",e)}}}headersEqual(e,t){if(e===t)return!0;const r=e=>{const t=[];return e.forEach((e,r)=>t.push(`${r}: ${e}`)),t.sort().toString()};return r(e)===r(t)}getAttrHandler(e,t,r){return this.xhrInstanceAttr.includes(t)?this.xhrInstanceAttrHandler[t](e):this.xhrMethodsHandler[t]?this.xhrMethodsHandler[t](this,e,r):null}_generateProxyXMLHttpRequest(){const e=this;function t(){const t=new e.nativeXhr;t[r]=new h;return new Proxy(t,{get:(t,r,n)=>e.getAttrHandler(t,r,n)??a(t,r),set(t,r,n,s){if("function"==typeof n&&r.startsWith("on")){const o=e.xhrResponseEvents.includes(r.replace(/^on/,"")),a=async function(...r){o&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(n,s,r)};return Reflect.set(t,r,a)}return Reflect.set(t,r,n)}})}return c({source:e.nativeXhr,target:t,prototype:this.nativeXhrPrototype}),t}}class h extends n{xhrAlreadyReturned=!1;xhrOpenRestArgs=[];xhrSetRequestHeadersAfterOpen=new Headers;xhrReset(){this.req={},this.resp={},this.xhrOpenRestArgs=[],this.xhrSetRequestHeadersAfterOpen=new Headers,this.xhrAlreadyReturned=!1}constructor({req:e={}}={}){super({req:e})}}class u extends n{constructor({req:e={}}={}){super({req:e})}}class d{nativeFetch=window.fetch;nativeFetchPrototype=this.nativeFetch.prototype;hooks=[];fetchInstanceAttr=["status","statusText","ok","headers","redirected"];fetchInstanceAttrHandler=this.fetchInstanceAttr.reduce((e,t)=>(e[t]=function(e,n){return n[r].resp[t]},e),{});fetchMethods=["json","formData","blob","arrayBuffer","text"];fetchMethodsHandler=this.fetchMethods.reduce((e,t)=>(e[t]=function(e,n){return async function(...e){return n[r].resp[t]}},e),{});constructor(){}inject(){window.fetch=this._generateProxyFetch()}uninject(){window.fetch=this.nativeFetch}getAttrHandler(e,t){return this.fetchInstanceAttr.includes(t)?this.fetchInstanceAttrHandler[t](this,e):this.fetchMethodsHandler[t]?this.fetchMethodsHandler[t](this,e):null}normalizeRequest(e){let t="",r=null,n=null,s=null;return"string"==typeof e||e instanceof URL?t=o(e):(t=o(e.url),r=e.method??null,n=e.headers??null,s=e.body??null),{url:t,method:r,headers:n,data:s}}resolveRequest(e,t){return"string"==typeof e?t.url:e instanceof URL?new URL(t.url):e instanceof Request?new Request(t.url,e):e}resolveOptions({options:e,newRequest:t}){return{...e,headers:t.headers,body:t.data,method:t.method,...t.data instanceof ReadableStream?{duplex:"half"}:{}}}resolveHeaders(e){return e instanceof Headers?e:new Headers(e)}_generateProxyFetch(){const e=this;async function n(n,s={}){const o=e.normalizeRequest(n),c=e.nativeFetch,i=new u;let h=o;try{h=await i.execute({type:t,url:o.url,method:s.method??o.method??"GET",headers:e.resolveHeaders(s.headers??o.headers??new Headers),data:s.body??o.data??null,response:()=>{}},e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in fetch request hooks:",e)}i.req=h;const d=await c(e.resolveRequest(n,h),e.resolveOptions({options:s,newRequest:h})),l=d.headers.get("content-type")||"",p=l.includes("text/event-stream")||l.includes("application/stream+json")||l.includes("application/x-ndjson")||l.includes("application/jsonl")||l.includes("application/json-seq");let f=d;if(p&&d.body){i.resp={status:d.status,statusText:d.statusText,ok:d.ok,headers:d.headers,finalUrl:d.url,redirected:d.redirected};try{await i.req.response(i.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch stream response callback:",e)}let e=0;const{readable:t,writable:r}=new TransformStream({async transform(t,r){try{const n=(new TextDecoder).decode(t,{stream:!0});let s=n;if(i.req.onStreamChunk){const r={text:n,raw:t,index:e++,timestamp:Date.now()},o=await i.req.onStreamChunk(r);"string"==typeof o&&(s=o)}const o=new TextEncoder;r.enqueue(o.encode(s))}catch(e){r.enqueue(t)}}});d.body.pipeTo(r),f=new Response(t,{status:d.status,statusText:d.statusText,headers:d.headers})}else if(!p){const[e,t,r,n,s]=await Promise.allSettled([d.clone().json(),d.clone().text(),d.clone().arrayBuffer(),d.clone().blob(),d.clone().formData()]).then(e=>e.map(e=>"fulfilled"===e.status?e.value:null));i.resp={status:d.status,statusText:d.statusText,ok:d.ok,headers:d.headers,finalUrl:d.url,redirected:d.redirected,json:e,text:t,arrayBuffer:r,blob:n,formData:s};try{await i.req.response(i.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch response callback:",e)}}f[r]=i;return new Proxy(f,{get(t,r){const n=e.getAttrHandler(t,r);return n||a(t,r)},set:(e,t,r)=>Reflect.set(e,t,r)})}return c({source:this.nativeFetch,target:n,prototype:this.nativeFetchPrototype}),n}}class l{xhrInterceptor;fetchInterceptor;static#e;static#t=Symbol("AjaxInterceptor");static getInstance(e={}){return l.#e||(l.#e=new l(l.#t,e)),l.#e}constructor(e,t={}){if(e!==l.#t)throw new Error("AjaxInterceptor is a singleton");this.xhrInterceptor=new i,this.fetchInterceptor=new d}toggleInject(r,n){switch(r){case e:this.xhrInterceptor[n]();break;case t:this.fetchInterceptor[n]();break;default:this.xhrInterceptor[n](),this.fetchInterceptor[n]()}}inject(e){if("undefined"==typeof window)throw new Error("AjaxInterceptor requires a browser environment");window.XMLHttpRequest||console.warn("XMLHttpRequest is not supported in this environment"),window.fetch||console.warn("Fetch API is not supported in this environment"),this.toggleInject(e,"inject")}uninject(e){this.toggleInject(e,"uninject")}hook(r,n){switch(n){case e:this.xhrInterceptor.hooks.push(r);break;case t:this.fetchInterceptor.hooks.push(r);break;default:this.xhrInterceptor.hooks.push(r),this.fetchInterceptor.hooks.push(r)}}}exports.AjaxInterceptor=l,exports.default=l;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e="xhr",t="fetch",r=Symbol("CycleScheduler");class s{req={};resp={};constructor({req:e={}}={}){this.req=e}async execute(e,t){let r=e;for(const e of t){const t=await e(r);t&&(r=t)}return r}}Object.getOwnPropertyDescriptor.bind(Object);const n=Object.prototype.toString.call.bind(Object.prototype.toString),o=(e="")=>new URL(e,window.location.origin).toString(),a=(e,t)=>{const r=Reflect.get(e,t);return"function"!=typeof r?r:function(...t){return Reflect.apply(r,e,t)}},c=({source:e,target:t,prototype:r})=>{Object.keys(e).forEach(r=>{t[r]=e[r]}),t.prototype=r};class h{nativeXhr=window.XMLHttpRequest;nativeXhrPrototype=window.XMLHttpRequest.prototype;hooks=[];xhrResponseEvents=["readystatechange","load","loadend"];xhrInstanceAttr=["response","responseText","responseXML","status","statusText"];xhrInstanceAttrHandler=this.xhrInstanceAttr.reduce((e,t)=>(e[t]=function(e){const s=e[r];return s.xhrAlreadyReturned?s.resp[t]:e[t]},e),{});xhrMethodsHandler={open:function(t,s){return function(...n){const a=s[r];a.xhrReset(),a.req={type:e,method:n[0]||"GET",url:o(n[1]),headers:new Headers,data:null,response:()=>{}},a.xhrOpenRestArgs=n.slice(2),t.nativeXhrPrototype.open.apply(s,[a.req.method,a.req.url,...a.xhrOpenRestArgs||[]])}},send:function(e,t){return async function(s){const n=t[r];n.req.data=s??null,n.req.responseType=t.responseType,n.req.withCredentials=t.withCredentials,n.req.timeout=t.timeout,n.req.headers=new Headers(n.xhrSetRequestHeadersAfterOpen);const o=n.req;let a={...n.req,headers:new Headers(n.req.headers)};try{a=await n.execute(a,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in xhr request hooks:",e)}n.req=a;const c=o.method!==a.method||o.url!==a.url;c&&e.nativeXhrPrototype.open.apply(t,[n.req.method,n.req.url,...n.xhrOpenRestArgs||[]]);const h=["responseType","withCredentials","timeout"];for(const e of h)a[e]!==o[e]&&(t[e]=a[e]);!c&&e.headersEqual(o.headers,a.headers)||n.req.headers.forEach((e,r)=>{t.setRequestHeader(r,e)}),e.nativeXhrPrototype.send.apply(t,[n.req.data])}},setRequestHeader:function(e,t){return function(s,n){const o=t[r];e.nativeXhrPrototype.setRequestHeader.apply(t,[s,n]),o.xhrSetRequestHeadersAfterOpen.append(s,n)}},addEventListener:function(e,t,r){return function(s,n,...o){const a=e.xhrResponseEvents.includes(s);t.addEventListener(s,async function(...s){a&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(n,r,s)},...o)}}};constructor(){}inject(){window.XMLHttpRequest=this._generateProxyXMLHttpRequest()}uninject(){window.XMLHttpRequest=this.nativeXhr}parseHeaders(e){const t={};if(!e)return t;const r=(e,r)=>{const s=e.toLowerCase();t[s]=s in t?`${t[s]}, ${r}`:r},s=n(e);if("[object String]"===s){const t=e;for(const e of t.trim().split(/[\r\n]+/)){const t=e.indexOf(":");if(-1===t)continue;const s=e.slice(0,t).trim(),n=e.slice(t+1).trim();s&&r(s,n)}}else if("[object Headers]"===s){e.forEach((e,t)=>{r(t,e)})}else if("[object Object]"===s){const t=e;for(const[e,s]of Object.entries(t))null!=s&&r(e,String(s))}return t}async responseProcessor(e){const t=e[r];if(!t.xhrAlreadyReturned){t.xhrAlreadyReturned=!0,t.resp={status:e.status,statusText:e.statusText,response:e.response,headers:new Headers(this.parseHeaders(e.getAllResponseHeaders())),finalUrl:e.responseURL||""};try{await t.req.response(t.resp)}catch(e){console.warn("[AjaxInterceptor] Error in xhr response callback:",e)}}}headersEqual(e,t){if(e===t)return!0;const r=e=>{const t=[];return e.forEach((e,r)=>t.push(`${r}: ${e}`)),t.sort().toString()};return r(e)===r(t)}getAttrHandler(e,t,r){return this.xhrInstanceAttr.includes(t)?this.xhrInstanceAttrHandler[t](e):this.xhrMethodsHandler[t]?this.xhrMethodsHandler[t](this,e,r):null}_generateProxyXMLHttpRequest(){const e=this;function t(){const t=new e.nativeXhr;t[r]=new i;return new Proxy(t,{get:(t,r,s)=>e.getAttrHandler(t,r,s)??a(t,r),set(t,r,s,n){if("function"==typeof s&&r.startsWith("on")){const o=e.xhrResponseEvents.includes(r.replace(/^on/,"")),a=async function(...r){o&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(s,n,r)};return Reflect.set(t,r,a)}return Reflect.set(t,r,s)}})}return c({source:e.nativeXhr,target:t,prototype:this.nativeXhrPrototype}),t}}class i extends s{xhrAlreadyReturned=!1;xhrOpenRestArgs=[];xhrSetRequestHeadersAfterOpen=new Headers;xhrReset(){this.req={},this.resp={},this.xhrOpenRestArgs=[],this.xhrSetRequestHeadersAfterOpen=new Headers,this.xhrAlreadyReturned=!1}constructor({req:e={}}={}){super({req:e})}}class d extends s{constructor({req:e={}}={}){super({req:e})}}class u{nativeFetch=window.fetch;nativeFetchPrototype=this.nativeFetch.prototype;hooks=[];fetchInstanceAttr=["status","statusText","ok","headers","redirected"];fetchInstanceAttrHandler=this.fetchInstanceAttr.reduce((e,t)=>(e[t]=function(e,s){return s[r].resp[t]},e),{});fetchMethods=["json","formData","blob","arrayBuffer","text"];fetchMethodsHandler=this.fetchMethods.reduce((e,t)=>(e[t]=function(e,s){return async function(...e){return s[r].resp[t]}},e),{});constructor(){}inject(){window.fetch=this._generateProxyFetch()}uninject(){window.fetch=this.nativeFetch}getAttrHandler(e,t){return this.fetchInstanceAttr.includes(t)?this.fetchInstanceAttrHandler[t](this,e):this.fetchMethodsHandler[t]?this.fetchMethodsHandler[t](this,e):null}normalizeRequest(e){let t="",r=null,s=null,n=null;return"string"==typeof e||e instanceof URL?t=o(e):(t=o(e.url),r=e.method??null,s=e.headers??null,n=e.body??null),{url:t,method:r,headers:s,data:n}}resolveRequest(e,t,r){const s="string"==typeof e&&t.url!==e||e instanceof URL&&t.url!==e.href||e instanceof Request&&t.url!==e.url;if("string"==typeof e)return s?t.url:e;if(e instanceof URL)return s?new URL(t.url):e;if(e instanceof Request){if(s||"request"===r.method&&t.method!==e.method||"request"===r.headers&&t.headers!==e.headers||"request"===r.data&&t.data!==e.body){const n="request"===r.method&&t.method!==e.method,o="request"===r.headers&&t.headers!==e.headers,a="request"===r.data&&t.data!==e.body;return new Request(s?t.url:e,{...n&&{method:t.method},...o&&{headers:t.headers},...a&&{body:t.data}})}return e}return e}resolveOptions({options:e,newRequest:t,sourceMap:r}){const{method:s,headers:n,body:o,...a}=e||{},c={...a};if(("options"===r.method||"default"===r.method&&t.method!==(e?.method??"GET"))&&(c.method=t.method),"options"===r.headers)c.headers=t.headers;else if("default"===r.headers){let e=!0;if(t.headers instanceof Headers){let r=0;t.headers.forEach(()=>{r++}),e=0===r}else t.headers&&(e=0===Object.keys(t.headers).length);e||(c.headers=t.headers)}return("options"===r.data||"default"===r.data&&t.data!==(e?.body??null))&&(c.body=t.data),t.data instanceof ReadableStream&&(c.duplex="half"),c}resolveHeaders(e){return e instanceof Headers?e:new Headers(e)}_generateProxyFetch(){const e=this;async function s(s,n={}){const o=e.normalizeRequest(s),c=e.nativeFetch,h=new d,i={method:"default",headers:"default",data:"default"};s instanceof Request&&(i.method="request",i.headers="request",null!==s.body&&(i.data="request")),void 0!==n.method&&(i.method="options"),void 0!==n.headers&&(i.headers="options"),void 0!==n.body&&(i.data="options");const u={type:t,url:o.url,method:n.method??o.method??"GET",headers:e.resolveHeaders(n.headers??o.headers??new Headers),data:n.body??o.data??null,response:()=>{}};let l=u;try{l=await h.execute(u,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in fetch request hooks:",e)}h.req=l;const p=await c(e.resolveRequest(s,l,i),e.resolveOptions({options:n,newRequest:l,sourceMap:i})),f=p.headers.get("content-type")||"",y=f.includes("text/event-stream")||f.includes("application/stream+json")||f.includes("application/x-ndjson")||f.includes("application/jsonl")||f.includes("application/json-seq");let x=p;if(y&&p.body){h.resp={status:p.status,statusText:p.statusText,ok:p.ok,headers:p.headers,finalUrl:p.url,redirected:p.redirected};try{await h.req.response(h.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch stream response callback:",e)}let e=0;const{readable:t,writable:r}=new TransformStream({async transform(t,r){try{const s=(new TextDecoder).decode(t,{stream:!0});let n=s;if(h.req.onStreamChunk){const r={text:s,raw:t,index:e++,timestamp:Date.now()},o=await h.req.onStreamChunk(r);"string"==typeof o&&(n=o)}const o=new TextEncoder;r.enqueue(o.encode(n))}catch(e){r.enqueue(t)}}});p.body.pipeTo(r),x=new Response(t,{status:p.status,statusText:p.statusText,headers:p.headers})}else if(!y){const[e,t,r,s,n]=await Promise.allSettled([p.clone().json(),p.clone().text(),p.clone().arrayBuffer(),p.clone().blob(),p.clone().formData()]).then(e=>e.map(e=>"fulfilled"===e.status?e.value:null));h.resp={status:p.status,statusText:p.statusText,ok:p.ok,headers:p.headers,finalUrl:p.url,redirected:p.redirected,json:e,text:t,arrayBuffer:r,blob:s,formData:n};try{await h.req.response(h.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch response callback:",e)}}x[r]=h;return new Proxy(x,{get(t,r){const s=e.getAttrHandler(t,r);return s||a(t,r)},set:(e,t,r)=>Reflect.set(e,t,r)})}return c({source:this.nativeFetch,target:s,prototype:this.nativeFetchPrototype}),s}}class l{xhrInterceptor;fetchInterceptor;static#e;static#t=Symbol("AjaxInterceptor");static getInstance(e={}){return l.#e||(l.#e=new l(l.#t,e)),l.#e}constructor(e,t={}){if(e!==l.#t)throw new Error("AjaxInterceptor is a singleton");this.xhrInterceptor=new h,this.fetchInterceptor=new u}toggleInject(r,s){switch(r){case e:this.xhrInterceptor[s]();break;case t:this.fetchInterceptor[s]();break;default:this.xhrInterceptor[s](),this.fetchInterceptor[s]()}}inject(e){if("undefined"==typeof window)throw new Error("AjaxInterceptor requires a browser environment");window.XMLHttpRequest||console.warn("XMLHttpRequest is not supported in this environment"),window.fetch||console.warn("Fetch API is not supported in this environment"),this.toggleInject(e,"inject")}uninject(e){this.toggleInject(e,"uninject")}hook(r,s){switch(s){case e:this.xhrInterceptor.hooks.push(r);break;case t:this.fetchInterceptor.hooks.push(r);break;default:this.xhrInterceptor.hooks.push(r),this.fetchInterceptor.hooks.push(r)}}unhook(r,s){const n=e=>{if(!r)return void(e.length=0);const t=e.indexOf(r);-1!==t&&e.splice(t,1)};switch(s){case e:n(this.xhrInterceptor.hooks);break;case t:n(this.fetchInterceptor.hooks);break;default:n(this.xhrInterceptor.hooks),n(this.fetchInterceptor.hooks)}}}exports.AjaxInterceptor=l,exports.default=l;
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- const e="xhr",t="fetch",r=Symbol("CycleScheduler");class n{req={};resp={};constructor({req:e={}}={}){this.req=e}async execute(e,t){let r=e;for(const e of t){const t=await e(r);t&&(r=t)}return r}}Object.getOwnPropertyDescriptor.bind(Object);const s=Object.prototype.toString.call.bind(Object.prototype.toString),o=(e="")=>new URL(e,window.location.origin).toString(),a=(e,t)=>{const r=Reflect.get(e,t);return"function"!=typeof r?r:function(...t){return Reflect.apply(r,e,t)}},c=({source:e,target:t,prototype:r})=>{Object.keys(e).forEach(r=>{t[r]=e[r]}),t.prototype=r};class i{nativeXhr=window.XMLHttpRequest;nativeXhrPrototype=window.XMLHttpRequest.prototype;hooks=[];xhrResponseEvents=["readystatechange","load","loadend"];xhrInstanceAttr=["response","responseText","responseXML","status","statusText"];xhrInstanceAttrHandler=this.xhrInstanceAttr.reduce((e,t)=>(e[t]=function(e){const n=e[r];return n.xhrAlreadyReturned?n.resp[t]:e[t]},e),{});xhrMethodsHandler={open:function(t,n){return function(...s){const a=n[r];a.xhrReset(),a.req={type:e,method:s[0]||"GET",url:o(s[1]),headers:new Headers,data:null,response:()=>{}},a.xhrOpenRestArgs=s.slice(2),t.nativeXhrPrototype.open.apply(n,[a.req.method,a.req.url,...a.xhrOpenRestArgs||[]])}},send:function(e,t){return async function(n){const s=t[r];s.req.data=n??null,s.req.responseType=t.responseType,s.req.withCredentials=t.withCredentials,s.req.timeout=t.timeout,s.req.headers=new Headers(s.xhrSetRequestHeadersAfterOpen);const o=s.req;let a={...s.req,headers:new Headers(s.req.headers)};try{a=await s.execute(a,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in xhr request hooks:",e)}s.req=a;const c=o.method!==a.method||o.url!==a.url;c&&e.nativeXhrPrototype.open.apply(t,[s.req.method,s.req.url,...s.xhrOpenRestArgs||[]]);const i=["responseType","withCredentials","timeout"];for(const e of i)a[e]!==o[e]&&(t[e]=a[e]);!c&&e.headersEqual(o.headers,a.headers)||s.req.headers.forEach((e,r)=>{t.setRequestHeader(r,e)}),e.nativeXhrPrototype.send.apply(t,[s.req.data])}},setRequestHeader:function(e,t){return function(n,s){const o=t[r];e.nativeXhrPrototype.setRequestHeader.apply(t,[n,s]),o.xhrSetRequestHeadersAfterOpen.append(n,s)}},addEventListener:function(e,t,r){return function(n,s,...o){const a=e.xhrResponseEvents.includes(n);t.addEventListener(n,async function(...n){a&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(s,r,n)},...o)}}};constructor(){}inject(){window.XMLHttpRequest=this._generateProxyXMLHttpRequest()}uninject(){window.XMLHttpRequest=this.nativeXhr}parseHeaders(e){const t={};if(!e)return t;const r=(e,r)=>{const n=e.toLowerCase();t[n]=n in t?`${t[n]}, ${r}`:r},n=s(e);if("[object String]"===n){const t=e;for(const e of t.trim().split(/[\r\n]+/)){const t=e.indexOf(":");if(-1===t)continue;const n=e.slice(0,t).trim(),s=e.slice(t+1).trim();n&&r(n,s)}}else if("[object Headers]"===n){e.forEach((e,t)=>{r(t,e)})}else if("[object Object]"===n){const t=e;for(const[e,n]of Object.entries(t))null!=n&&r(e,String(n))}return t}async responseProcessor(e){const t=e[r];if(!t.xhrAlreadyReturned){t.xhrAlreadyReturned=!0,t.resp={status:e.status,statusText:e.statusText,response:e.response,headers:new Headers(this.parseHeaders(e.getAllResponseHeaders())),finalUrl:e.responseURL||""};try{await t.req.response(t.resp)}catch(e){console.warn("[AjaxInterceptor] Error in xhr response callback:",e)}}}headersEqual(e,t){if(e===t)return!0;const r=e=>{const t=[];return e.forEach((e,r)=>t.push(`${r}: ${e}`)),t.sort().toString()};return r(e)===r(t)}getAttrHandler(e,t,r){return this.xhrInstanceAttr.includes(t)?this.xhrInstanceAttrHandler[t](e):this.xhrMethodsHandler[t]?this.xhrMethodsHandler[t](this,e,r):null}_generateProxyXMLHttpRequest(){const e=this;function t(){const t=new e.nativeXhr;t[r]=new h;return new Proxy(t,{get:(t,r,n)=>e.getAttrHandler(t,r,n)??a(t,r),set(t,r,n,s){if("function"==typeof n&&r.startsWith("on")){const o=e.xhrResponseEvents.includes(r.replace(/^on/,"")),a=async function(...r){o&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(n,s,r)};return Reflect.set(t,r,a)}return Reflect.set(t,r,n)}})}return c({source:e.nativeXhr,target:t,prototype:this.nativeXhrPrototype}),t}}class h extends n{xhrAlreadyReturned=!1;xhrOpenRestArgs=[];xhrSetRequestHeadersAfterOpen=new Headers;xhrReset(){this.req={},this.resp={},this.xhrOpenRestArgs=[],this.xhrSetRequestHeadersAfterOpen=new Headers,this.xhrAlreadyReturned=!1}constructor({req:e={}}={}){super({req:e})}}class u extends n{constructor({req:e={}}={}){super({req:e})}}class d{nativeFetch=window.fetch;nativeFetchPrototype=this.nativeFetch.prototype;hooks=[];fetchInstanceAttr=["status","statusText","ok","headers","redirected"];fetchInstanceAttrHandler=this.fetchInstanceAttr.reduce((e,t)=>(e[t]=function(e,n){return n[r].resp[t]},e),{});fetchMethods=["json","formData","blob","arrayBuffer","text"];fetchMethodsHandler=this.fetchMethods.reduce((e,t)=>(e[t]=function(e,n){return async function(...e){return n[r].resp[t]}},e),{});constructor(){}inject(){window.fetch=this._generateProxyFetch()}uninject(){window.fetch=this.nativeFetch}getAttrHandler(e,t){return this.fetchInstanceAttr.includes(t)?this.fetchInstanceAttrHandler[t](this,e):this.fetchMethodsHandler[t]?this.fetchMethodsHandler[t](this,e):null}normalizeRequest(e){let t="",r=null,n=null,s=null;return"string"==typeof e||e instanceof URL?t=o(e):(t=o(e.url),r=e.method??null,n=e.headers??null,s=e.body??null),{url:t,method:r,headers:n,data:s}}resolveRequest(e,t){return"string"==typeof e?t.url:e instanceof URL?new URL(t.url):e instanceof Request?new Request(t.url,e):e}resolveOptions({options:e,newRequest:t}){return{...e,headers:t.headers,body:t.data,method:t.method,...t.data instanceof ReadableStream?{duplex:"half"}:{}}}resolveHeaders(e){return e instanceof Headers?e:new Headers(e)}_generateProxyFetch(){const e=this;async function n(n,s={}){const o=e.normalizeRequest(n),c=e.nativeFetch,i=new u;let h=o;try{h=await i.execute({type:t,url:o.url,method:s.method??o.method??"GET",headers:e.resolveHeaders(s.headers??o.headers??new Headers),data:s.body??o.data??null,response:()=>{}},e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in fetch request hooks:",e)}i.req=h;const d=await c(e.resolveRequest(n,h),e.resolveOptions({options:s,newRequest:h})),l=d.headers.get("content-type")||"",p=l.includes("text/event-stream")||l.includes("application/stream+json")||l.includes("application/x-ndjson")||l.includes("application/jsonl")||l.includes("application/json-seq");let f=d;if(p&&d.body){i.resp={status:d.status,statusText:d.statusText,ok:d.ok,headers:d.headers,finalUrl:d.url,redirected:d.redirected};try{await i.req.response(i.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch stream response callback:",e)}let e=0;const{readable:t,writable:r}=new TransformStream({async transform(t,r){try{const n=(new TextDecoder).decode(t,{stream:!0});let s=n;if(i.req.onStreamChunk){const r={text:n,raw:t,index:e++,timestamp:Date.now()},o=await i.req.onStreamChunk(r);"string"==typeof o&&(s=o)}const o=new TextEncoder;r.enqueue(o.encode(s))}catch(e){r.enqueue(t)}}});d.body.pipeTo(r),f=new Response(t,{status:d.status,statusText:d.statusText,headers:d.headers})}else if(!p){const[e,t,r,n,s]=await Promise.allSettled([d.clone().json(),d.clone().text(),d.clone().arrayBuffer(),d.clone().blob(),d.clone().formData()]).then(e=>e.map(e=>"fulfilled"===e.status?e.value:null));i.resp={status:d.status,statusText:d.statusText,ok:d.ok,headers:d.headers,finalUrl:d.url,redirected:d.redirected,json:e,text:t,arrayBuffer:r,blob:n,formData:s};try{await i.req.response(i.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch response callback:",e)}}f[r]=i;return new Proxy(f,{get(t,r){const n=e.getAttrHandler(t,r);return n||a(t,r)},set:(e,t,r)=>Reflect.set(e,t,r)})}return c({source:this.nativeFetch,target:n,prototype:this.nativeFetchPrototype}),n}}class l{xhrInterceptor;fetchInterceptor;static#e;static#t=Symbol("AjaxInterceptor");static getInstance(e={}){return l.#e||(l.#e=new l(l.#t,e)),l.#e}constructor(e,t={}){if(e!==l.#t)throw new Error("AjaxInterceptor is a singleton");this.xhrInterceptor=new i,this.fetchInterceptor=new d}toggleInject(r,n){switch(r){case e:this.xhrInterceptor[n]();break;case t:this.fetchInterceptor[n]();break;default:this.xhrInterceptor[n](),this.fetchInterceptor[n]()}}inject(e){if("undefined"==typeof window)throw new Error("AjaxInterceptor requires a browser environment");window.XMLHttpRequest||console.warn("XMLHttpRequest is not supported in this environment"),window.fetch||console.warn("Fetch API is not supported in this environment"),this.toggleInject(e,"inject")}uninject(e){this.toggleInject(e,"uninject")}hook(r,n){switch(n){case e:this.xhrInterceptor.hooks.push(r);break;case t:this.fetchInterceptor.hooks.push(r);break;default:this.xhrInterceptor.hooks.push(r),this.fetchInterceptor.hooks.push(r)}}}export{l as AjaxInterceptor,l as default};
1
+ const e="xhr",t="fetch",r=Symbol("CycleScheduler");class s{req={};resp={};constructor({req:e={}}={}){this.req=e}async execute(e,t){let r=e;for(const e of t){const t=await e(r);t&&(r=t)}return r}}Object.getOwnPropertyDescriptor.bind(Object);const n=Object.prototype.toString.call.bind(Object.prototype.toString),o=(e="")=>new URL(e,window.location.origin).toString(),a=(e,t)=>{const r=Reflect.get(e,t);return"function"!=typeof r?r:function(...t){return Reflect.apply(r,e,t)}},c=({source:e,target:t,prototype:r})=>{Object.keys(e).forEach(r=>{t[r]=e[r]}),t.prototype=r};class h{nativeXhr=window.XMLHttpRequest;nativeXhrPrototype=window.XMLHttpRequest.prototype;hooks=[];xhrResponseEvents=["readystatechange","load","loadend"];xhrInstanceAttr=["response","responseText","responseXML","status","statusText"];xhrInstanceAttrHandler=this.xhrInstanceAttr.reduce((e,t)=>(e[t]=function(e){const s=e[r];return s.xhrAlreadyReturned?s.resp[t]:e[t]},e),{});xhrMethodsHandler={open:function(t,s){return function(...n){const a=s[r];a.xhrReset(),a.req={type:e,method:n[0]||"GET",url:o(n[1]),headers:new Headers,data:null,response:()=>{}},a.xhrOpenRestArgs=n.slice(2),t.nativeXhrPrototype.open.apply(s,[a.req.method,a.req.url,...a.xhrOpenRestArgs||[]])}},send:function(e,t){return async function(s){const n=t[r];n.req.data=s??null,n.req.responseType=t.responseType,n.req.withCredentials=t.withCredentials,n.req.timeout=t.timeout,n.req.headers=new Headers(n.xhrSetRequestHeadersAfterOpen);const o=n.req;let a={...n.req,headers:new Headers(n.req.headers)};try{a=await n.execute(a,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in xhr request hooks:",e)}n.req=a;const c=o.method!==a.method||o.url!==a.url;c&&e.nativeXhrPrototype.open.apply(t,[n.req.method,n.req.url,...n.xhrOpenRestArgs||[]]);const h=["responseType","withCredentials","timeout"];for(const e of h)a[e]!==o[e]&&(t[e]=a[e]);!c&&e.headersEqual(o.headers,a.headers)||n.req.headers.forEach((e,r)=>{t.setRequestHeader(r,e)}),e.nativeXhrPrototype.send.apply(t,[n.req.data])}},setRequestHeader:function(e,t){return function(s,n){const o=t[r];e.nativeXhrPrototype.setRequestHeader.apply(t,[s,n]),o.xhrSetRequestHeadersAfterOpen.append(s,n)}},addEventListener:function(e,t,r){return function(s,n,...o){const a=e.xhrResponseEvents.includes(s);t.addEventListener(s,async function(...s){a&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(n,r,s)},...o)}}};constructor(){}inject(){window.XMLHttpRequest=this._generateProxyXMLHttpRequest()}uninject(){window.XMLHttpRequest=this.nativeXhr}parseHeaders(e){const t={};if(!e)return t;const r=(e,r)=>{const s=e.toLowerCase();t[s]=s in t?`${t[s]}, ${r}`:r},s=n(e);if("[object String]"===s){const t=e;for(const e of t.trim().split(/[\r\n]+/)){const t=e.indexOf(":");if(-1===t)continue;const s=e.slice(0,t).trim(),n=e.slice(t+1).trim();s&&r(s,n)}}else if("[object Headers]"===s){e.forEach((e,t)=>{r(t,e)})}else if("[object Object]"===s){const t=e;for(const[e,s]of Object.entries(t))null!=s&&r(e,String(s))}return t}async responseProcessor(e){const t=e[r];if(!t.xhrAlreadyReturned){t.xhrAlreadyReturned=!0,t.resp={status:e.status,statusText:e.statusText,response:e.response,headers:new Headers(this.parseHeaders(e.getAllResponseHeaders())),finalUrl:e.responseURL||""};try{await t.req.response(t.resp)}catch(e){console.warn("[AjaxInterceptor] Error in xhr response callback:",e)}}}headersEqual(e,t){if(e===t)return!0;const r=e=>{const t=[];return e.forEach((e,r)=>t.push(`${r}: ${e}`)),t.sort().toString()};return r(e)===r(t)}getAttrHandler(e,t,r){return this.xhrInstanceAttr.includes(t)?this.xhrInstanceAttrHandler[t](e):this.xhrMethodsHandler[t]?this.xhrMethodsHandler[t](this,e,r):null}_generateProxyXMLHttpRequest(){const e=this;function t(){const t=new e.nativeXhr;t[r]=new i;return new Proxy(t,{get:(t,r,s)=>e.getAttrHandler(t,r,s)??a(t,r),set(t,r,s,n){if("function"==typeof s&&r.startsWith("on")){const o=e.xhrResponseEvents.includes(r.replace(/^on/,"")),a=async function(...r){o&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(s,n,r)};return Reflect.set(t,r,a)}return Reflect.set(t,r,s)}})}return c({source:e.nativeXhr,target:t,prototype:this.nativeXhrPrototype}),t}}class i extends s{xhrAlreadyReturned=!1;xhrOpenRestArgs=[];xhrSetRequestHeadersAfterOpen=new Headers;xhrReset(){this.req={},this.resp={},this.xhrOpenRestArgs=[],this.xhrSetRequestHeadersAfterOpen=new Headers,this.xhrAlreadyReturned=!1}constructor({req:e={}}={}){super({req:e})}}class d extends s{constructor({req:e={}}={}){super({req:e})}}class u{nativeFetch=window.fetch;nativeFetchPrototype=this.nativeFetch.prototype;hooks=[];fetchInstanceAttr=["status","statusText","ok","headers","redirected"];fetchInstanceAttrHandler=this.fetchInstanceAttr.reduce((e,t)=>(e[t]=function(e,s){return s[r].resp[t]},e),{});fetchMethods=["json","formData","blob","arrayBuffer","text"];fetchMethodsHandler=this.fetchMethods.reduce((e,t)=>(e[t]=function(e,s){return async function(...e){return s[r].resp[t]}},e),{});constructor(){}inject(){window.fetch=this._generateProxyFetch()}uninject(){window.fetch=this.nativeFetch}getAttrHandler(e,t){return this.fetchInstanceAttr.includes(t)?this.fetchInstanceAttrHandler[t](this,e):this.fetchMethodsHandler[t]?this.fetchMethodsHandler[t](this,e):null}normalizeRequest(e){let t="",r=null,s=null,n=null;return"string"==typeof e||e instanceof URL?t=o(e):(t=o(e.url),r=e.method??null,s=e.headers??null,n=e.body??null),{url:t,method:r,headers:s,data:n}}resolveRequest(e,t,r){const s="string"==typeof e&&t.url!==e||e instanceof URL&&t.url!==e.href||e instanceof Request&&t.url!==e.url;if("string"==typeof e)return s?t.url:e;if(e instanceof URL)return s?new URL(t.url):e;if(e instanceof Request){if(s||"request"===r.method&&t.method!==e.method||"request"===r.headers&&t.headers!==e.headers||"request"===r.data&&t.data!==e.body){const n="request"===r.method&&t.method!==e.method,o="request"===r.headers&&t.headers!==e.headers,a="request"===r.data&&t.data!==e.body;return new Request(s?t.url:e,{...n&&{method:t.method},...o&&{headers:t.headers},...a&&{body:t.data}})}return e}return e}resolveOptions({options:e,newRequest:t,sourceMap:r}){const{method:s,headers:n,body:o,...a}=e||{},c={...a};if(("options"===r.method||"default"===r.method&&t.method!==(e?.method??"GET"))&&(c.method=t.method),"options"===r.headers)c.headers=t.headers;else if("default"===r.headers){let e=!0;if(t.headers instanceof Headers){let r=0;t.headers.forEach(()=>{r++}),e=0===r}else t.headers&&(e=0===Object.keys(t.headers).length);e||(c.headers=t.headers)}return("options"===r.data||"default"===r.data&&t.data!==(e?.body??null))&&(c.body=t.data),t.data instanceof ReadableStream&&(c.duplex="half"),c}resolveHeaders(e){return e instanceof Headers?e:new Headers(e)}_generateProxyFetch(){const e=this;async function s(s,n={}){const o=e.normalizeRequest(s),c=e.nativeFetch,h=new d,i={method:"default",headers:"default",data:"default"};s instanceof Request&&(i.method="request",i.headers="request",null!==s.body&&(i.data="request")),void 0!==n.method&&(i.method="options"),void 0!==n.headers&&(i.headers="options"),void 0!==n.body&&(i.data="options");const u={type:t,url:o.url,method:n.method??o.method??"GET",headers:e.resolveHeaders(n.headers??o.headers??new Headers),data:n.body??o.data??null,response:()=>{}};let l=u;try{l=await h.execute(u,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in fetch request hooks:",e)}h.req=l;const p=await c(e.resolveRequest(s,l,i),e.resolveOptions({options:n,newRequest:l,sourceMap:i})),f=p.headers.get("content-type")||"",y=f.includes("text/event-stream")||f.includes("application/stream+json")||f.includes("application/x-ndjson")||f.includes("application/jsonl")||f.includes("application/json-seq");let w=p;if(y&&p.body){h.resp={status:p.status,statusText:p.statusText,ok:p.ok,headers:p.headers,finalUrl:p.url,redirected:p.redirected};try{await h.req.response(h.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch stream response callback:",e)}let e=0;const{readable:t,writable:r}=new TransformStream({async transform(t,r){try{const s=(new TextDecoder).decode(t,{stream:!0});let n=s;if(h.req.onStreamChunk){const r={text:s,raw:t,index:e++,timestamp:Date.now()},o=await h.req.onStreamChunk(r);"string"==typeof o&&(n=o)}const o=new TextEncoder;r.enqueue(o.encode(n))}catch(e){r.enqueue(t)}}});p.body.pipeTo(r),w=new Response(t,{status:p.status,statusText:p.statusText,headers:p.headers})}else if(!y){const[e,t,r,s,n]=await Promise.allSettled([p.clone().json(),p.clone().text(),p.clone().arrayBuffer(),p.clone().blob(),p.clone().formData()]).then(e=>e.map(e=>"fulfilled"===e.status?e.value:null));h.resp={status:p.status,statusText:p.statusText,ok:p.ok,headers:p.headers,finalUrl:p.url,redirected:p.redirected,json:e,text:t,arrayBuffer:r,blob:s,formData:n};try{await h.req.response(h.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch response callback:",e)}}w[r]=h;return new Proxy(w,{get(t,r){const s=e.getAttrHandler(t,r);return s||a(t,r)},set:(e,t,r)=>Reflect.set(e,t,r)})}return c({source:this.nativeFetch,target:s,prototype:this.nativeFetchPrototype}),s}}class l{xhrInterceptor;fetchInterceptor;static#e;static#t=Symbol("AjaxInterceptor");static getInstance(e={}){return l.#e||(l.#e=new l(l.#t,e)),l.#e}constructor(e,t={}){if(e!==l.#t)throw new Error("AjaxInterceptor is a singleton");this.xhrInterceptor=new h,this.fetchInterceptor=new u}toggleInject(r,s){switch(r){case e:this.xhrInterceptor[s]();break;case t:this.fetchInterceptor[s]();break;default:this.xhrInterceptor[s](),this.fetchInterceptor[s]()}}inject(e){if("undefined"==typeof window)throw new Error("AjaxInterceptor requires a browser environment");window.XMLHttpRequest||console.warn("XMLHttpRequest is not supported in this environment"),window.fetch||console.warn("Fetch API is not supported in this environment"),this.toggleInject(e,"inject")}uninject(e){this.toggleInject(e,"uninject")}hook(r,s){switch(s){case e:this.xhrInterceptor.hooks.push(r);break;case t:this.fetchInterceptor.hooks.push(r);break;default:this.xhrInterceptor.hooks.push(r),this.fetchInterceptor.hooks.push(r)}}unhook(r,s){const n=e=>{if(!r)return void(e.length=0);const t=e.indexOf(r);-1!==t&&e.splice(t,1)};switch(s){case e:n(this.xhrInterceptor.hooks);break;case t:n(this.fetchInterceptor.hooks);break;default:n(this.xhrInterceptor.hooks),n(this.fetchInterceptor.hooks)}}}export{l as AjaxInterceptor,l as default};
@@ -1 +1 @@
1
- var ajaxInterceptor=function(e){"use strict";const t="xhr",r="fetch",n=Symbol("CycleScheduler");class s{req={};resp={};constructor({req:e={}}={}){this.req=e}async execute(e,t){let r=e;for(const e of t){const t=await e(r);t&&(r=t)}return r}}Object.getOwnPropertyDescriptor.bind(Object);const o=Object.prototype.toString.call.bind(Object.prototype.toString),a=(e="")=>new URL(e,window.location.origin).toString(),c=(e,t)=>{const r=Reflect.get(e,t);return"function"!=typeof r?r:function(...t){return Reflect.apply(r,e,t)}},i=({source:e,target:t,prototype:r})=>{Object.keys(e).forEach(r=>{t[r]=e[r]}),t.prototype=r};class h{nativeXhr=window.XMLHttpRequest;nativeXhrPrototype=window.XMLHttpRequest.prototype;hooks=[];xhrResponseEvents=["readystatechange","load","loadend"];xhrInstanceAttr=["response","responseText","responseXML","status","statusText"];xhrInstanceAttrHandler=this.xhrInstanceAttr.reduce((e,t)=>(e[t]=function(e){const r=e[n];return r.xhrAlreadyReturned?r.resp[t]:e[t]},e),{});xhrMethodsHandler={open:function(e,r){return function(...s){const o=r[n];o.xhrReset(),o.req={type:t,method:s[0]||"GET",url:a(s[1]),headers:new Headers,data:null,response:()=>{}},o.xhrOpenRestArgs=s.slice(2),e.nativeXhrPrototype.open.apply(r,[o.req.method,o.req.url,...o.xhrOpenRestArgs||[]])}},send:function(e,t){return async function(r){const s=t[n];s.req.data=r??null,s.req.responseType=t.responseType,s.req.withCredentials=t.withCredentials,s.req.timeout=t.timeout,s.req.headers=new Headers(s.xhrSetRequestHeadersAfterOpen);const o=s.req;let a={...s.req,headers:new Headers(s.req.headers)};try{a=await s.execute(a,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in xhr request hooks:",e)}s.req=a;const c=o.method!==a.method||o.url!==a.url;c&&e.nativeXhrPrototype.open.apply(t,[s.req.method,s.req.url,...s.xhrOpenRestArgs||[]]);const i=["responseType","withCredentials","timeout"];for(const e of i)a[e]!==o[e]&&(t[e]=a[e]);!c&&e.headersEqual(o.headers,a.headers)||s.req.headers.forEach((e,r)=>{t.setRequestHeader(r,e)}),e.nativeXhrPrototype.send.apply(t,[s.req.data])}},setRequestHeader:function(e,t){return function(r,s){const o=t[n];e.nativeXhrPrototype.setRequestHeader.apply(t,[r,s]),o.xhrSetRequestHeadersAfterOpen.append(r,s)}},addEventListener:function(e,t,r){return function(n,s,...o){const a=e.xhrResponseEvents.includes(n);t.addEventListener(n,async function(...n){a&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(s,r,n)},...o)}}};constructor(){}inject(){window.XMLHttpRequest=this._generateProxyXMLHttpRequest()}uninject(){window.XMLHttpRequest=this.nativeXhr}parseHeaders(e){const t={};if(!e)return t;const r=(e,r)=>{const n=e.toLowerCase();t[n]=n in t?`${t[n]}, ${r}`:r},n=o(e);if("[object String]"===n){const t=e;for(const e of t.trim().split(/[\r\n]+/)){const t=e.indexOf(":");if(-1===t)continue;const n=e.slice(0,t).trim(),s=e.slice(t+1).trim();n&&r(n,s)}}else if("[object Headers]"===n){e.forEach((e,t)=>{r(t,e)})}else if("[object Object]"===n){const t=e;for(const[e,n]of Object.entries(t))null!=n&&r(e,String(n))}return t}async responseProcessor(e){const t=e[n];if(!t.xhrAlreadyReturned){t.xhrAlreadyReturned=!0,t.resp={status:e.status,statusText:e.statusText,response:e.response,headers:new Headers(this.parseHeaders(e.getAllResponseHeaders())),finalUrl:e.responseURL||""};try{await t.req.response(t.resp)}catch(e){console.warn("[AjaxInterceptor] Error in xhr response callback:",e)}}}headersEqual(e,t){if(e===t)return!0;const r=e=>{const t=[];return e.forEach((e,r)=>t.push(`${r}: ${e}`)),t.sort().toString()};return r(e)===r(t)}getAttrHandler(e,t,r){return this.xhrInstanceAttr.includes(t)?this.xhrInstanceAttrHandler[t](e):this.xhrMethodsHandler[t]?this.xhrMethodsHandler[t](this,e,r):null}_generateProxyXMLHttpRequest(){const e=this;function t(){const t=new e.nativeXhr;t[n]=new u;return new Proxy(t,{get:(t,r,n)=>e.getAttrHandler(t,r,n)??c(t,r),set(t,r,n,s){if("function"==typeof n&&r.startsWith("on")){const o=e.xhrResponseEvents.includes(r.replace(/^on/,"")),a=async function(...r){o&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(n,s,r)};return Reflect.set(t,r,a)}return Reflect.set(t,r,n)}})}return i({source:e.nativeXhr,target:t,prototype:this.nativeXhrPrototype}),t}}class u extends s{xhrAlreadyReturned=!1;xhrOpenRestArgs=[];xhrSetRequestHeadersAfterOpen=new Headers;xhrReset(){this.req={},this.resp={},this.xhrOpenRestArgs=[],this.xhrSetRequestHeadersAfterOpen=new Headers,this.xhrAlreadyReturned=!1}constructor({req:e={}}={}){super({req:e})}}class d extends s{constructor({req:e={}}={}){super({req:e})}}class l{nativeFetch=window.fetch;nativeFetchPrototype=this.nativeFetch.prototype;hooks=[];fetchInstanceAttr=["status","statusText","ok","headers","redirected"];fetchInstanceAttrHandler=this.fetchInstanceAttr.reduce((e,t)=>(e[t]=function(e,r){return r[n].resp[t]},e),{});fetchMethods=["json","formData","blob","arrayBuffer","text"];fetchMethodsHandler=this.fetchMethods.reduce((e,t)=>(e[t]=function(e,r){return async function(...e){return r[n].resp[t]}},e),{});constructor(){}inject(){window.fetch=this._generateProxyFetch()}uninject(){window.fetch=this.nativeFetch}getAttrHandler(e,t){return this.fetchInstanceAttr.includes(t)?this.fetchInstanceAttrHandler[t](this,e):this.fetchMethodsHandler[t]?this.fetchMethodsHandler[t](this,e):null}normalizeRequest(e){let t="",r=null,n=null,s=null;return"string"==typeof e||e instanceof URL?t=a(e):(t=a(e.url),r=e.method??null,n=e.headers??null,s=e.body??null),{url:t,method:r,headers:n,data:s}}resolveRequest(e,t){return"string"==typeof e?t.url:e instanceof URL?new URL(t.url):e instanceof Request?new Request(t.url,e):e}resolveOptions({options:e,newRequest:t}){return{...e,headers:t.headers,body:t.data,method:t.method,...t.data instanceof ReadableStream?{duplex:"half"}:{}}}resolveHeaders(e){return e instanceof Headers?e:new Headers(e)}_generateProxyFetch(){const e=this;async function t(t,s={}){const o=e.normalizeRequest(t),a=e.nativeFetch,i=new d;let h=o;try{h=await i.execute({type:r,url:o.url,method:s.method??o.method??"GET",headers:e.resolveHeaders(s.headers??o.headers??new Headers),data:s.body??o.data??null,response:()=>{}},e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in fetch request hooks:",e)}i.req=h;const u=await a(e.resolveRequest(t,h),e.resolveOptions({options:s,newRequest:h})),l=u.headers.get("content-type")||"",p=l.includes("text/event-stream")||l.includes("application/stream+json")||l.includes("application/x-ndjson")||l.includes("application/jsonl")||l.includes("application/json-seq");let f=u;if(p&&u.body){i.resp={status:u.status,statusText:u.statusText,ok:u.ok,headers:u.headers,finalUrl:u.url,redirected:u.redirected};try{await i.req.response(i.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch stream response callback:",e)}let e=0;const{readable:t,writable:r}=new TransformStream({async transform(t,r){try{const n=(new TextDecoder).decode(t,{stream:!0});let s=n;if(i.req.onStreamChunk){const r={text:n,raw:t,index:e++,timestamp:Date.now()},o=await i.req.onStreamChunk(r);"string"==typeof o&&(s=o)}const o=new TextEncoder;r.enqueue(o.encode(s))}catch(e){r.enqueue(t)}}});u.body.pipeTo(r),f=new Response(t,{status:u.status,statusText:u.statusText,headers:u.headers})}else if(!p){const[e,t,r,n,s]=await Promise.allSettled([u.clone().json(),u.clone().text(),u.clone().arrayBuffer(),u.clone().blob(),u.clone().formData()]).then(e=>e.map(e=>"fulfilled"===e.status?e.value:null));i.resp={status:u.status,statusText:u.statusText,ok:u.ok,headers:u.headers,finalUrl:u.url,redirected:u.redirected,json:e,text:t,arrayBuffer:r,blob:n,formData:s};try{await i.req.response(i.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch response callback:",e)}}f[n]=i;return new Proxy(f,{get(t,r){const n=e.getAttrHandler(t,r);return n||c(t,r)},set:(e,t,r)=>Reflect.set(e,t,r)})}return i({source:this.nativeFetch,target:t,prototype:this.nativeFetchPrototype}),t}}class p{xhrInterceptor;fetchInterceptor;static#e;static#t=Symbol("AjaxInterceptor");static getInstance(e={}){return p.#e||(p.#e=new p(p.#t,e)),p.#e}constructor(e,t={}){if(e!==p.#t)throw new Error("AjaxInterceptor is a singleton");this.xhrInterceptor=new h,this.fetchInterceptor=new l}toggleInject(e,n){switch(e){case t:this.xhrInterceptor[n]();break;case r:this.fetchInterceptor[n]();break;default:this.xhrInterceptor[n](),this.fetchInterceptor[n]()}}inject(e){if("undefined"==typeof window)throw new Error("AjaxInterceptor requires a browser environment");window.XMLHttpRequest||console.warn("XMLHttpRequest is not supported in this environment"),window.fetch||console.warn("Fetch API is not supported in this environment"),this.toggleInject(e,"inject")}uninject(e){this.toggleInject(e,"uninject")}hook(e,n){switch(n){case t:this.xhrInterceptor.hooks.push(e);break;case r:this.fetchInterceptor.hooks.push(e);break;default:this.xhrInterceptor.hooks.push(e),this.fetchInterceptor.hooks.push(e)}}}return e.AjaxInterceptor=p,e.default=p,Object.defineProperty(e,"__esModule",{value:!0}),e}({});
1
+ var ajaxInterceptor=function(e){"use strict";const t="xhr",r="fetch",s=Symbol("CycleScheduler");class n{req={};resp={};constructor({req:e={}}={}){this.req=e}async execute(e,t){let r=e;for(const e of t){const t=await e(r);t&&(r=t)}return r}}Object.getOwnPropertyDescriptor.bind(Object);const o=Object.prototype.toString.call.bind(Object.prototype.toString),a=(e="")=>new URL(e,window.location.origin).toString(),c=(e,t)=>{const r=Reflect.get(e,t);return"function"!=typeof r?r:function(...t){return Reflect.apply(r,e,t)}},h=({source:e,target:t,prototype:r})=>{Object.keys(e).forEach(r=>{t[r]=e[r]}),t.prototype=r};class i{nativeXhr=window.XMLHttpRequest;nativeXhrPrototype=window.XMLHttpRequest.prototype;hooks=[];xhrResponseEvents=["readystatechange","load","loadend"];xhrInstanceAttr=["response","responseText","responseXML","status","statusText"];xhrInstanceAttrHandler=this.xhrInstanceAttr.reduce((e,t)=>(e[t]=function(e){const r=e[s];return r.xhrAlreadyReturned?r.resp[t]:e[t]},e),{});xhrMethodsHandler={open:function(e,r){return function(...n){const o=r[s];o.xhrReset(),o.req={type:t,method:n[0]||"GET",url:a(n[1]),headers:new Headers,data:null,response:()=>{}},o.xhrOpenRestArgs=n.slice(2),e.nativeXhrPrototype.open.apply(r,[o.req.method,o.req.url,...o.xhrOpenRestArgs||[]])}},send:function(e,t){return async function(r){const n=t[s];n.req.data=r??null,n.req.responseType=t.responseType,n.req.withCredentials=t.withCredentials,n.req.timeout=t.timeout,n.req.headers=new Headers(n.xhrSetRequestHeadersAfterOpen);const o=n.req;let a={...n.req,headers:new Headers(n.req.headers)};try{a=await n.execute(a,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in xhr request hooks:",e)}n.req=a;const c=o.method!==a.method||o.url!==a.url;c&&e.nativeXhrPrototype.open.apply(t,[n.req.method,n.req.url,...n.xhrOpenRestArgs||[]]);const h=["responseType","withCredentials","timeout"];for(const e of h)a[e]!==o[e]&&(t[e]=a[e]);!c&&e.headersEqual(o.headers,a.headers)||n.req.headers.forEach((e,r)=>{t.setRequestHeader(r,e)}),e.nativeXhrPrototype.send.apply(t,[n.req.data])}},setRequestHeader:function(e,t){return function(r,n){const o=t[s];e.nativeXhrPrototype.setRequestHeader.apply(t,[r,n]),o.xhrSetRequestHeadersAfterOpen.append(r,n)}},addEventListener:function(e,t,r){return function(s,n,...o){const a=e.xhrResponseEvents.includes(s);t.addEventListener(s,async function(...s){a&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(n,r,s)},...o)}}};constructor(){}inject(){window.XMLHttpRequest=this._generateProxyXMLHttpRequest()}uninject(){window.XMLHttpRequest=this.nativeXhr}parseHeaders(e){const t={};if(!e)return t;const r=(e,r)=>{const s=e.toLowerCase();t[s]=s in t?`${t[s]}, ${r}`:r},s=o(e);if("[object String]"===s){const t=e;for(const e of t.trim().split(/[\r\n]+/)){const t=e.indexOf(":");if(-1===t)continue;const s=e.slice(0,t).trim(),n=e.slice(t+1).trim();s&&r(s,n)}}else if("[object Headers]"===s){e.forEach((e,t)=>{r(t,e)})}else if("[object Object]"===s){const t=e;for(const[e,s]of Object.entries(t))null!=s&&r(e,String(s))}return t}async responseProcessor(e){const t=e[s];if(!t.xhrAlreadyReturned){t.xhrAlreadyReturned=!0,t.resp={status:e.status,statusText:e.statusText,response:e.response,headers:new Headers(this.parseHeaders(e.getAllResponseHeaders())),finalUrl:e.responseURL||""};try{await t.req.response(t.resp)}catch(e){console.warn("[AjaxInterceptor] Error in xhr response callback:",e)}}}headersEqual(e,t){if(e===t)return!0;const r=e=>{const t=[];return e.forEach((e,r)=>t.push(`${r}: ${e}`)),t.sort().toString()};return r(e)===r(t)}getAttrHandler(e,t,r){return this.xhrInstanceAttr.includes(t)?this.xhrInstanceAttrHandler[t](e):this.xhrMethodsHandler[t]?this.xhrMethodsHandler[t](this,e,r):null}_generateProxyXMLHttpRequest(){const e=this;function t(){const t=new e.nativeXhr;t[s]=new d;return new Proxy(t,{get:(t,r,s)=>e.getAttrHandler(t,r,s)??c(t,r),set(t,r,s,n){if("function"==typeof s&&r.startsWith("on")){const o=e.xhrResponseEvents.includes(r.replace(/^on/,"")),a=async function(...r){o&&4===t.readyState&&await e.responseProcessor(t),Reflect.apply(s,n,r)};return Reflect.set(t,r,a)}return Reflect.set(t,r,s)}})}return h({source:e.nativeXhr,target:t,prototype:this.nativeXhrPrototype}),t}}class d extends n{xhrAlreadyReturned=!1;xhrOpenRestArgs=[];xhrSetRequestHeadersAfterOpen=new Headers;xhrReset(){this.req={},this.resp={},this.xhrOpenRestArgs=[],this.xhrSetRequestHeadersAfterOpen=new Headers,this.xhrAlreadyReturned=!1}constructor({req:e={}}={}){super({req:e})}}class u extends n{constructor({req:e={}}={}){super({req:e})}}class l{nativeFetch=window.fetch;nativeFetchPrototype=this.nativeFetch.prototype;hooks=[];fetchInstanceAttr=["status","statusText","ok","headers","redirected"];fetchInstanceAttrHandler=this.fetchInstanceAttr.reduce((e,t)=>(e[t]=function(e,r){return r[s].resp[t]},e),{});fetchMethods=["json","formData","blob","arrayBuffer","text"];fetchMethodsHandler=this.fetchMethods.reduce((e,t)=>(e[t]=function(e,r){return async function(...e){return r[s].resp[t]}},e),{});constructor(){}inject(){window.fetch=this._generateProxyFetch()}uninject(){window.fetch=this.nativeFetch}getAttrHandler(e,t){return this.fetchInstanceAttr.includes(t)?this.fetchInstanceAttrHandler[t](this,e):this.fetchMethodsHandler[t]?this.fetchMethodsHandler[t](this,e):null}normalizeRequest(e){let t="",r=null,s=null,n=null;return"string"==typeof e||e instanceof URL?t=a(e):(t=a(e.url),r=e.method??null,s=e.headers??null,n=e.body??null),{url:t,method:r,headers:s,data:n}}resolveRequest(e,t,r){const s="string"==typeof e&&t.url!==e||e instanceof URL&&t.url!==e.href||e instanceof Request&&t.url!==e.url;if("string"==typeof e)return s?t.url:e;if(e instanceof URL)return s?new URL(t.url):e;if(e instanceof Request){if(s||"request"===r.method&&t.method!==e.method||"request"===r.headers&&t.headers!==e.headers||"request"===r.data&&t.data!==e.body){const n="request"===r.method&&t.method!==e.method,o="request"===r.headers&&t.headers!==e.headers,a="request"===r.data&&t.data!==e.body;return new Request(s?t.url:e,{...n&&{method:t.method},...o&&{headers:t.headers},...a&&{body:t.data}})}return e}return e}resolveOptions({options:e,newRequest:t,sourceMap:r}){const{method:s,headers:n,body:o,...a}=e||{},c={...a};if(("options"===r.method||"default"===r.method&&t.method!==(e?.method??"GET"))&&(c.method=t.method),"options"===r.headers)c.headers=t.headers;else if("default"===r.headers){let e=!0;if(t.headers instanceof Headers){let r=0;t.headers.forEach(()=>{r++}),e=0===r}else t.headers&&(e=0===Object.keys(t.headers).length);e||(c.headers=t.headers)}return("options"===r.data||"default"===r.data&&t.data!==(e?.body??null))&&(c.body=t.data),t.data instanceof ReadableStream&&(c.duplex="half"),c}resolveHeaders(e){return e instanceof Headers?e:new Headers(e)}_generateProxyFetch(){const e=this;async function t(t,n={}){const o=e.normalizeRequest(t),a=e.nativeFetch,h=new u,i={method:"default",headers:"default",data:"default"};t instanceof Request&&(i.method="request",i.headers="request",null!==t.body&&(i.data="request")),void 0!==n.method&&(i.method="options"),void 0!==n.headers&&(i.headers="options"),void 0!==n.body&&(i.data="options");const d={type:r,url:o.url,method:n.method??o.method??"GET",headers:e.resolveHeaders(n.headers??o.headers??new Headers),data:n.body??o.data??null,response:()=>{}};let l=d;try{l=await h.execute(d,e.hooks)}catch(e){console.warn("[AjaxInterceptor] Error in fetch request hooks:",e)}h.req=l;const p=await a(e.resolveRequest(t,l,i),e.resolveOptions({options:n,newRequest:l,sourceMap:i})),f=p.headers.get("content-type")||"",y=f.includes("text/event-stream")||f.includes("application/stream+json")||f.includes("application/x-ndjson")||f.includes("application/jsonl")||f.includes("application/json-seq");let x=p;if(y&&p.body){h.resp={status:p.status,statusText:p.statusText,ok:p.ok,headers:p.headers,finalUrl:p.url,redirected:p.redirected};try{await h.req.response(h.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch stream response callback:",e)}let e=0;const{readable:t,writable:r}=new TransformStream({async transform(t,r){try{const s=(new TextDecoder).decode(t,{stream:!0});let n=s;if(h.req.onStreamChunk){const r={text:s,raw:t,index:e++,timestamp:Date.now()},o=await h.req.onStreamChunk(r);"string"==typeof o&&(n=o)}const o=new TextEncoder;r.enqueue(o.encode(n))}catch(e){r.enqueue(t)}}});p.body.pipeTo(r),x=new Response(t,{status:p.status,statusText:p.statusText,headers:p.headers})}else if(!y){const[e,t,r,s,n]=await Promise.allSettled([p.clone().json(),p.clone().text(),p.clone().arrayBuffer(),p.clone().blob(),p.clone().formData()]).then(e=>e.map(e=>"fulfilled"===e.status?e.value:null));h.resp={status:p.status,statusText:p.statusText,ok:p.ok,headers:p.headers,finalUrl:p.url,redirected:p.redirected,json:e,text:t,arrayBuffer:r,blob:s,formData:n};try{await h.req.response(h.resp)}catch(e){console.warn("[AjaxInterceptor] Error in fetch response callback:",e)}}x[s]=h;return new Proxy(x,{get(t,r){const s=e.getAttrHandler(t,r);return s||c(t,r)},set:(e,t,r)=>Reflect.set(e,t,r)})}return h({source:this.nativeFetch,target:t,prototype:this.nativeFetchPrototype}),t}}class p{xhrInterceptor;fetchInterceptor;static#e;static#t=Symbol("AjaxInterceptor");static getInstance(e={}){return p.#e||(p.#e=new p(p.#t,e)),p.#e}constructor(e,t={}){if(e!==p.#t)throw new Error("AjaxInterceptor is a singleton");this.xhrInterceptor=new i,this.fetchInterceptor=new l}toggleInject(e,s){switch(e){case t:this.xhrInterceptor[s]();break;case r:this.fetchInterceptor[s]();break;default:this.xhrInterceptor[s](),this.fetchInterceptor[s]()}}inject(e){if("undefined"==typeof window)throw new Error("AjaxInterceptor requires a browser environment");window.XMLHttpRequest||console.warn("XMLHttpRequest is not supported in this environment"),window.fetch||console.warn("Fetch API is not supported in this environment"),this.toggleInject(e,"inject")}uninject(e){this.toggleInject(e,"uninject")}hook(e,s){switch(s){case t:this.xhrInterceptor.hooks.push(e);break;case r:this.fetchInterceptor.hooks.push(e);break;default:this.xhrInterceptor.hooks.push(e),this.fetchInterceptor.hooks.push(e)}}unhook(e,s){const n=t=>{if(!e)return void(t.length=0);const r=t.indexOf(e);-1!==r&&t.splice(r,1)};switch(s){case t:n(this.xhrInterceptor.hooks);break;case r:n(this.fetchInterceptor.hooks);break;default:n(this.xhrInterceptor.hooks),n(this.fetchInterceptor.hooks)}}}return e.AjaxInterceptor=p,e.default=p,Object.defineProperty(e,"__esModule",{value:!0}),e}({});
@@ -3,7 +3,15 @@ import { XhrInterceptor } from './xhr';
3
3
  import { FetchInterceptor } from './fetch';
4
4
  declare class AjaxInterceptor {
5
5
  #private;
6
+ /**
7
+ * @deprecated Direct access to internal interceptor instances is kept only for 1.x compatibility.
8
+ * Use `hook` / `unhook` / `inject` / `uninject` instead. Planned to become private in 2.x.
9
+ */
6
10
  xhrInterceptor: XhrInterceptor;
11
+ /**
12
+ * @deprecated Direct access to internal interceptor instances is kept only for 1.x compatibility.
13
+ * Use `hook` / `unhook` / `inject` / `uninject` instead. Planned to become private in 2.x.
14
+ */
7
15
  fetchInterceptor: FetchInterceptor;
8
16
  static getInstance(options?: AjaxInterceptorCreateInstanceOptions): AjaxInterceptor;
9
17
  private constructor();
@@ -11,6 +19,7 @@ declare class AjaxInterceptor {
11
19
  inject(type?: AjaxType): void;
12
20
  uninject(type?: AjaxType): void;
13
21
  hook(fn: HookFunction, type?: AjaxType): void;
22
+ unhook(fn?: HookFunction, type?: AjaxType): void;
14
23
  }
15
24
  export default AjaxInterceptor;
16
25
  export { AjaxInterceptor };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "ajax-hooker",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A plugin for intercepting and modifying AJAX requests",
5
5
  "main": "dist/cjs/index.js",
6
+ "module": "dist/esm/index.js",
6
7
  "type": "module",
7
8
  "types": "dist/types/index.d.ts",
8
9
  "exports": {
@@ -15,6 +16,9 @@
15
16
  "files": [
16
17
  "dist"
17
18
  ],
19
+ "simple-git-hooks": {
20
+ "commit-msg": "npx --no-install commitlint --edit $1"
21
+ },
18
22
  "author": "arktomson",
19
23
  "license": "MIT",
20
24
  "keywords": [
@@ -29,8 +33,8 @@
29
33
  "url": "https://github.com/Arktomson/ajaxInterceptor/issues"
30
34
  },
31
35
  "devDependencies": {
32
- "@changesets/changelog-github": "^0.5.2",
33
- "@changesets/cli": "^2.29.8",
36
+ "@commitlint/cli": "^20.4.2",
37
+ "@commitlint/config-conventional": "^20.4.2",
34
38
  "@rollup/plugin-node-resolve": "^16.0.1",
35
39
  "@rollup/plugin-terser": "^0.4.4",
36
40
  "@rollup/plugin-typescript": "^11.1.6",
@@ -44,6 +48,7 @@
44
48
  "rollup-plugin-clear": "^2.0.7",
45
49
  "rollup-plugin-define": "^1.0.1",
46
50
  "rollup-plugin-serve": "^3.0.0",
51
+ "simple-git-hooks": "^2.13.1",
47
52
  "type-fest": "^5.4.4",
48
53
  "typescript": "^5.7.3",
49
54
  "vitest": "^4.0.15"
@@ -53,13 +58,13 @@
53
58
  },
54
59
  "scripts": {
55
60
  "dev": "rollup -c -w --environment NODE_ENV:development --configPlugin @rollup/plugin-typescript",
56
- "build": "rollup -c --environment NODE_ENV:production --configPlugin @rollup/plugin-typescript",
61
+ "build:js": "rollup -c --environment NODE_ENV:production --configPlugin @rollup/plugin-typescript",
62
+ "build:types": "tsc --target ESNext --module ESNext --moduleResolution Node --lib ESNext,DOM --esModuleInterop --allowSyntheticDefaultImports --skipLibCheck --declaration --emitDeclarationOnly --declarationDir dist/types --declarationMap false --sourceMap false --types node src/index.ts",
63
+ "build": "pnpm run build:js && pnpm run build:types",
57
64
  "test": "vitest",
58
65
  "test:ui": "vitest --ui",
59
66
  "test:run": "vitest run",
60
67
  "test:coverage": "vitest run --coverage",
61
- "changeset": "changeset",
62
- "version-packages": "changeset version",
63
- "release": "pnpm run build && changeset publish"
68
+ "release": "pnpm run build && node scripts/release.cjs patch && pnpm publish"
64
69
  }
65
70
  }
@@ -1,19 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class EventSourceInterceptor {
3
- readonly nativeEventSource: {
4
- new (url: string | URL, eventSourceInitDict?: EventSourceInit): EventSource;
5
- prototype: EventSource;
6
- readonly CONNECTING: 0;
7
- readonly OPEN: 1;
8
- readonly CLOSED: 2;
9
- };
10
- readonly nativeEventSourcePrototype: EventSource;
11
- hooks: HookFunction[];
12
- constructor();
13
- inject(): void;
14
- uninject(): void;
15
- private processStreamChunk;
16
- private wrapMessageListener;
17
- private createWrappedOnHandler;
18
- private _generateProxyEventSource;
19
- }
@@ -1,9 +0,0 @@
1
- import { AjaxInterceptorRequest, AjaxResponse, HookFunction } from './type';
2
- export declare class CycleScheduler {
3
- req: AjaxInterceptorRequest;
4
- resp: AjaxResponse;
5
- constructor({ req, }?: {
6
- req?: AjaxInterceptorRequest;
7
- });
8
- execute(request: AjaxInterceptorRequest, fnList: HookFunction[]): Promise<AjaxInterceptorRequest>;
9
- }
@@ -1,6 +0,0 @@
1
- export declare const AJAX_TYPE: {
2
- readonly XHR: "xhr";
3
- readonly FETCH: "fetch";
4
- };
5
- export type AjaxType = (typeof AJAX_TYPE)[keyof typeof AJAX_TYPE];
6
- export declare const CYCLE_SCHEDULER: unique symbol;
@@ -1,19 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class FetchInterceptor {
3
- readonly nativeFetch: ((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) & typeof fetch;
4
- readonly nativeFetchPrototype: any;
5
- hooks: HookFunction[];
6
- private fetchInstanceAttr;
7
- private fetchInstanceAttrHandler;
8
- private fetchMethods;
9
- private fetchMethodsHandler;
10
- constructor();
11
- inject(): void;
12
- uninject(): void;
13
- private getAttrHandler;
14
- private normalizeRequest;
15
- private resolveRequest;
16
- private resolveOptions;
17
- private resolveHeaders;
18
- private _generateProxyFetch;
19
- }
@@ -1,3 +0,0 @@
1
- export { default } from './interceptor';
2
- export * from './interceptor';
3
- export * from './type';
@@ -1,16 +0,0 @@
1
- import { HookFunction, AjaxType, AjaxInterceptorCreateInstanceOptions } from './type';
2
- import { XhrInterceptor } from './xhr';
3
- import { FetchInterceptor } from './fetch';
4
- declare class AjaxInterceptor {
5
- #private;
6
- xhrInterceptor: XhrInterceptor;
7
- fetchInterceptor: FetchInterceptor;
8
- static getInstance(options?: AjaxInterceptorCreateInstanceOptions): AjaxInterceptor;
9
- private constructor();
10
- private toggleInject;
11
- inject(type?: AjaxType): void;
12
- uninject(type?: AjaxType): void;
13
- hook(fn: HookFunction, type?: AjaxType): void;
14
- }
15
- export default AjaxInterceptor;
16
- export { AjaxInterceptor };
@@ -1,19 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class EventSourceInterceptor {
3
- readonly nativeEventSource: {
4
- new (url: string | URL, eventSourceInitDict?: EventSourceInit): EventSource;
5
- prototype: EventSource;
6
- readonly CONNECTING: 0;
7
- readonly OPEN: 1;
8
- readonly CLOSED: 2;
9
- };
10
- readonly nativeEventSourcePrototype: EventSource;
11
- hooks: HookFunction[];
12
- constructor();
13
- inject(): void;
14
- uninject(): void;
15
- private processStreamChunk;
16
- private wrapMessageListener;
17
- private createWrappedOnHandler;
18
- private _generateProxyEventSource;
19
- }
@@ -1,43 +0,0 @@
1
- import type { AjaxType } from './constant';
2
- import type { Writable } from 'type-fest';
3
- export type { AjaxType };
4
- export type ActionType = 'inject' | 'uninject';
5
- interface BaseResponse extends Writable<Pick<Response, 'status' | 'statusText'>>, Pick<Response, 'headers'> {
6
- finalUrl: string;
7
- }
8
- export interface XhrResponse extends Writable<Pick<XMLHttpRequest, 'response' | 'responseText' | 'responseXML'>> {
9
- }
10
- export interface FetchResponse extends Pick<Response, 'ok' | 'redirected'> {
11
- text: string;
12
- arrayBuffer: ArrayBuffer;
13
- blob: Blob;
14
- formData: FormData;
15
- json: any;
16
- }
17
- export interface AjaxResponse extends BaseResponse, Partial<XhrResponse & FetchResponse> {
18
- }
19
- export interface StreamChunk {
20
- text: string;
21
- raw: Uint8Array;
22
- index: number;
23
- timestamp: number;
24
- }
25
- export interface XhrRequest extends Partial<Pick<XMLHttpRequest, 'responseType' | 'withCredentials' | 'timeout'>> {
26
- }
27
- type XhrRequestBody = Document | XMLHttpRequestBodyInit | null;
28
- type FetchRequestBody = BodyInit | null;
29
- export interface AjaxInterceptorRequest extends XhrRequest {
30
- type: AjaxType;
31
- method: string;
32
- url: string;
33
- headers: Headers;
34
- data: XhrRequestBody | FetchRequestBody;
35
- response: (response: AjaxResponse) => void | Promise<void>;
36
- onStreamChunk?: (chunk: StreamChunk) => string | void | Promise<string | void>;
37
- }
38
- export interface HookFunction {
39
- (request: AjaxInterceptorRequest): Promise<AjaxInterceptorRequest | void> | AjaxInterceptorRequest | void;
40
- }
41
- export interface AjaxInterceptorCreateInstanceOptions {
42
- [key: string]: any;
43
- }
@@ -1,11 +0,0 @@
1
- export declare const getDescriptor: any;
2
- export declare const sleep: (ms: number) => Promise<unknown>;
3
- export declare const getType: any;
4
- export declare const resolveUrl: (url?: string | URL) => string;
5
- export declare const safeStringify: (value: any) => string;
6
- export declare const getProxyValue: (target: object, prop: string | symbol) => any;
7
- export declare const copyNativePropsAndPrototype: ({ source, target, prototype, }: {
8
- source: Record<string, any>;
9
- target: Record<string, any>;
10
- prototype: object;
11
- }) => void;
@@ -1,26 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class XhrInterceptor {
3
- readonly nativeXhr: {
4
- new (): XMLHttpRequest;
5
- prototype: XMLHttpRequest;
6
- readonly UNSENT: 0;
7
- readonly OPENED: 1;
8
- readonly HEADERS_RECEIVED: 2;
9
- readonly LOADING: 3;
10
- readonly DONE: 4;
11
- };
12
- readonly nativeXhrPrototype: XMLHttpRequest;
13
- hooks: HookFunction[];
14
- private xhrResponseEvents;
15
- private xhrInstanceAttr;
16
- private xhrInstanceAttrHandler;
17
- private xhrMethodsHandler;
18
- constructor();
19
- inject(): void;
20
- uninject(): void;
21
- private parseHeaders;
22
- private responseProcessor;
23
- private headersEqual;
24
- private getAttrHandler;
25
- private _generateProxyXMLHttpRequest;
26
- }
@@ -1,9 +0,0 @@
1
- import { AjaxInterceptorRequest, AjaxResponse, HookFunction } from './type';
2
- export declare class CycleScheduler {
3
- req: AjaxInterceptorRequest;
4
- resp: AjaxResponse;
5
- constructor({ req, }?: {
6
- req?: AjaxInterceptorRequest;
7
- });
8
- execute(request: AjaxInterceptorRequest, fnList: HookFunction[]): Promise<AjaxInterceptorRequest>;
9
- }
@@ -1,6 +0,0 @@
1
- export declare const AJAX_TYPE: {
2
- readonly XHR: "xhr";
3
- readonly FETCH: "fetch";
4
- };
5
- export type AjaxType = (typeof AJAX_TYPE)[keyof typeof AJAX_TYPE];
6
- export declare const CYCLE_SCHEDULER: unique symbol;
@@ -1,19 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class FetchInterceptor {
3
- readonly nativeFetch: ((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) & typeof fetch;
4
- readonly nativeFetchPrototype: any;
5
- hooks: HookFunction[];
6
- private fetchInstanceAttr;
7
- private fetchInstanceAttrHandler;
8
- private fetchMethods;
9
- private fetchMethodsHandler;
10
- constructor();
11
- inject(): void;
12
- uninject(): void;
13
- private getAttrHandler;
14
- private normalizeRequest;
15
- private resolveRequest;
16
- private resolveOptions;
17
- private resolveHeaders;
18
- private _generateProxyFetch;
19
- }
@@ -1,3 +0,0 @@
1
- export { default } from './interceptor';
2
- export * from './interceptor';
3
- export * from './type';
@@ -1,16 +0,0 @@
1
- import { HookFunction, AjaxType, AjaxInterceptorCreateInstanceOptions } from './type';
2
- import { XhrInterceptor } from './xhr';
3
- import { FetchInterceptor } from './fetch';
4
- declare class AjaxInterceptor {
5
- #private;
6
- xhrInterceptor: XhrInterceptor;
7
- fetchInterceptor: FetchInterceptor;
8
- static getInstance(options?: AjaxInterceptorCreateInstanceOptions): AjaxInterceptor;
9
- private constructor();
10
- private toggleInject;
11
- inject(type?: AjaxType): void;
12
- uninject(type?: AjaxType): void;
13
- hook(fn: HookFunction, type?: AjaxType): void;
14
- }
15
- export default AjaxInterceptor;
16
- export { AjaxInterceptor };
@@ -1,19 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class EventSourceInterceptor {
3
- readonly nativeEventSource: {
4
- new (url: string | URL, eventSourceInitDict?: EventSourceInit): EventSource;
5
- prototype: EventSource;
6
- readonly CONNECTING: 0;
7
- readonly OPEN: 1;
8
- readonly CLOSED: 2;
9
- };
10
- readonly nativeEventSourcePrototype: EventSource;
11
- hooks: HookFunction[];
12
- constructor();
13
- inject(): void;
14
- uninject(): void;
15
- private processStreamChunk;
16
- private wrapMessageListener;
17
- private createWrappedOnHandler;
18
- private _generateProxyEventSource;
19
- }
@@ -1,43 +0,0 @@
1
- import type { AjaxType } from './constant';
2
- import type { Writable } from 'type-fest';
3
- export type { AjaxType };
4
- export type ActionType = 'inject' | 'uninject';
5
- interface BaseResponse extends Writable<Pick<Response, 'status' | 'statusText'>>, Pick<Response, 'headers'> {
6
- finalUrl: string;
7
- }
8
- export interface XhrResponse extends Writable<Pick<XMLHttpRequest, 'response' | 'responseText' | 'responseXML'>> {
9
- }
10
- export interface FetchResponse extends Pick<Response, 'ok' | 'redirected'> {
11
- text: string;
12
- arrayBuffer: ArrayBuffer;
13
- blob: Blob;
14
- formData: FormData;
15
- json: any;
16
- }
17
- export interface AjaxResponse extends BaseResponse, Partial<XhrResponse & FetchResponse> {
18
- }
19
- export interface StreamChunk {
20
- text: string;
21
- raw: Uint8Array;
22
- index: number;
23
- timestamp: number;
24
- }
25
- export interface XhrRequest extends Partial<Pick<XMLHttpRequest, 'responseType' | 'withCredentials' | 'timeout'>> {
26
- }
27
- type XhrRequestBody = Document | XMLHttpRequestBodyInit | null;
28
- type FetchRequestBody = BodyInit | null;
29
- export interface AjaxInterceptorRequest extends XhrRequest {
30
- type: AjaxType;
31
- method: string;
32
- url: string;
33
- headers: Headers;
34
- data: XhrRequestBody | FetchRequestBody;
35
- response: (response: AjaxResponse) => void | Promise<void>;
36
- onStreamChunk?: (chunk: StreamChunk) => string | void | Promise<string | void>;
37
- }
38
- export interface HookFunction {
39
- (request: AjaxInterceptorRequest): Promise<AjaxInterceptorRequest | void> | AjaxInterceptorRequest | void;
40
- }
41
- export interface AjaxInterceptorCreateInstanceOptions {
42
- [key: string]: any;
43
- }
@@ -1,11 +0,0 @@
1
- export declare const getDescriptor: any;
2
- export declare const sleep: (ms: number) => Promise<unknown>;
3
- export declare const getType: any;
4
- export declare const resolveUrl: (url?: string | URL) => string;
5
- export declare const safeStringify: (value: any) => string;
6
- export declare const getProxyValue: (target: object, prop: string | symbol) => any;
7
- export declare const copyNativePropsAndPrototype: ({ source, target, prototype, }: {
8
- source: Record<string, any>;
9
- target: Record<string, any>;
10
- prototype: object;
11
- }) => void;
@@ -1,26 +0,0 @@
1
- import { HookFunction } from './type';
2
- export declare class XhrInterceptor {
3
- readonly nativeXhr: {
4
- new (): XMLHttpRequest;
5
- prototype: XMLHttpRequest;
6
- readonly UNSENT: 0;
7
- readonly OPENED: 1;
8
- readonly HEADERS_RECEIVED: 2;
9
- readonly LOADING: 3;
10
- readonly DONE: 4;
11
- };
12
- readonly nativeXhrPrototype: XMLHttpRequest;
13
- hooks: HookFunction[];
14
- private xhrResponseEvents;
15
- private xhrInstanceAttr;
16
- private xhrInstanceAttrHandler;
17
- private xhrMethodsHandler;
18
- constructor();
19
- inject(): void;
20
- uninject(): void;
21
- private parseHeaders;
22
- private responseProcessor;
23
- private headersEqual;
24
- private getAttrHandler;
25
- private _generateProxyXMLHttpRequest;
26
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes