@fluwa-tool/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/Config.d.ts +28 -0
- package/dist/core/Config.js +74 -0
- package/dist/core/IdGenerator.d.ts +19 -0
- package/dist/core/IdGenerator.js +25 -0
- package/dist/core/Logger.d.ts +38 -0
- package/dist/core/Logger.js +76 -0
- package/dist/features/communication/HttpClient.d.ts +41 -0
- package/dist/features/communication/HttpClient.js +88 -0
- package/dist/features/communication/WebSocketClient.d.ts +61 -0
- package/dist/features/communication/WebSocketClient.js +158 -0
- package/dist/features/network/FetchInterceptor.d.ts +51 -0
- package/dist/features/network/FetchInterceptor.js +172 -0
- package/dist/features/network/MockResolver.d.ts +49 -0
- package/dist/features/network/MockResolver.js +129 -0
- package/dist/features/network/RequestLogger.d.ts +28 -0
- package/dist/features/network/RequestLogger.js +42 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +177 -0
- package/dist/types/index.d.ts +107 -0
- package/dist/types/index.js +39 -0
- package/package.json +39 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gerenciamento centralizado de configuração
|
|
3
|
+
* Responsabilidade única: manter e validar configurações
|
|
4
|
+
*/
|
|
5
|
+
import { FluwaConfig } from '../types';
|
|
6
|
+
export interface IConfigManager {
|
|
7
|
+
getConfig(): FluwaConfig;
|
|
8
|
+
updateConfig(partial: Partial<FluwaConfig>): void;
|
|
9
|
+
isEnabled(): boolean;
|
|
10
|
+
getServerUrl(): string;
|
|
11
|
+
getAppName(): string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Gerenciador de configuração
|
|
15
|
+
* Implementa pattern: Configuration Object
|
|
16
|
+
*/
|
|
17
|
+
export declare class ConfigManager implements IConfigManager {
|
|
18
|
+
private config;
|
|
19
|
+
constructor(initialConfig?: Partial<FluwaConfig>);
|
|
20
|
+
private validate;
|
|
21
|
+
getConfig(): FluwaConfig;
|
|
22
|
+
updateConfig(partial: Partial<FluwaConfig>): void;
|
|
23
|
+
isEnabled(): boolean;
|
|
24
|
+
getServerUrl(): string;
|
|
25
|
+
getAppName(): string;
|
|
26
|
+
}
|
|
27
|
+
export declare function getConfigManager(initialConfig?: Partial<FluwaConfig>): ConfigManager;
|
|
28
|
+
export declare function resetConfig(): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Gerenciamento centralizado de configuração
|
|
4
|
+
* Responsabilidade única: manter e validar configurações
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ConfigManager = void 0;
|
|
8
|
+
exports.getConfigManager = getConfigManager;
|
|
9
|
+
exports.resetConfig = resetConfig;
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
serverUrl: 'http://localhost:5555',
|
|
12
|
+
enabled: true,
|
|
13
|
+
appName: 'DefaultApp',
|
|
14
|
+
debug: false,
|
|
15
|
+
requestTimeout: 5000,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Gerenciador de configuração
|
|
19
|
+
* Implementa pattern: Configuration Object
|
|
20
|
+
*/
|
|
21
|
+
class ConfigManager {
|
|
22
|
+
constructor(initialConfig) {
|
|
23
|
+
this.config = {
|
|
24
|
+
...DEFAULT_CONFIG,
|
|
25
|
+
...initialConfig,
|
|
26
|
+
};
|
|
27
|
+
this.validate();
|
|
28
|
+
}
|
|
29
|
+
validate() {
|
|
30
|
+
if (!this.config.serverUrl || typeof this.config.serverUrl !== 'string') {
|
|
31
|
+
throw new Error('serverUrl inválida');
|
|
32
|
+
}
|
|
33
|
+
if (typeof this.config.enabled !== 'boolean') {
|
|
34
|
+
throw new Error('enabled deve ser boolean');
|
|
35
|
+
}
|
|
36
|
+
if (!this.config.appName || typeof this.config.appName !== 'string') {
|
|
37
|
+
throw new Error('appName inválida');
|
|
38
|
+
}
|
|
39
|
+
if (this.config.requestTimeout && this.config.requestTimeout < 0) {
|
|
40
|
+
throw new Error('requestTimeout não pode ser negativa');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
getConfig() {
|
|
44
|
+
return { ...this.config };
|
|
45
|
+
}
|
|
46
|
+
updateConfig(partial) {
|
|
47
|
+
this.config = {
|
|
48
|
+
...this.config,
|
|
49
|
+
...partial,
|
|
50
|
+
};
|
|
51
|
+
this.validate();
|
|
52
|
+
}
|
|
53
|
+
isEnabled() {
|
|
54
|
+
return this.config.enabled;
|
|
55
|
+
}
|
|
56
|
+
getServerUrl() {
|
|
57
|
+
return this.config.serverUrl;
|
|
58
|
+
}
|
|
59
|
+
getAppName() {
|
|
60
|
+
return this.config.appName;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.ConfigManager = ConfigManager;
|
|
64
|
+
// Instância singleton (lazy-loaded)
|
|
65
|
+
let configInstance = null;
|
|
66
|
+
function getConfigManager(initialConfig) {
|
|
67
|
+
if (!configInstance) {
|
|
68
|
+
configInstance = new ConfigManager(initialConfig);
|
|
69
|
+
}
|
|
70
|
+
return configInstance;
|
|
71
|
+
}
|
|
72
|
+
function resetConfig() {
|
|
73
|
+
configInstance = null;
|
|
74
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gerador de IDs únicos
|
|
3
|
+
* Responsabilidade única: gerar identificadores
|
|
4
|
+
*/
|
|
5
|
+
export interface IIdGenerator {
|
|
6
|
+
generate(): string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Implementação de gerador de IDs
|
|
10
|
+
* Usa combinação de timestamp + random para garantir unicidade
|
|
11
|
+
*/
|
|
12
|
+
export declare class IdGenerator implements IIdGenerator {
|
|
13
|
+
/**
|
|
14
|
+
* Gera ID único no formato: timestamp_random
|
|
15
|
+
* Exemplo: 1710000000000_abc123def456
|
|
16
|
+
*/
|
|
17
|
+
generate(): string;
|
|
18
|
+
}
|
|
19
|
+
export declare const idGenerator: IdGenerator;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Gerador de IDs únicos
|
|
4
|
+
* Responsabilidade única: gerar identificadores
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.idGenerator = exports.IdGenerator = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Implementação de gerador de IDs
|
|
10
|
+
* Usa combinação de timestamp + random para garantir unicidade
|
|
11
|
+
*/
|
|
12
|
+
class IdGenerator {
|
|
13
|
+
/**
|
|
14
|
+
* Gera ID único no formato: timestamp_random
|
|
15
|
+
* Exemplo: 1710000000000_abc123def456
|
|
16
|
+
*/
|
|
17
|
+
generate() {
|
|
18
|
+
const timestamp = Date.now();
|
|
19
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
20
|
+
return `${timestamp}_${random}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.IdGenerator = IdGenerator;
|
|
24
|
+
// Instância singleton
|
|
25
|
+
exports.idGenerator = new IdGenerator();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger centralizado com níveis de severidade
|
|
3
|
+
* Responsabilidade única: logging estruturado
|
|
4
|
+
*/
|
|
5
|
+
export declare enum LogLevel {
|
|
6
|
+
DEBUG = "DEBUG",
|
|
7
|
+
INFO = "INFO",
|
|
8
|
+
WARN = "WARN",
|
|
9
|
+
ERROR = "ERROR",
|
|
10
|
+
SUCCESS = "SUCCESS"
|
|
11
|
+
}
|
|
12
|
+
export interface ILogger {
|
|
13
|
+
debug(message: string, data?: unknown): void;
|
|
14
|
+
info(message: string, data?: unknown): void;
|
|
15
|
+
warn(message: string, data?: unknown): void;
|
|
16
|
+
error(message: string, error?: Error | unknown): void;
|
|
17
|
+
success(message: string): void;
|
|
18
|
+
setLevel(level: LogLevel): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Implementação do logger
|
|
22
|
+
* Permite ativar/desativar diferentes níveis
|
|
23
|
+
*/
|
|
24
|
+
export declare class Logger implements ILogger {
|
|
25
|
+
private namespace;
|
|
26
|
+
private minLevel;
|
|
27
|
+
private readonly levelOrder;
|
|
28
|
+
constructor(namespace?: string, minLevel?: LogLevel);
|
|
29
|
+
private shouldLog;
|
|
30
|
+
private formatMessage;
|
|
31
|
+
debug(message: string, data?: unknown): void;
|
|
32
|
+
info(message: string, data?: unknown): void;
|
|
33
|
+
warn(message: string, data?: unknown): void;
|
|
34
|
+
error(message: string, error?: Error | unknown): void;
|
|
35
|
+
success(message: string): void;
|
|
36
|
+
setLevel(level: LogLevel): void;
|
|
37
|
+
}
|
|
38
|
+
export declare const defaultLogger: Logger;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logger centralizado com níveis de severidade
|
|
4
|
+
* Responsabilidade única: logging estruturado
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.defaultLogger = exports.Logger = exports.LogLevel = void 0;
|
|
8
|
+
var LogLevel;
|
|
9
|
+
(function (LogLevel) {
|
|
10
|
+
LogLevel["DEBUG"] = "DEBUG";
|
|
11
|
+
LogLevel["INFO"] = "INFO";
|
|
12
|
+
LogLevel["WARN"] = "WARN";
|
|
13
|
+
LogLevel["ERROR"] = "ERROR";
|
|
14
|
+
LogLevel["SUCCESS"] = "SUCCESS";
|
|
15
|
+
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
16
|
+
/**
|
|
17
|
+
* Implementação do logger
|
|
18
|
+
* Permite ativar/desativar diferentes níveis
|
|
19
|
+
*/
|
|
20
|
+
class Logger {
|
|
21
|
+
constructor(namespace = 'Fluwa', minLevel = LogLevel.INFO) {
|
|
22
|
+
this.levelOrder = {
|
|
23
|
+
[LogLevel.DEBUG]: 0,
|
|
24
|
+
[LogLevel.INFO]: 1,
|
|
25
|
+
[LogLevel.WARN]: 2,
|
|
26
|
+
[LogLevel.ERROR]: 3,
|
|
27
|
+
[LogLevel.SUCCESS]: 1,
|
|
28
|
+
};
|
|
29
|
+
this.namespace = namespace;
|
|
30
|
+
this.minLevel = minLevel;
|
|
31
|
+
}
|
|
32
|
+
shouldLog(level) {
|
|
33
|
+
return this.levelOrder[level] >= this.levelOrder[this.minLevel];
|
|
34
|
+
}
|
|
35
|
+
formatMessage(level, message) {
|
|
36
|
+
const timestamp = new Date().toISOString();
|
|
37
|
+
return `[${timestamp}] [${this.namespace}:${level}] ${message}`;
|
|
38
|
+
}
|
|
39
|
+
debug(message, data) {
|
|
40
|
+
if (!this.shouldLog(LogLevel.DEBUG))
|
|
41
|
+
return;
|
|
42
|
+
console.log(this.formatMessage(LogLevel.DEBUG, message), data || '');
|
|
43
|
+
}
|
|
44
|
+
info(message, data) {
|
|
45
|
+
if (!this.shouldLog(LogLevel.INFO))
|
|
46
|
+
return;
|
|
47
|
+
console.log(this.formatMessage(LogLevel.INFO, message), data || '');
|
|
48
|
+
}
|
|
49
|
+
warn(message, data) {
|
|
50
|
+
if (!this.shouldLog(LogLevel.WARN))
|
|
51
|
+
return;
|
|
52
|
+
console.warn(this.formatMessage(LogLevel.WARN, message), data || '');
|
|
53
|
+
}
|
|
54
|
+
error(message, error) {
|
|
55
|
+
if (!this.shouldLog(LogLevel.ERROR))
|
|
56
|
+
return;
|
|
57
|
+
console.error(this.formatMessage(LogLevel.ERROR, message));
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
console.error(`Stack: ${error.stack}`);
|
|
60
|
+
}
|
|
61
|
+
else if (error) {
|
|
62
|
+
console.error('Details:', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
success(message) {
|
|
66
|
+
if (!this.shouldLog(LogLevel.SUCCESS))
|
|
67
|
+
return;
|
|
68
|
+
console.log(`✓ ${this.formatMessage(LogLevel.SUCCESS, message)}`);
|
|
69
|
+
}
|
|
70
|
+
setLevel(level) {
|
|
71
|
+
this.minLevel = level;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.Logger = Logger;
|
|
75
|
+
// Instância singleton do logger padrão
|
|
76
|
+
exports.defaultLogger = new Logger('Fluwa', LogLevel.INFO);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cliente HTTP para comunicação com Fluwa Server
|
|
3
|
+
* Responsabilidade única: fazer requisições HTTP
|
|
4
|
+
*/
|
|
5
|
+
import { ILogger } from '../../core/Logger';
|
|
6
|
+
export interface IHttpClient {
|
|
7
|
+
post<T>(endpoint: string, data: unknown): Promise<T>;
|
|
8
|
+
get<T>(endpoint: string, params?: Record<string, string>): Promise<T>;
|
|
9
|
+
put<T>(endpoint: string, data: unknown): Promise<T>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Implementação do cliente HTTP
|
|
13
|
+
* Usa fetch nativo do navegador/Node.js
|
|
14
|
+
*/
|
|
15
|
+
export declare class HttpClient implements IHttpClient {
|
|
16
|
+
private baseUrl;
|
|
17
|
+
private logger;
|
|
18
|
+
private timeout;
|
|
19
|
+
constructor(baseUrl: string, logger: ILogger, timeout?: number);
|
|
20
|
+
/**
|
|
21
|
+
* Fazer POST request
|
|
22
|
+
* Implementa timeout automático
|
|
23
|
+
*/
|
|
24
|
+
post<T>(endpoint: string, data: unknown): Promise<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Fazer GET request com query parameters
|
|
27
|
+
*/
|
|
28
|
+
get<T>(endpoint: string, params?: Record<string, string>): Promise<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Fazer PUT request
|
|
31
|
+
*/
|
|
32
|
+
put<T>(endpoint: string, data: unknown): Promise<T>;
|
|
33
|
+
/**
|
|
34
|
+
* Request genérica com timeout
|
|
35
|
+
*/
|
|
36
|
+
private request;
|
|
37
|
+
/**
|
|
38
|
+
* Construir URL com query parameters
|
|
39
|
+
*/
|
|
40
|
+
private buildUrl;
|
|
41
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cliente HTTP para comunicação com Fluwa Server
|
|
4
|
+
* Responsabilidade única: fazer requisições HTTP
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.HttpClient = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Implementação do cliente HTTP
|
|
10
|
+
* Usa fetch nativo do navegador/Node.js
|
|
11
|
+
*/
|
|
12
|
+
class HttpClient {
|
|
13
|
+
constructor(baseUrl, logger, timeout = 5000) {
|
|
14
|
+
this.baseUrl = baseUrl;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
this.timeout = timeout;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fazer POST request
|
|
20
|
+
* Implementa timeout automático
|
|
21
|
+
*/
|
|
22
|
+
async post(endpoint, data) {
|
|
23
|
+
return this.request('POST', endpoint, { body: JSON.stringify(data) });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fazer GET request com query parameters
|
|
27
|
+
*/
|
|
28
|
+
async get(endpoint, params) {
|
|
29
|
+
const url = this.buildUrl(endpoint, params);
|
|
30
|
+
return this.request('GET', url);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fazer PUT request
|
|
34
|
+
*/
|
|
35
|
+
async put(endpoint, data) {
|
|
36
|
+
return this.request('PUT', endpoint, { body: JSON.stringify(data) });
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Request genérica com timeout
|
|
40
|
+
*/
|
|
41
|
+
async request(method, endpoint, options = {}) {
|
|
42
|
+
const url = endpoint.startsWith('http')
|
|
43
|
+
? endpoint
|
|
44
|
+
: `${this.baseUrl}${endpoint}`;
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(url, {
|
|
49
|
+
method,
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
'X-Fluwa-Internal': 'true', // Marcar como requisição interna do Fluwa
|
|
53
|
+
...options.headers,
|
|
54
|
+
},
|
|
55
|
+
...options,
|
|
56
|
+
signal: controller.signal,
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
60
|
+
}
|
|
61
|
+
return (await response.json());
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const isTimeout = error instanceof Error && error.name === 'AbortError';
|
|
65
|
+
if (isTimeout) {
|
|
66
|
+
this.logger.warn(`Request timeout: ${endpoint}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.logger.error(`HTTP request failed: ${endpoint}`, error);
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Construir URL com query parameters
|
|
79
|
+
*/
|
|
80
|
+
buildUrl(endpoint, params) {
|
|
81
|
+
if (!params || Object.keys(params).length === 0) {
|
|
82
|
+
return endpoint;
|
|
83
|
+
}
|
|
84
|
+
const queryString = new URLSearchParams(params).toString();
|
|
85
|
+
return `${endpoint}?${queryString}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.HttpClient = HttpClient;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cliente WebSocket para comunicação bidirecional em tempo real
|
|
3
|
+
* Responsabilidade única: gerenciar conexão WebSocket
|
|
4
|
+
*/
|
|
5
|
+
import { ILogger } from '../../core/Logger';
|
|
6
|
+
import { WebSocketMessage } from '../../types';
|
|
7
|
+
export declare enum WebSocketState {
|
|
8
|
+
CONNECTING = "CONNECTING",
|
|
9
|
+
CONNECTED = "CONNECTED",
|
|
10
|
+
DISCONNECTED = "DISCONNECTED",
|
|
11
|
+
ERROR = "ERROR"
|
|
12
|
+
}
|
|
13
|
+
export interface IWebSocketClient {
|
|
14
|
+
connect(): Promise<void>;
|
|
15
|
+
disconnect(): void;
|
|
16
|
+
send(message: WebSocketMessage): void;
|
|
17
|
+
isConnected(): boolean;
|
|
18
|
+
on(event: 'message' | 'connected' | 'disconnected' | 'error', callback: (data?: unknown) => void): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Implementação do cliente WebSocket
|
|
22
|
+
* Implementa pattern: Observer
|
|
23
|
+
*/
|
|
24
|
+
export declare class WebSocketClient implements IWebSocketClient {
|
|
25
|
+
private logger;
|
|
26
|
+
private ws;
|
|
27
|
+
private url;
|
|
28
|
+
private state;
|
|
29
|
+
private reconnectAttempts;
|
|
30
|
+
private maxReconnectAttempts;
|
|
31
|
+
private reconnectDelay;
|
|
32
|
+
private messageListeners;
|
|
33
|
+
private connectedListeners;
|
|
34
|
+
private disconnectedListeners;
|
|
35
|
+
private errorListeners;
|
|
36
|
+
constructor(serverUrl: string, logger: ILogger, appName?: string);
|
|
37
|
+
/**
|
|
38
|
+
* Conectar ao servidor WebSocket
|
|
39
|
+
*/
|
|
40
|
+
connect(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Desconectar do WebSocket
|
|
43
|
+
*/
|
|
44
|
+
disconnect(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Enviar mensagem via WebSocket
|
|
47
|
+
*/
|
|
48
|
+
send(message: WebSocketMessage): void;
|
|
49
|
+
/**
|
|
50
|
+
* Verificar se está conectado
|
|
51
|
+
*/
|
|
52
|
+
isConnected(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Registrar listener para evento
|
|
55
|
+
*/
|
|
56
|
+
on(event: 'message' | 'connected' | 'disconnected' | 'error', callback: (data?: unknown) => void): void;
|
|
57
|
+
/**
|
|
58
|
+
* Tentar reconectar com backoff exponencial
|
|
59
|
+
*/
|
|
60
|
+
private reconnect;
|
|
61
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cliente WebSocket para comunicação bidirecional em tempo real
|
|
4
|
+
* Responsabilidade única: gerenciar conexão WebSocket
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.WebSocketClient = exports.WebSocketState = void 0;
|
|
8
|
+
var WebSocketState;
|
|
9
|
+
(function (WebSocketState) {
|
|
10
|
+
WebSocketState["CONNECTING"] = "CONNECTING";
|
|
11
|
+
WebSocketState["CONNECTED"] = "CONNECTED";
|
|
12
|
+
WebSocketState["DISCONNECTED"] = "DISCONNECTED";
|
|
13
|
+
WebSocketState["ERROR"] = "ERROR";
|
|
14
|
+
})(WebSocketState || (exports.WebSocketState = WebSocketState = {}));
|
|
15
|
+
/**
|
|
16
|
+
* Implementação do cliente WebSocket
|
|
17
|
+
* Implementa pattern: Observer
|
|
18
|
+
*/
|
|
19
|
+
class WebSocketClient {
|
|
20
|
+
constructor(serverUrl, logger, appName) {
|
|
21
|
+
this.logger = logger;
|
|
22
|
+
this.ws = null;
|
|
23
|
+
this.state = WebSocketState.DISCONNECTED;
|
|
24
|
+
this.reconnectAttempts = 0;
|
|
25
|
+
this.maxReconnectAttempts = 5;
|
|
26
|
+
this.reconnectDelay = 3000;
|
|
27
|
+
// Listeners para eventos
|
|
28
|
+
this.messageListeners = new Set();
|
|
29
|
+
this.connectedListeners = new Set();
|
|
30
|
+
this.disconnectedListeners = new Set();
|
|
31
|
+
this.errorListeners = new Set();
|
|
32
|
+
// Converter http:// para ws:// ou https:// para wss://
|
|
33
|
+
const wsUrl = serverUrl
|
|
34
|
+
.replace(/^https?:\/\//, (match) => {
|
|
35
|
+
return match.startsWith('https') ? 'wss://' : 'ws://';
|
|
36
|
+
})
|
|
37
|
+
.concat('/ws');
|
|
38
|
+
// Adicionar appName como query string se fornecido
|
|
39
|
+
this.url = appName ? `${wsUrl}?appName=${encodeURIComponent(appName)}` : wsUrl;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Conectar ao servidor WebSocket
|
|
43
|
+
*/
|
|
44
|
+
async connect() {
|
|
45
|
+
if (this.state === WebSocketState.CONNECTED || this.state === WebSocketState.CONNECTING) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.state = WebSocketState.CONNECTING;
|
|
49
|
+
this.logger.debug(`Conectando ao WebSocket: ${this.url}`);
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
try {
|
|
52
|
+
this.ws = new WebSocket(this.url);
|
|
53
|
+
this.ws.onopen = () => {
|
|
54
|
+
this.state = WebSocketState.CONNECTED;
|
|
55
|
+
this.reconnectAttempts = 0;
|
|
56
|
+
this.logger.success('WebSocket conectado');
|
|
57
|
+
this.connectedListeners.forEach((cb) => cb());
|
|
58
|
+
resolve();
|
|
59
|
+
};
|
|
60
|
+
this.ws.onmessage = (event) => {
|
|
61
|
+
try {
|
|
62
|
+
const message = JSON.parse(event.data);
|
|
63
|
+
this.messageListeners.forEach((cb) => cb(message));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.logger.warn('Erro ao parsear mensagem WebSocket', error);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
this.ws.onerror = (error) => {
|
|
70
|
+
this.state = WebSocketState.ERROR;
|
|
71
|
+
this.logger.error('Erro WebSocket', error);
|
|
72
|
+
this.errorListeners.forEach((cb) => cb(error));
|
|
73
|
+
};
|
|
74
|
+
this.ws.onclose = () => {
|
|
75
|
+
this.state = WebSocketState.DISCONNECTED;
|
|
76
|
+
this.logger.warn('WebSocket desconectado');
|
|
77
|
+
this.disconnectedListeners.forEach((cb) => cb());
|
|
78
|
+
// Tentar reconectar
|
|
79
|
+
this.reconnect();
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
this.state = WebSocketState.ERROR;
|
|
84
|
+
this.logger.error('Erro ao criar WebSocket', error);
|
|
85
|
+
reject(error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Desconectar do WebSocket
|
|
91
|
+
*/
|
|
92
|
+
disconnect() {
|
|
93
|
+
if (this.ws) {
|
|
94
|
+
this.ws.close();
|
|
95
|
+
this.ws = null;
|
|
96
|
+
this.state = WebSocketState.DISCONNECTED;
|
|
97
|
+
this.maxReconnectAttempts = 0; // Previne reconexão automática
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Enviar mensagem via WebSocket
|
|
102
|
+
*/
|
|
103
|
+
send(message) {
|
|
104
|
+
if (!this.isConnected()) {
|
|
105
|
+
this.logger.warn('WebSocket não está conectado');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
this.ws.send(JSON.stringify(message));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
this.logger.error('Erro ao enviar mensagem WebSocket', error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Verificar se está conectado
|
|
117
|
+
*/
|
|
118
|
+
isConnected() {
|
|
119
|
+
return this.state === WebSocketState.CONNECTED;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Registrar listener para evento
|
|
123
|
+
*/
|
|
124
|
+
on(event, callback) {
|
|
125
|
+
switch (event) {
|
|
126
|
+
case 'message':
|
|
127
|
+
this.messageListeners.add(callback);
|
|
128
|
+
break;
|
|
129
|
+
case 'connected':
|
|
130
|
+
this.connectedListeners.add(callback);
|
|
131
|
+
break;
|
|
132
|
+
case 'disconnected':
|
|
133
|
+
this.disconnectedListeners.add(callback);
|
|
134
|
+
break;
|
|
135
|
+
case 'error':
|
|
136
|
+
this.errorListeners.add(callback);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Tentar reconectar com backoff exponencial
|
|
142
|
+
*/
|
|
143
|
+
reconnect() {
|
|
144
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
145
|
+
this.logger.error(`Falha ao reconectar após ${this.maxReconnectAttempts} tentativas`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.reconnectAttempts++;
|
|
149
|
+
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
150
|
+
this.logger.warn(`Tentando reconectar em ${delay}ms (tentativa ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
this.connect().catch((error) => {
|
|
153
|
+
this.logger.error('Reconexão falhou', error);
|
|
154
|
+
});
|
|
155
|
+
}, delay);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.WebSocketClient = WebSocketClient;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interceptador de fetch nativo do navegador
|
|
3
|
+
* Responsabilidade única: interceptar e logar requisições fetch
|
|
4
|
+
*/
|
|
5
|
+
import { ILogger } from '../../core/Logger';
|
|
6
|
+
import { IRequestLogger } from './RequestLogger';
|
|
7
|
+
import { IMockResolver } from './MockResolver';
|
|
8
|
+
export interface IFetchInterceptor {
|
|
9
|
+
install(): void;
|
|
10
|
+
uninstall(): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Implementação de interceptador de fetch
|
|
14
|
+
* Preserva fetch original e sobrescreve com versão interceptada
|
|
15
|
+
*/
|
|
16
|
+
export declare class FetchInterceptor implements IFetchInterceptor {
|
|
17
|
+
private logger;
|
|
18
|
+
private requestLogger;
|
|
19
|
+
private mockResolver;
|
|
20
|
+
private sessionId;
|
|
21
|
+
private appName;
|
|
22
|
+
private originalFetch;
|
|
23
|
+
private isInstalled;
|
|
24
|
+
constructor(logger: ILogger, requestLogger: IRequestLogger, mockResolver: IMockResolver, sessionId: string, appName: string);
|
|
25
|
+
/**
|
|
26
|
+
* Instalar o interceptador
|
|
27
|
+
* Substitui fetch global
|
|
28
|
+
*/
|
|
29
|
+
install(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Desinstalar o interceptador
|
|
32
|
+
* Restaura fetch original
|
|
33
|
+
*/
|
|
34
|
+
uninstall(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Criar versão interceptada de fetch
|
|
37
|
+
*/
|
|
38
|
+
private createInterceptedFetch;
|
|
39
|
+
/**
|
|
40
|
+
* Parse seguro do body (string ou object)
|
|
41
|
+
*/
|
|
42
|
+
private safeParseBody;
|
|
43
|
+
/**
|
|
44
|
+
* Gerar ID único para requisição
|
|
45
|
+
*/
|
|
46
|
+
private generateId;
|
|
47
|
+
/**
|
|
48
|
+
* Sleep (para delays de mock)
|
|
49
|
+
*/
|
|
50
|
+
private delay;
|
|
51
|
+
}
|