@fluwa-tool/sdk 1.0.0 → 1.0.26

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.
@@ -21,6 +21,7 @@ export declare class FetchInterceptor implements IFetchInterceptor {
21
21
  private appName;
22
22
  private originalFetch;
23
23
  private isInstalled;
24
+ private isIntercepting;
24
25
  constructor(logger: ILogger, requestLogger: IRequestLogger, mockResolver: IMockResolver, sessionId: string, appName: string);
25
26
  /**
26
27
  * Instalar o interceptador
@@ -48,4 +49,9 @@ export declare class FetchInterceptor implements IFetchInterceptor {
48
49
  * Sleep (para delays de mock)
49
50
  */
50
51
  private delay;
52
+ /**
53
+ * Verificar se requisição é interna do Fluwa
54
+ * Suporta headers como plain object ou Headers instance
55
+ */
56
+ private isFluwaInternalRequest;
51
57
  }
@@ -19,6 +19,7 @@ class FetchInterceptor {
19
19
  this.appName = appName;
20
20
  this.originalFetch = null;
21
21
  this.isInstalled = false;
22
+ this.isIntercepting = false; // Flag para prevenir re-intercepção
22
23
  }
23
24
  /**
24
25
  * Instalar o interceptador
@@ -64,13 +65,14 @@ class FetchInterceptor {
64
65
  return async (...args) => {
65
66
  const startTime = performance.now();
66
67
  const [resource, init] = args;
68
+ // Evitar re-intercepção: se já estamos interceptando, usar fetch original
69
+ if (this.isIntercepting) {
70
+ return this.originalFetch.apply(globalThis, args);
71
+ }
67
72
  // Ignorar requisições internas do Fluwa (header X-Fluwa-Internal)
68
- if (init?.headers && typeof init.headers === 'object') {
69
- const headers = init.headers;
70
- if (headers['X-Fluwa-Internal'] === 'true') {
71
- // Usar fetch original para requisições internas
72
- return this.originalFetch.apply(globalThis, args);
73
- }
73
+ if (this.isFluwaInternalRequest(init)) {
74
+ // Usar fetch original para requisições internas
75
+ return this.originalFetch.apply(globalThis, args);
74
76
  }
75
77
  // Extrair informações da requisição
76
78
  const method = (init?.method || 'GET').toUpperCase();
@@ -87,6 +89,8 @@ class FetchInterceptor {
87
89
  appName: this.appName,
88
90
  source: types_1.RequestSource.REAL, // Default, pode mudar se usar mock
89
91
  };
92
+ // Marcar que estamos interceptando para evitar re-intercepção
93
+ this.isIntercepting = true;
90
94
  try {
91
95
  // Registrar que começou
92
96
  await this.requestLogger.logStart(requestMetadata);
@@ -140,6 +144,10 @@ class FetchInterceptor {
140
144
  await this.requestLogger.logComplete(requestMetadata);
141
145
  throw error;
142
146
  }
147
+ finally {
148
+ // Sempre restaurar a flag
149
+ this.isIntercepting = false;
150
+ }
143
151
  };
144
152
  }
145
153
  /**
@@ -168,5 +176,24 @@ class FetchInterceptor {
168
176
  delay(ms) {
169
177
  return new Promise((resolve) => setTimeout(resolve, ms));
170
178
  }
179
+ /**
180
+ * Verificar se requisição é interna do Fluwa
181
+ * Suporta headers como plain object ou Headers instance
182
+ */
183
+ isFluwaInternalRequest(init) {
184
+ if (!init || !init.headers) {
185
+ return false;
186
+ }
187
+ const headers = init.headers;
188
+ // Verificar se é plain object
189
+ if (typeof headers === 'object' && headers.constructor === Object) {
190
+ return headers['X-Fluwa-Internal'] === 'true';
191
+ }
192
+ // Verificar se é Headers instance
193
+ if (headers instanceof Headers || (typeof headers?.get === 'function')) {
194
+ return headers.get('X-Fluwa-Internal') === 'true';
195
+ }
196
+ return false;
197
+ }
171
198
  }
172
199
  exports.FetchInterceptor = FetchInterceptor;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Interceptador de XMLHttpRequest
