tradingview-mcp-server 0.0.1
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/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/api/client.d.ts +11 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +45 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/types.d.ts +68 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +5 -0
- package/dist/api/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +265 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/presets.d.ts +24 -0
- package/dist/resources/presets.d.ts.map +1 -0
- package/dist/resources/presets.js +85 -0
- package/dist/resources/presets.js.map +1 -0
- package/dist/tests/cache.test.d.ts +2 -0
- package/dist/tests/cache.test.d.ts.map +1 -0
- package/dist/tests/cache.test.js +41 -0
- package/dist/tests/cache.test.js.map +1 -0
- package/dist/tests/fields.test.d.ts +2 -0
- package/dist/tests/fields.test.d.ts.map +1 -0
- package/dist/tests/fields.test.js +56 -0
- package/dist/tests/fields.test.js.map +1 -0
- package/dist/tests/presets.test.d.ts +2 -0
- package/dist/tests/presets.test.d.ts.map +1 -0
- package/dist/tests/presets.test.js +59 -0
- package/dist/tests/presets.test.js.map +1 -0
- package/dist/tests/rateLimit.test.d.ts +2 -0
- package/dist/tests/rateLimit.test.d.ts.map +1 -0
- package/dist/tests/rateLimit.test.js +44 -0
- package/dist/tests/rateLimit.test.js.map +1 -0
- package/dist/tools/fields.d.ts +8 -0
- package/dist/tools/fields.d.ts.map +1 -0
- package/dist/tools/fields.js +229 -0
- package/dist/tools/fields.js.map +1 -0
- package/dist/tools/screen.d.ts +17 -0
- package/dist/tools/screen.d.ts.map +1 -0
- package/dist/tools/screen.js +171 -0
- package/dist/tools/screen.js.map +1 -0
- package/dist/utils/cache.d.ts +13 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +45 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/rateLimit.d.ts +11 -0
- package/dist/utils/rateLimit.d.ts.map +1 -0
- package/dist/utils/rateLimit.js +28 -0
- package/dist/utils/rateLimit.js.map +1 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* TradingView MCP Server
|
|
4
|
+
* Unofficial MCP server for TradingView stock screener API
|
|
5
|
+
*/
|
|
6
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { TradingViewClient } from "./api/client.js";
|
|
10
|
+
import { ScreenTool } from "./tools/screen.js";
|
|
11
|
+
import { FieldsTool } from "./tools/fields.js";
|
|
12
|
+
import { PresetsTool, PRESETS } from "./resources/presets.js";
|
|
13
|
+
import { Cache } from "./utils/cache.js";
|
|
14
|
+
import { RateLimiter } from "./utils/rateLimit.js";
|
|
15
|
+
// Configuration from environment
|
|
16
|
+
const CACHE_TTL = parseInt(process.env.CACHE_TTL_SECONDS || "300");
|
|
17
|
+
const RATE_LIMIT_RPM = parseInt(process.env.RATE_LIMIT_RPM || "10");
|
|
18
|
+
// Initialize components
|
|
19
|
+
const client = new TradingViewClient();
|
|
20
|
+
const cache = new Cache(CACHE_TTL);
|
|
21
|
+
const rateLimiter = new RateLimiter(RATE_LIMIT_RPM);
|
|
22
|
+
const screenTool = new ScreenTool(client, cache, rateLimiter);
|
|
23
|
+
const fieldsTool = new FieldsTool();
|
|
24
|
+
const presetsTool = new PresetsTool();
|
|
25
|
+
// Start cache cleanup
|
|
26
|
+
cache.startCleanup();
|
|
27
|
+
// Create MCP server
|
|
28
|
+
const server = new Server({
|
|
29
|
+
name: "tradingview-mcp-server",
|
|
30
|
+
version: "0.1.0",
|
|
31
|
+
}, {
|
|
32
|
+
capabilities: {
|
|
33
|
+
tools: {},
|
|
34
|
+
resources: {},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
// List available tools
|
|
38
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
39
|
+
return {
|
|
40
|
+
tools: [
|
|
41
|
+
{
|
|
42
|
+
name: "screen_stocks",
|
|
43
|
+
description: "Screen stocks based on fundamental and technical criteria. Returns stocks matching the specified filters.",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: {
|
|
47
|
+
filters: {
|
|
48
|
+
type: "array",
|
|
49
|
+
description: "Array of filter conditions to apply",
|
|
50
|
+
items: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
field: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Field name to filter (e.g., 'return_on_equity', 'price_earnings_ttm')",
|
|
56
|
+
},
|
|
57
|
+
operator: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Comparison operator: greater, less, greater_or_equal, less_or_equal, equal, in_range, etc.",
|
|
60
|
+
},
|
|
61
|
+
value: {
|
|
62
|
+
description: "Value to compare against (number, string for field comparison, or array [min, max] for in_range)",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ["field", "operator", "value"],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
markets: {
|
|
69
|
+
type: "array",
|
|
70
|
+
items: { type: "string" },
|
|
71
|
+
description: "Markets to scan (e.g., ['america', 'japan']). Default: ['america']",
|
|
72
|
+
},
|
|
73
|
+
sort_by: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "Field to sort results by. Default: 'market_cap_basic'",
|
|
76
|
+
},
|
|
77
|
+
sort_order: {
|
|
78
|
+
type: "string",
|
|
79
|
+
enum: ["asc", "desc"],
|
|
80
|
+
description: "Sort order. Default: 'desc'",
|
|
81
|
+
},
|
|
82
|
+
limit: {
|
|
83
|
+
type: "number",
|
|
84
|
+
description: "Number of results to return (1-200). Default: 20",
|
|
85
|
+
minimum: 1,
|
|
86
|
+
maximum: 200,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ["filters"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "list_fields",
|
|
94
|
+
description: "List available fields for filtering and display. Use this to discover what fields you can filter and sort by.",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
asset_type: {
|
|
99
|
+
type: "string",
|
|
100
|
+
enum: ["stock", "forex", "crypto"],
|
|
101
|
+
description: "Type of asset. Default: 'stock'",
|
|
102
|
+
},
|
|
103
|
+
category: {
|
|
104
|
+
type: "string",
|
|
105
|
+
enum: ["fundamental", "technical", "performance"],
|
|
106
|
+
description: "Filter fields by category. If omitted, returns all categories",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "get_preset",
|
|
113
|
+
description: "Get a pre-configured screening strategy. Returns filter configuration for common screening strategies like quality stocks, value stocks, etc.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: "object",
|
|
116
|
+
properties: {
|
|
117
|
+
preset_name: {
|
|
118
|
+
type: "string",
|
|
119
|
+
description: "Name of preset: quality_stocks, value_stocks, dividend_stocks, momentum_stocks, growth_stocks",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
required: ["preset_name"],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "list_presets",
|
|
127
|
+
description: "List all available preset screening strategies",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
// Handle tool calls
|
|
137
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
138
|
+
try {
|
|
139
|
+
const { name, arguments: args } = request.params;
|
|
140
|
+
switch (name) {
|
|
141
|
+
case "screen_stocks": {
|
|
142
|
+
const result = await screenTool.screenStocks(args);
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: JSON.stringify(result, null, 2),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
case "list_fields": {
|
|
153
|
+
const result = fieldsTool.listFields(args);
|
|
154
|
+
return {
|
|
155
|
+
content: [
|
|
156
|
+
{
|
|
157
|
+
type: "text",
|
|
158
|
+
text: JSON.stringify(result, null, 2),
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
case "get_preset": {
|
|
164
|
+
const preset = presetsTool.getPreset(args.preset_name);
|
|
165
|
+
if (!preset) {
|
|
166
|
+
return {
|
|
167
|
+
content: [
|
|
168
|
+
{
|
|
169
|
+
type: "text",
|
|
170
|
+
text: JSON.stringify({ error: "Preset not found" }, null, 2),
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
isError: true,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: "text",
|
|
180
|
+
text: JSON.stringify(preset, null, 2),
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
case "list_presets": {
|
|
186
|
+
const presets = presetsTool.listPresets();
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: "text",
|
|
191
|
+
text: JSON.stringify(presets, null, 2),
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
default:
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: JSON.stringify({ error: `Unknown tool: ${name}` }),
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
isError: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
return {
|
|
210
|
+
content: [
|
|
211
|
+
{
|
|
212
|
+
type: "text",
|
|
213
|
+
text: JSON.stringify({
|
|
214
|
+
error: error instanceof Error ? error.message : String(error),
|
|
215
|
+
}, null, 2),
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
isError: true,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
// List resources
|
|
223
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
224
|
+
return {
|
|
225
|
+
resources: Object.keys(PRESETS).map((key) => ({
|
|
226
|
+
uri: `preset://${key}`,
|
|
227
|
+
name: PRESETS[key].name,
|
|
228
|
+
description: PRESETS[key].description,
|
|
229
|
+
mimeType: "application/json",
|
|
230
|
+
})),
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
// Read resource
|
|
234
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
235
|
+
const uri = request.params.uri;
|
|
236
|
+
if (!uri.startsWith("preset://")) {
|
|
237
|
+
throw new Error("Invalid resource URI");
|
|
238
|
+
}
|
|
239
|
+
const presetName = uri.replace("preset://", "");
|
|
240
|
+
const preset = PRESETS[presetName];
|
|
241
|
+
if (!preset) {
|
|
242
|
+
throw new Error(`Preset not found: ${presetName}`);
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
contents: [
|
|
246
|
+
{
|
|
247
|
+
uri,
|
|
248
|
+
mimeType: "application/json",
|
|
249
|
+
text: JSON.stringify(preset, null, 2),
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
// Start server
|
|
255
|
+
async function main() {
|
|
256
|
+
const transport = new StdioServerTransport();
|
|
257
|
+
await server.connect(transport);
|
|
258
|
+
console.error("TradingView MCP Server running on stdio");
|
|
259
|
+
console.error(`Cache TTL: ${CACHE_TTL}s | Rate Limit: ${RATE_LIMIT_RPM} req/min`);
|
|
260
|
+
}
|
|
261
|
+
main().catch((error) => {
|
|
262
|
+
console.error("Fatal error:", error);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
});
|
|
265
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset screening configurations
|
|
3
|
+
*/
|
|
4
|
+
export interface Preset {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
filters: Array<{
|
|
8
|
+
field: string;
|
|
9
|
+
operator: string;
|
|
10
|
+
value: number | string | [number, number];
|
|
11
|
+
}>;
|
|
12
|
+
markets?: string[];
|
|
13
|
+
sort_by?: string;
|
|
14
|
+
sort_order?: "asc" | "desc";
|
|
15
|
+
}
|
|
16
|
+
export declare const PRESETS: Record<string, Preset>;
|
|
17
|
+
export declare class PresetsTool {
|
|
18
|
+
getPreset(presetName: string): Preset | null;
|
|
19
|
+
listPresets(): Array<{
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/resources/presets.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC3C,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA0E1C,CAAC;AAEF,qBAAa,WAAW;IACtB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI5C,WAAW,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAM5D"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset screening configurations
|
|
3
|
+
*/
|
|
4
|
+
export const PRESETS = {
|
|
5
|
+
quality_stocks: {
|
|
6
|
+
name: "Quality Stocks (Conservative)",
|
|
7
|
+
description: "High-quality, low-volatility stocks with strong fundamentals and uptrends. Based on Avanza conservative screening strategy.",
|
|
8
|
+
filters: [
|
|
9
|
+
{ field: "return_on_equity", operator: "greater", value: 12 },
|
|
10
|
+
{ field: "market_cap_basic", operator: "greater", value: 200000000 },
|
|
11
|
+
{ field: "price_earnings_ttm", operator: "less", value: 40 },
|
|
12
|
+
{ field: "price_sales_ratio", operator: "less", value: 8 },
|
|
13
|
+
{ field: "debt_to_equity", operator: "less", value: 0.7 },
|
|
14
|
+
{ field: "after_tax_margin", operator: "greater", value: 10 },
|
|
15
|
+
{ field: "RSI", operator: "in_range", value: [45, 65] },
|
|
16
|
+
{ field: "Volatility.M", operator: "less_or_equal", value: 3 },
|
|
17
|
+
{ field: "SMA50", operator: "greater", value: "SMA200" },
|
|
18
|
+
],
|
|
19
|
+
markets: ["america"],
|
|
20
|
+
sort_by: "market_cap_basic",
|
|
21
|
+
sort_order: "desc",
|
|
22
|
+
},
|
|
23
|
+
value_stocks: {
|
|
24
|
+
name: "Value Stocks",
|
|
25
|
+
description: "Undervalued stocks with low P/E and P/B ratios",
|
|
26
|
+
filters: [
|
|
27
|
+
{ field: "price_earnings_ttm", operator: "less", value: 15 },
|
|
28
|
+
{ field: "price_book_fq", operator: "less", value: 1.5 },
|
|
29
|
+
{ field: "market_cap_basic", operator: "greater", value: 1000000000 },
|
|
30
|
+
{ field: "return_on_equity", operator: "greater", value: 10 },
|
|
31
|
+
],
|
|
32
|
+
markets: ["america"],
|
|
33
|
+
sort_by: "price_earnings_ttm",
|
|
34
|
+
sort_order: "asc",
|
|
35
|
+
},
|
|
36
|
+
dividend_stocks: {
|
|
37
|
+
name: "Dividend Stocks",
|
|
38
|
+
description: "High dividend yield with consistent payout",
|
|
39
|
+
filters: [
|
|
40
|
+
{ field: "dividend_yield_recent", operator: "greater", value: 3 },
|
|
41
|
+
{ field: "market_cap_basic", operator: "greater", value: 5000000000 },
|
|
42
|
+
{ field: "debt_to_equity", operator: "less", value: 1.0 },
|
|
43
|
+
],
|
|
44
|
+
markets: ["america"],
|
|
45
|
+
sort_by: "dividend_yield_recent",
|
|
46
|
+
sort_order: "desc",
|
|
47
|
+
},
|
|
48
|
+
momentum_stocks: {
|
|
49
|
+
name: "Momentum Stocks",
|
|
50
|
+
description: "Stocks with strong recent performance and technical momentum",
|
|
51
|
+
filters: [
|
|
52
|
+
{ field: "RSI", operator: "in_range", value: [50, 70] },
|
|
53
|
+
{ field: "SMA50", operator: "greater", value: "SMA200" },
|
|
54
|
+
{ field: "Perf.1M", operator: "greater", value: 5 },
|
|
55
|
+
{ field: "volume", operator: "greater", value: 1000000 },
|
|
56
|
+
],
|
|
57
|
+
markets: ["america"],
|
|
58
|
+
sort_by: "Perf.1M",
|
|
59
|
+
sort_order: "desc",
|
|
60
|
+
},
|
|
61
|
+
growth_stocks: {
|
|
62
|
+
name: "Growth Stocks",
|
|
63
|
+
description: "High-growth companies with strong revenue and earnings expansion",
|
|
64
|
+
filters: [
|
|
65
|
+
{ field: "return_on_equity", operator: "greater", value: 20 },
|
|
66
|
+
{ field: "operating_margin", operator: "greater", value: 15 },
|
|
67
|
+
{ field: "market_cap_basic", operator: "greater", value: 1000000000 },
|
|
68
|
+
],
|
|
69
|
+
markets: ["america"],
|
|
70
|
+
sort_by: "return_on_equity",
|
|
71
|
+
sort_order: "desc",
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
export class PresetsTool {
|
|
75
|
+
getPreset(presetName) {
|
|
76
|
+
return PRESETS[presetName] || null;
|
|
77
|
+
}
|
|
78
|
+
listPresets() {
|
|
79
|
+
return Object.entries(PRESETS).map(([key, preset]) => ({
|
|
80
|
+
name: key,
|
|
81
|
+
description: preset.description,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/resources/presets.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH,MAAM,CAAC,MAAM,OAAO,GAA2B;IAC7C,cAAc,EAAE;QACd,IAAI,EAAE,+BAA+B;QACrC,WAAW,EACT,6HAA6H;QAC/H,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;YAC7D,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;YACpE,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;YAC5D,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE;YAC1D,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE;YACzD,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;YAC7D,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE;YACvD,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE;YAC9D,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE;SACzD;QACD,OAAO,EAAE,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,kBAAkB;QAC3B,UAAU,EAAE,MAAM;KACnB;IAED,YAAY,EAAE;QACZ,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,gDAAgD;QAC7D,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;YAC5D,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE;YACxD,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE;YACrE,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;SAC9D;QACD,OAAO,EAAE,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,oBAAoB;QAC7B,UAAU,EAAE,KAAK;KAClB;IAED,eAAe,EAAE;QACf,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,4CAA4C;QACzD,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,uBAAuB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE;YACjE,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE;YACrE,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE;SAC1D;QACD,OAAO,EAAE,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,uBAAuB;QAChC,UAAU,EAAE,MAAM;KACnB;IAED,eAAe,EAAE;QACf,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,8DAA8D;QAC3E,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE;YACvD,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE;YACxD,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE;YACnD,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE;SACzD;QACD,OAAO,EAAE,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,MAAM;KACnB;IAED,aAAa,EAAE;QACb,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,kEAAkE;QAC/E,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;YAC7D,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;YAC7D,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE;SACtE;QACD,OAAO,EAAE,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,kBAAkB;QAC3B,UAAU,EAAE,MAAM;KACnB;CACF,CAAC;AAEF,MAAM,OAAO,WAAW;IACtB,SAAS,CAAC,UAAkB;QAC1B,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,WAAW;QACT,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACrD,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,MAAM,CAAC,WAAW;SAChC,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../../src/tests/cache.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { Cache } from "../utils/cache.js";
|
|
4
|
+
describe("Cache", () => {
|
|
5
|
+
it("should store and retrieve values", () => {
|
|
6
|
+
const cache = new Cache(60);
|
|
7
|
+
cache.set("test", { value: 123 });
|
|
8
|
+
const result = cache.get("test");
|
|
9
|
+
assert.deepStrictEqual(result, { value: 123 });
|
|
10
|
+
});
|
|
11
|
+
it("should return null for non-existent keys", () => {
|
|
12
|
+
const cache = new Cache(60);
|
|
13
|
+
const result = cache.get("nonexistent");
|
|
14
|
+
assert.strictEqual(result, null);
|
|
15
|
+
});
|
|
16
|
+
it("should expire entries after TTL", async () => {
|
|
17
|
+
const cache = new Cache(1); // 1 second TTL
|
|
18
|
+
cache.set("test", "value");
|
|
19
|
+
// Should exist immediately
|
|
20
|
+
assert.strictEqual(cache.get("test"), "value");
|
|
21
|
+
// Wait for expiration
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
23
|
+
// Should be expired
|
|
24
|
+
assert.strictEqual(cache.get("test"), null);
|
|
25
|
+
});
|
|
26
|
+
it("should not cache when TTL is 0", () => {
|
|
27
|
+
const cache = new Cache(0);
|
|
28
|
+
cache.set("test", "value");
|
|
29
|
+
const result = cache.get("test");
|
|
30
|
+
assert.strictEqual(result, null);
|
|
31
|
+
});
|
|
32
|
+
it("should clear all entries", () => {
|
|
33
|
+
const cache = new Cache(60);
|
|
34
|
+
cache.set("key1", "value1");
|
|
35
|
+
cache.set("key2", "value2");
|
|
36
|
+
cache.clear();
|
|
37
|
+
assert.strictEqual(cache.get("key1"), null);
|
|
38
|
+
assert.strictEqual(cache.get("key2"), null);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../src/tests/cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe;QAC3C,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAE3B,2BAA2B;QAC3B,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAE/C,sBAAsB;QACtB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAE1D,oBAAoB;QACpB,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC5B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAE5B,KAAK,CAAC,KAAK,EAAE,CAAC;QAEd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.test.d.ts","sourceRoot":"","sources":["../../src/tests/fields.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { FieldsTool } from "../tools/fields.js";
|
|
4
|
+
describe("FieldsTool", () => {
|
|
5
|
+
const fieldsTool = new FieldsTool();
|
|
6
|
+
it("should list all stock fields", () => {
|
|
7
|
+
const result = fieldsTool.listFields({ asset_type: "stock" });
|
|
8
|
+
assert.strictEqual(result.asset_type, "stock");
|
|
9
|
+
assert.strictEqual(result.category, "all");
|
|
10
|
+
assert.ok(result.field_count > 0, "Should have fields");
|
|
11
|
+
assert.ok(Array.isArray(result.fields), "Fields should be an array");
|
|
12
|
+
});
|
|
13
|
+
it("should filter by category", () => {
|
|
14
|
+
const result = fieldsTool.listFields({
|
|
15
|
+
asset_type: "stock",
|
|
16
|
+
category: "fundamental",
|
|
17
|
+
});
|
|
18
|
+
assert.strictEqual(result.category, "fundamental");
|
|
19
|
+
assert.ok(result.fields.length > 0, "Should have fundamental fields");
|
|
20
|
+
// All fields should be fundamental
|
|
21
|
+
const allFundamental = result.fields.every((f) => f.category === "fundamental");
|
|
22
|
+
assert.ok(allFundamental, "All fields should be fundamental");
|
|
23
|
+
});
|
|
24
|
+
it("should filter technical fields", () => {
|
|
25
|
+
const result = fieldsTool.listFields({
|
|
26
|
+
asset_type: "stock",
|
|
27
|
+
category: "technical",
|
|
28
|
+
});
|
|
29
|
+
assert.strictEqual(result.category, "technical");
|
|
30
|
+
const allTechnical = result.fields.every((f) => f.category === "technical");
|
|
31
|
+
assert.ok(allTechnical, "All fields should be technical");
|
|
32
|
+
});
|
|
33
|
+
it("should filter performance fields", () => {
|
|
34
|
+
const result = fieldsTool.listFields({
|
|
35
|
+
asset_type: "stock",
|
|
36
|
+
category: "performance",
|
|
37
|
+
});
|
|
38
|
+
assert.strictEqual(result.category, "performance");
|
|
39
|
+
const allPerformance = result.fields.every((f) => f.category === "performance");
|
|
40
|
+
assert.ok(allPerformance, "All fields should be performance");
|
|
41
|
+
});
|
|
42
|
+
it("should return message for unsupported asset types", () => {
|
|
43
|
+
const result = fieldsTool.listFields({ asset_type: "forex" });
|
|
44
|
+
assert.ok(result.message, "Should have message");
|
|
45
|
+
assert.strictEqual(result.fields.length, 0, "Should have no fields");
|
|
46
|
+
});
|
|
47
|
+
it("should include field metadata", () => {
|
|
48
|
+
const result = fieldsTool.listFields({ asset_type: "stock" });
|
|
49
|
+
const sampleField = result.fields[0];
|
|
50
|
+
assert.ok(sampleField.name, "Field should have name");
|
|
51
|
+
assert.ok(sampleField.label, "Field should have label");
|
|
52
|
+
assert.ok(sampleField.category, "Field should have category");
|
|
53
|
+
assert.ok(sampleField.type, "Field should have type");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=fields.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.test.js","sourceRoot":"","sources":["../../src/tests/fields.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.test.d.ts","sourceRoot":"","sources":["../../src/tests/presets.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { PresetsTool, PRESETS } from "../resources/presets.js";
|
|
4
|
+
describe("PresetsTool", () => {
|
|
5
|
+
const presetsTool = new PresetsTool();
|
|
6
|
+
it("should list all presets", () => {
|
|
7
|
+
const presets = presetsTool.listPresets();
|
|
8
|
+
assert.ok(Array.isArray(presets), "Should return array");
|
|
9
|
+
assert.ok(presets.length > 0, "Should have presets");
|
|
10
|
+
const preset = presets[0];
|
|
11
|
+
assert.ok(preset.name, "Preset should have name");
|
|
12
|
+
assert.ok(preset.description, "Preset should have description");
|
|
13
|
+
});
|
|
14
|
+
it("should get quality_stocks preset", () => {
|
|
15
|
+
const preset = presetsTool.getPreset("quality_stocks");
|
|
16
|
+
assert.ok(preset, "Should return preset");
|
|
17
|
+
assert.strictEqual(preset.name, "Quality Stocks (Conservative)");
|
|
18
|
+
assert.ok(Array.isArray(preset.filters), "Should have filters");
|
|
19
|
+
assert.ok(preset.filters.length > 0, "Should have at least one filter");
|
|
20
|
+
});
|
|
21
|
+
it("should get value_stocks preset", () => {
|
|
22
|
+
const preset = presetsTool.getPreset("value_stocks");
|
|
23
|
+
assert.ok(preset, "Should return preset");
|
|
24
|
+
assert.strictEqual(preset.name, "Value Stocks");
|
|
25
|
+
assert.ok(preset.filters.length > 0, "Should have filters");
|
|
26
|
+
});
|
|
27
|
+
it("should return null for non-existent preset", () => {
|
|
28
|
+
const preset = presetsTool.getPreset("nonexistent");
|
|
29
|
+
assert.strictEqual(preset, null);
|
|
30
|
+
});
|
|
31
|
+
it("should have valid filter structure in presets", () => {
|
|
32
|
+
for (const [key, preset] of Object.entries(PRESETS)) {
|
|
33
|
+
assert.ok(preset.filters, `${key} should have filters`);
|
|
34
|
+
for (const filter of preset.filters) {
|
|
35
|
+
assert.ok(filter.field, `Filter should have field in ${key}`);
|
|
36
|
+
assert.ok(filter.operator, `Filter should have operator in ${key}`);
|
|
37
|
+
assert.ok(filter.value !== undefined, `Filter should have value in ${key}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
it("should have quality_stocks preset with correct filters", () => {
|
|
42
|
+
const preset = PRESETS.quality_stocks;
|
|
43
|
+
// Check for key filters
|
|
44
|
+
const roeFilter = preset.filters.find((f) => f.field === "return_on_equity");
|
|
45
|
+
assert.ok(roeFilter, "Should have ROE filter");
|
|
46
|
+
assert.strictEqual(roeFilter.operator, "greater");
|
|
47
|
+
assert.strictEqual(roeFilter.value, 12);
|
|
48
|
+
const rsiFilter = preset.filters.find((f) => f.field === "RSI");
|
|
49
|
+
assert.ok(rsiFilter, "Should have RSI filter");
|
|
50
|
+
assert.strictEqual(rsiFilter.operator, "in_range");
|
|
51
|
+
assert.deepStrictEqual(rsiFilter.value, [45, 65]);
|
|
52
|
+
});
|
|
53
|
+
it("should have markets defined for stock presets", () => {
|
|
54
|
+
const preset = PRESETS.quality_stocks;
|
|
55
|
+
assert.ok(preset.markets, "Should have markets");
|
|
56
|
+
assert.ok(preset.markets.includes("america"), "Should include america market");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=presets.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.test.js","sourceRoot":"","sources":["../../src/tests/presets.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAE/D,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IAEtC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;QAE1C,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;QACzD,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,qBAAqB,CAAC,CAAC;QAErD,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC;QAClD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,gCAAgC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAEvD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,+BAA+B,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;QAChE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,iCAAiC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAErD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACpD,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,GAAG,sBAAsB,CAAC,CAAC;YAExD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,+BAA+B,GAAG,EAAE,CAAC,CAAC;gBAC9D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,kCAAkC,GAAG,EAAE,CAAC,CAAC;gBACpE,MAAM,CAAC,EAAE,CACP,MAAM,CAAC,KAAK,KAAK,SAAS,EAC1B,+BAA+B,GAAG,EAAE,CACrC,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;QAEtC,wBAAwB;QACxB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,kBAAkB,CAAC,CAAC;QAC7E,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;QAC/C,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAExC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QAChE,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;QAC/C,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACnD,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;QACjD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,+BAA+B,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.test.d.ts","sourceRoot":"","sources":["../../src/tests/rateLimit.test.ts"],"names":[],"mappings":""}
|