3
+ * Intercepta requisições axios, jQuery, XMLHttpRequest nativo
4
+ */
5
+ import { ILogger } from '../../core/Logger';
6
+ import { IRequestLogger } from './RequestLogger';
7
+ import { IMockResolver } from './MockResolver';
8
+ export interface IXMLHttpRequestInterceptor {
9
+ install(): void;
10
+ uninstall(): void;
11
+ }
12
+ export declare class XMLHttpRequestInterceptor implements IXMLHttpRequestInterceptor {
13
+ private logger;
14
+ private requestLogger;
15
+ private mockResolver;
16
+ private sessionId;
17
+ private appName;
18
+ private originalOpen;
19
+ private originalSend;
20
+ private originalSetRequestHeader;
21
+ private isInstalled;
22
+ constructor(logger: ILogger, requestLogger: IRequestLogger, mockResolver: IMockResolver, sessionId: string, appName: string);
23
+ install(): void;
24
+ uninstall(): void;
25
+ private safeParseBody;
26
+ private generateId;
27
+ }
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ /**
3
+ * Interceptador de XMLHttpRequest
4
+ * Intercepta requisições axios, jQuery, XMLHttpRequest nativo
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.XMLHttpRequestInterceptor = void 0;
8
+ const types_1 = require("../../types");
9
+ class XMLHttpRequestInterceptor {
10
+ constructor(logger, requestLogger, mockResolver, sessionId, appName) {
11
+ this.logger = logger;
12
+ this.requestLogger = requestLogger;
13
+ this.mockResolver = mockResolver;
14
+ this.sessionId = sessionId;
15
+ this.appName = appName;
16
+ this.isInstalled = false;
17
+ }
18
+ install() {
19
+ if (this.isInstalled) {
20
+ this.logger.warn('XMLHttpRequest interceptor já foi instalado');
21
+ return;
22
+ }
23
+ // Verificar se XMLHttpRequest está disponível (apenas em navegadores)
24
+ if (typeof XMLHttpRequest === 'undefined') {
25
+ this.logger.debug('XMLHttpRequest não disponível nesse ambiente (requer navegador)');
26
+ return;
27
+ }
28
+ const self = this;
29
+ const originalXHR = XMLHttpRequest.prototype;
30
+ // Armazenar referência ao open original
31
+ this.originalOpen = originalXHR.open;
32
+ this.originalSend = originalXHR.send;
33
+ this.originalSetRequestHeader = originalXHR.setRequestHeader;
34
+ // Interceptar open()
35
+ originalXHR.open = function (method, url, ...args) {
36
+ const xhr = this;
37
+ xhr._fluwaMethod = method.toUpperCase();
38
+ xhr._fluwaUrl = url;
39
+ xhr._fluwaRequestId = self.generateId();
40
+ xhr._fluwaHeaders = {};
41
+ xhr._fluwaStartTime = Date.now();
42
+ return self.originalOpen.apply(this, [method, url, ...args]);
43
+ };
44
+ // Interceptar setRequestHeader()
45
+ originalXHR.setRequestHeader = function (header, value) {
46
+ const xhr = this;
47
+ if (!xhr._fluwaHeaders) {
48
+ xhr._fluwaHeaders = {};
49
+ }
50
+ xhr._fluwaHeaders[header] = value;
51
+ return self.originalSetRequestHeader.apply(this, [header, value]);
52
+ };
53
+ // Interceptar send()
54
+ this.originalSend = originalXHR.send;
55
+ originalXHR.send = function (body) {
56
+ const xhr = this;
57
+ const method = (xhr._fluwaMethod || 'GET');
58
+ const url = xhr._fluwaUrl;
59
+ const requestId = xhr._fluwaRequestId;
60
+ const headers = xhr._fluwaHeaders || {};
61
+ const requestMetadata = {
62
+ id: requestId,
63
+ method,
64
+ url,
65
+ headers,
66
+ body: body ? self.safeParseBody(body) : undefined,
67
+ timestamp: new Date().toISOString(),
68
+ sessionId: self.sessionId,
69
+ appName: self.appName,
70
+ source: types_1.RequestSource.REAL,
71
+ };
72
+ // Verificar se há mock
73
+ self.mockResolver.resolve(method, url)
74
+ .then((mockResponse) => {
75
+ if (mockResponse) {
76
+ // Há mock disponível
77
+ requestMetadata.source = types_1.RequestSource.MOCK;
78
+ requestMetadata.status = mockResponse.status || 200;
79
+ requestMetadata.response = mockResponse.response;
80
+ // Logar
81
+ self.requestLogger.logStart(requestMetadata).catch(() => { });
82
+ // Aplicar delay se configurado
83
+ if (mockResponse.delay) {
84
+ return new Promise(resolve => setTimeout(resolve, mockResponse.delay));
85
+ }
86
+ }
87
+ else {
88
+ // Sem mock, será requisição real
89
+ self.requestLogger.logStart(requestMetadata).catch(() => { });
90
+ }
91
+ })
92
+ .catch(() => {
93
+ self.requestLogger.logStart(requestMetadata).catch(() => { });
94
+ });
95
+ // Interceptar mudanças de estado
96
+ const originalOnReadyStateChange = xhr.onreadystatechange;
97
+ xhr.onreadystatechange = function () {
98
+ if (xhr.readyState === 4) {
99
+ // Requisição completou
100
+ requestMetadata.status = xhr.status;
101
+ // Tentar extrair response
102
+ try {
103
+ if (xhr.responseText) {
104
+ requestMetadata.response = self.safeParseBody(xhr.responseText);
105
+ }
106
+ }
107
+ catch (e) {
108
+ requestMetadata.response = xhr.responseText;
109
+ }
110
+ const duration = Date.now() - (xhr._fluwaStartTime || 0);
111
+ requestMetadata.duration = duration;
112
+ self.requestLogger.logComplete(requestMetadata).catch(() => { });
113
+ }
114
+ // Chamar handler original
115
+ if (originalOnReadyStateChange) {
116
+ originalOnReadyStateChange.call(this);
117
+ }
118
+ };
119
+ // Chamar send original
120
+ return self.originalSend.apply(this, [body]);
121
+ };
122
+ this.isInstalled = true;
123
+ this.logger.success('XMLHttpRequest interceptor instalado');
124
+ }
125
+ uninstall() {
126
+ if (!this.isInstalled || !this.originalOpen) {
127
+ return;
128
+ }
129
+ const originalXHR = XMLHttpRequest.prototype;
130
+ originalXHR.open = this.originalOpen;
131
+ originalXHR.send = this.originalSend;
132
+ originalXHR.setRequestHeader = this.originalSetRequestHeader;
133
+ this.isInstalled = false;
134
+ this.logger.success('XMLHttpRequest interceptor desinstalado');
135
+ }
136
+ safeParseBody(body) {
137
+ if (typeof body === 'string') {
138
+ try {
139
+ return JSON.parse(body);
140
+ }
141
+ catch {
142
+ return body;
143
+ }
144
+ }
145
+ return body;
146
+ }
147
+ generateId() {
148
+ return `${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
149
+ }
150
+ }
151
+ exports.XMLHttpRequestInterceptor = XMLHttpRequestInterceptor;
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ const Config_1 = require("./core/Config");
33
33
  const HttpClient_1 = require("./features/communication/HttpClient");
34
34
  const WebSocketClient_1 = require("./features/communication/WebSocketClient");
35
35
  const FetchInterceptor_1 = require("./features/network/FetchInterceptor");
36
+ const XMLHttpRequestInterceptor_1 = require("./features/network/XMLHttpRequestInterceptor");
36
37
  const RequestLogger_1 = require("./features/network/RequestLogger");
37
38
  const MockResolver_1 = require("./features/network/MockResolver");
38
39
  const IdGenerator_1 = require("./core/IdGenerator");
@@ -46,6 +47,7 @@ let sessionId;
46
47
  let httpClient;
47
48
  let wsClient;
48
49
  let fetchInterceptor;
50
+ let xmlHttpRequestInterceptor;
49
51
  let mockResolver;
50
52
  // ============================================
51
53
  // INICIALIZAÇÃO
@@ -82,8 +84,11 @@ async function initFluwaTool(partialConfig = {}) {
82
84
  const requestLogger = new RequestLogger_1.RequestLogger(httpClient, logger);
83
85
  // 6. Criar interceptador de fetch
84
86
  fetchInterceptor = new FetchInterceptor_1.FetchInterceptor(logger, requestLogger, mockResolver, sessionId, config.getAppName());
87
+ // 6b. Criar interceptador de XMLHttpRequest (para axios, jQuery, etc)
88
+ xmlHttpRequestInterceptor = new XMLHttpRequestInterceptor_1.XMLHttpRequestInterceptor(logger, requestLogger, mockResolver, sessionId, config.getAppName());
85
89
  // 7. Instalar interceptadores
86
90
  fetchInterceptor.install();
91
+ xmlHttpRequestInterceptor.install();
87
92
  // 8. Conectar WebSocket
88
93
  logger.debug('Conectando ao WebSocket...');
89
94
  try {
@@ -139,6 +144,7 @@ function disableFluwaTool() {
139
144
  return;
140
145
  }
141
146
  fetchInterceptor.uninstall();
147
+ xmlHttpRequestInterceptor.uninstall();
142
148
  wsClient.disconnect();
143
149
  isInitialized = false;
144
150
  logger.success('Fluwa-Tool desativado');
@@ -151,6 +157,7 @@ async function enableFluwaTool() {
151
157
  return;
152
158
  }
153
159
  fetchInterceptor.install();
160
+ xmlHttpRequestInterceptor.install();
154
161
  try {
155
162
  await wsClient.connect();
156
163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluwa-tool/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.26",
4
4
  "description": "Fluwa DevTools SDK for network interception and mocking",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",