recker 1.0.2-0 → 1.0.4
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 +0 -2
- package/README.md +121 -72
- package/dist/cache/memory-storage.d.ts.map +1 -1
- package/dist/cache/memory-storage.js +7 -1
- package/dist/constants/http-status.d.ts +74 -0
- package/dist/constants/http-status.d.ts.map +1 -0
- package/dist/constants/http-status.js +156 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -6
- package/dist/cookies/memory-cookie-jar.d.ts +31 -0
- package/dist/cookies/memory-cookie-jar.d.ts.map +1 -0
- package/dist/cookies/memory-cookie-jar.js +210 -0
- package/dist/core/client.d.ts +9 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +252 -53
- package/dist/core/errors.d.ts +18 -2
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +66 -5
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/request-promise.d.ts.map +1 -1
- package/dist/core/request-promise.js +8 -2
- package/dist/core/request.d.ts +7 -1
- package/dist/core/request.d.ts.map +1 -1
- package/dist/core/request.js +32 -0
- package/dist/core/response.d.ts +2 -0
- package/dist/core/response.d.ts.map +1 -1
- package/dist/core/response.js +44 -19
- package/dist/events/request-events.d.ts +48 -0
- package/dist/events/request-events.d.ts.map +1 -0
- package/dist/events/request-events.js +85 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -2
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +16 -5
- package/dist/mcp/contract.d.ts +77 -0
- package/dist/mcp/contract.d.ts.map +1 -0
- package/dist/mcp/contract.js +278 -0
- package/dist/mcp/types.d.ts +1 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/plugins/auth.d.ts +45 -0
- package/dist/plugins/auth.d.ts.map +1 -0
- package/dist/plugins/auth.js +268 -0
- package/dist/plugins/cache.d.ts +7 -1
- package/dist/plugins/cache.d.ts.map +1 -1
- package/dist/plugins/cache.js +470 -49
- package/dist/plugins/circuit-breaker.js +1 -1
- package/dist/plugins/compression.d.ts.map +1 -1
- package/dist/plugins/compression.js +3 -3
- package/dist/plugins/dedup.d.ts.map +1 -1
- package/dist/plugins/dedup.js +2 -1
- package/dist/plugins/graphql.d.ts +4 -3
- package/dist/plugins/graphql.d.ts.map +1 -1
- package/dist/plugins/graphql.js +24 -5
- package/dist/plugins/grpc-web.d.ts +80 -0
- package/dist/plugins/grpc-web.d.ts.map +1 -0
- package/dist/plugins/grpc-web.js +261 -0
- package/dist/plugins/har-player.d.ts.map +1 -1
- package/dist/plugins/har-player.js +11 -2
- package/dist/plugins/hls.d.ts +33 -0
- package/dist/plugins/hls.d.ts.map +1 -0
- package/dist/plugins/hls.js +225 -0
- package/dist/plugins/http2-push.d.ts +64 -0
- package/dist/plugins/http2-push.d.ts.map +1 -0
- package/dist/plugins/http2-push.js +274 -0
- package/dist/plugins/http3.d.ts +76 -0
- package/dist/plugins/http3.d.ts.map +1 -0
- package/dist/plugins/http3.js +231 -0
- package/dist/plugins/interface-rotator.d.ts +10 -0
- package/dist/plugins/interface-rotator.d.ts.map +1 -0
- package/dist/plugins/interface-rotator.js +57 -0
- package/dist/plugins/jsonrpc.d.ts +76 -0
- package/dist/plugins/jsonrpc.d.ts.map +1 -0
- package/dist/plugins/jsonrpc.js +143 -0
- package/dist/plugins/logger.d.ts +8 -5
- package/dist/plugins/logger.d.ts.map +1 -1
- package/dist/plugins/logger.js +66 -30
- package/dist/plugins/odata.d.ts +182 -0
- package/dist/plugins/odata.d.ts.map +1 -0
- package/dist/plugins/odata.js +561 -0
- package/dist/plugins/retry.d.ts +1 -0
- package/dist/plugins/retry.d.ts.map +1 -1
- package/dist/plugins/retry.js +26 -2
- package/dist/plugins/scrape.d.ts +22 -0
- package/dist/plugins/scrape.d.ts.map +1 -0
- package/dist/plugins/scrape.js +87 -0
- package/dist/plugins/soap.d.ts +73 -0
- package/dist/plugins/soap.d.ts.map +1 -0
- package/dist/plugins/soap.js +347 -0
- package/dist/plugins/user-agent.d.ts +8 -0
- package/dist/plugins/user-agent.d.ts.map +1 -0
- package/dist/plugins/user-agent.js +46 -0
- package/dist/plugins/xml.d.ts +10 -0
- package/dist/plugins/xml.d.ts.map +1 -0
- package/dist/plugins/xml.js +194 -0
- package/dist/presets/anthropic.d.ts +7 -0
- package/dist/presets/anthropic.d.ts.map +1 -0
- package/dist/presets/anthropic.js +17 -0
- package/dist/presets/azure-openai.d.ts +9 -0
- package/dist/presets/azure-openai.d.ts.map +1 -0
- package/dist/presets/azure-openai.js +25 -0
- package/dist/presets/cloudflare.d.ts +13 -0
- package/dist/presets/cloudflare.d.ts.map +1 -0
- package/dist/presets/cloudflare.js +39 -0
- package/dist/presets/cohere.d.ts +6 -0
- package/dist/presets/cohere.d.ts.map +1 -0
- package/dist/presets/cohere.js +16 -0
- package/dist/presets/deepseek.d.ts +6 -0
- package/dist/presets/deepseek.d.ts.map +1 -0
- package/dist/presets/deepseek.js +16 -0
- package/dist/presets/digitalocean.d.ts +6 -0
- package/dist/presets/digitalocean.d.ts.map +1 -0
- package/dist/presets/digitalocean.js +16 -0
- package/dist/presets/discord.d.ts +7 -0
- package/dist/presets/discord.d.ts.map +1 -0
- package/dist/presets/discord.js +17 -0
- package/dist/presets/fireworks.d.ts +6 -0
- package/dist/presets/fireworks.d.ts.map +1 -0
- package/dist/presets/fireworks.js +16 -0
- package/dist/presets/gemini.d.ts +6 -0
- package/dist/presets/gemini.d.ts.map +1 -0
- package/dist/presets/gemini.js +16 -0
- package/dist/presets/github.d.ts +7 -0
- package/dist/presets/github.d.ts.map +1 -0
- package/dist/presets/github.js +17 -0
- package/dist/presets/gitlab.d.ts +7 -0
- package/dist/presets/gitlab.d.ts.map +1 -0
- package/dist/presets/gitlab.js +16 -0
- package/dist/presets/groq.d.ts +6 -0
- package/dist/presets/groq.d.ts.map +1 -0
- package/dist/presets/groq.js +16 -0
- package/dist/presets/huggingface.d.ts +6 -0
- package/dist/presets/huggingface.d.ts.map +1 -0
- package/dist/presets/huggingface.js +16 -0
- package/dist/presets/index.d.ts +28 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +27 -0
- package/dist/presets/linear.d.ts +6 -0
- package/dist/presets/linear.d.ts.map +1 -0
- package/dist/presets/linear.js +16 -0
- package/dist/presets/mistral.d.ts +6 -0
- package/dist/presets/mistral.d.ts.map +1 -0
- package/dist/presets/mistral.js +16 -0
- package/dist/presets/notion.d.ts +7 -0
- package/dist/presets/notion.d.ts.map +1 -0
- package/dist/presets/notion.js +17 -0
- package/dist/presets/openai.d.ts +8 -0
- package/dist/presets/openai.d.ts.map +1 -0
- package/dist/presets/openai.js +23 -0
- package/dist/presets/perplexity.d.ts +6 -0
- package/dist/presets/perplexity.d.ts.map +1 -0
- package/dist/presets/perplexity.js +16 -0
- package/dist/presets/registry.d.ts +20 -0
- package/dist/presets/registry.d.ts.map +1 -0
- package/dist/presets/registry.js +311 -0
- package/dist/presets/replicate.d.ts +6 -0
- package/dist/presets/replicate.d.ts.map +1 -0
- package/dist/presets/replicate.js +16 -0
- package/dist/presets/slack.d.ts +6 -0
- package/dist/presets/slack.d.ts.map +1 -0
- package/dist/presets/slack.js +16 -0
- package/dist/presets/stripe.d.ts +8 -0
- package/dist/presets/stripe.d.ts.map +1 -0
- package/dist/presets/stripe.js +23 -0
- package/dist/presets/supabase.d.ts +7 -0
- package/dist/presets/supabase.d.ts.map +1 -0
- package/dist/presets/supabase.js +18 -0
- package/dist/presets/together.d.ts +6 -0
- package/dist/presets/together.d.ts.map +1 -0
- package/dist/presets/together.js +16 -0
- package/dist/presets/twilio.d.ts +7 -0
- package/dist/presets/twilio.d.ts.map +1 -0
- package/dist/presets/twilio.js +17 -0
- package/dist/presets/vercel.d.ts +7 -0
- package/dist/presets/vercel.d.ts.map +1 -0
- package/dist/presets/vercel.js +23 -0
- package/dist/presets/xai.d.ts +7 -0
- package/dist/presets/xai.d.ts.map +1 -0
- package/dist/presets/xai.js +17 -0
- package/dist/protocols/ftp.d.ts +63 -0
- package/dist/protocols/ftp.d.ts.map +1 -0
- package/dist/protocols/ftp.js +388 -0
- package/dist/protocols/index.d.ts +4 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +3 -0
- package/dist/protocols/sftp.d.ts +65 -0
- package/dist/protocols/sftp.d.ts.map +1 -0
- package/dist/protocols/sftp.js +346 -0
- package/dist/protocols/telnet.d.ts +50 -0
- package/dist/protocols/telnet.d.ts.map +1 -0
- package/dist/protocols/telnet.js +139 -0
- package/dist/runner/request-runner.d.ts.map +1 -1
- package/dist/runner/request-runner.js +1 -0
- package/dist/scrape/document.d.ts +44 -0
- package/dist/scrape/document.d.ts.map +1 -0
- package/dist/scrape/document.js +198 -0
- package/dist/scrape/element.d.ts +50 -0
- package/dist/scrape/element.d.ts.map +1 -0
- package/dist/scrape/element.js +176 -0
- package/dist/scrape/extractors.d.ts +17 -0
- package/dist/scrape/extractors.d.ts.map +1 -0
- package/dist/scrape/extractors.js +356 -0
- package/dist/scrape/index.d.ts +5 -0
- package/dist/scrape/index.d.ts.map +1 -0
- package/dist/scrape/index.js +3 -0
- package/dist/scrape/types.d.ts +108 -0
- package/dist/scrape/types.d.ts.map +1 -0
- package/dist/scrape/types.js +1 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/mock.d.ts +58 -0
- package/dist/testing/mock.d.ts.map +1 -0
- package/dist/testing/mock.js +252 -0
- package/dist/transport/fetch.d.ts.map +1 -1
- package/dist/transport/fetch.js +12 -4
- package/dist/transport/undici.d.ts +17 -1
- package/dist/transport/undici.d.ts.map +1 -1
- package/dist/transport/undici.js +708 -47
- package/dist/types/index.d.ts +111 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/logger.d.ts +17 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/logger.js +66 -0
- package/dist/utils/agent-manager.d.ts.map +1 -1
- package/dist/utils/agent-manager.js +20 -4
- package/dist/utils/body.d.ts.map +1 -1
- package/dist/utils/body.js +14 -2
- package/dist/utils/charset.d.ts +16 -0
- package/dist/utils/charset.d.ts.map +1 -0
- package/dist/utils/charset.js +169 -0
- package/dist/utils/client-pool.d.ts +21 -0
- package/dist/utils/client-pool.d.ts.map +1 -0
- package/dist/utils/client-pool.js +49 -0
- package/dist/utils/concurrency.d.ts.map +1 -1
- package/dist/utils/concurrency.js +8 -4
- package/dist/utils/dns-toolkit.d.ts +13 -0
- package/dist/utils/dns-toolkit.d.ts.map +1 -0
- package/dist/utils/dns-toolkit.js +48 -0
- package/dist/utils/doh.d.ts.map +1 -1
- package/dist/utils/doh.js +16 -3
- package/dist/utils/download.d.ts +15 -0
- package/dist/utils/download.d.ts.map +1 -0
- package/dist/utils/download.js +44 -0
- package/dist/utils/env-proxy.d.ts +13 -0
- package/dist/utils/env-proxy.d.ts.map +1 -0
- package/dist/utils/env-proxy.js +105 -0
- package/dist/utils/header-parser.d.ts +15 -1
- package/dist/utils/header-parser.d.ts.map +1 -1
- package/dist/utils/header-parser.js +161 -1
- package/dist/utils/link-header.d.ts +70 -0
- package/dist/utils/link-header.d.ts.map +1 -0
- package/dist/utils/link-header.js +190 -0
- package/dist/utils/progress.d.ts +7 -2
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +48 -15
- package/dist/utils/rdap.d.ts +17 -0
- package/dist/utils/rdap.d.ts.map +1 -0
- package/dist/utils/rdap.js +32 -0
- package/dist/utils/request-pool.d.ts.map +1 -1
- package/dist/utils/request-pool.js +4 -3
- package/dist/utils/sse.d.ts.map +1 -1
- package/dist/utils/sse.js +8 -2
- package/dist/utils/status-codes.d.ts +84 -0
- package/dist/utils/status-codes.d.ts.map +1 -0
- package/dist/utils/status-codes.js +204 -0
- package/dist/utils/streaming.d.ts.map +1 -1
- package/dist/utils/streaming.js +1 -0
- package/dist/utils/tls-inspector.d.ts +21 -0
- package/dist/utils/tls-inspector.d.ts.map +1 -0
- package/dist/utils/tls-inspector.js +39 -0
- package/dist/utils/try-fn.d.ts.map +1 -1
- package/dist/utils/try-fn.js +11 -5
- package/dist/utils/upload.d.ts +1 -0
- package/dist/utils/upload.d.ts.map +1 -1
- package/dist/utils/upload.js +20 -3
- package/dist/utils/user-agent.d.ts +9 -9
- package/dist/utils/user-agent.js +9 -9
- package/dist/utils/whois.d.ts.map +1 -1
- package/dist/utils/whois.js +11 -2
- package/dist/websocket/client.d.ts +29 -1
- package/dist/websocket/client.d.ts.map +1 -1
- package/dist/websocket/client.js +145 -13
- package/package.json +45 -8
package/dist/plugins/cache.js
CHANGED
|
@@ -1,72 +1,493 @@
|
|
|
1
1
|
import { HttpResponse } from '../core/response.js';
|
|
2
|
+
import { HttpRequest } from '../core/request.js';
|
|
2
3
|
import { MemoryStorage } from '../cache/memory-storage.js';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
function parseRequestCacheControl(header) {
|
|
6
|
+
if (!header)
|
|
7
|
+
return {};
|
|
8
|
+
const result = {};
|
|
9
|
+
const directives = header.toLowerCase().split(',').map(d => d.trim());
|
|
10
|
+
for (const directive of directives) {
|
|
11
|
+
if (directive.startsWith('max-age=')) {
|
|
12
|
+
result.maxAge = parseInt(directive.slice(8), 10);
|
|
13
|
+
}
|
|
14
|
+
else if (directive.startsWith('min-fresh=')) {
|
|
15
|
+
result.minFresh = parseInt(directive.slice(10), 10);
|
|
16
|
+
}
|
|
17
|
+
else if (directive.startsWith('max-stale')) {
|
|
18
|
+
const equals = directive.indexOf('=');
|
|
19
|
+
if (equals !== -1) {
|
|
20
|
+
result.maxStale = parseInt(directive.slice(equals + 1), 10);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
result.maxStale = Infinity;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (directive === 'only-if-cached') {
|
|
27
|
+
result.onlyIfCached = true;
|
|
28
|
+
}
|
|
29
|
+
else if (directive === 'no-cache') {
|
|
30
|
+
result.noCache = true;
|
|
31
|
+
}
|
|
32
|
+
else if (directive === 'no-store') {
|
|
33
|
+
result.noStore = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
function parseCacheControl(header) {
|
|
39
|
+
if (!header)
|
|
40
|
+
return {};
|
|
41
|
+
const result = {};
|
|
42
|
+
const directives = header.toLowerCase().split(',').map(d => d.trim());
|
|
43
|
+
for (const directive of directives) {
|
|
44
|
+
if (directive.startsWith('max-age=')) {
|
|
45
|
+
result.maxAge = parseInt(directive.slice(8), 10);
|
|
46
|
+
}
|
|
47
|
+
else if (directive.startsWith('s-maxage=')) {
|
|
48
|
+
result.sMaxAge = parseInt(directive.slice(9), 10);
|
|
49
|
+
}
|
|
50
|
+
else if (directive.startsWith('stale-while-revalidate=')) {
|
|
51
|
+
result.staleWhileRevalidate = parseInt(directive.slice(23), 10);
|
|
52
|
+
}
|
|
53
|
+
else if (directive.startsWith('stale-if-error=')) {
|
|
54
|
+
result.staleIfError = parseInt(directive.slice(15), 10);
|
|
55
|
+
}
|
|
56
|
+
else if (directive === 'no-cache') {
|
|
57
|
+
result.noCache = true;
|
|
58
|
+
}
|
|
59
|
+
else if (directive === 'no-store') {
|
|
60
|
+
result.noStore = true;
|
|
61
|
+
}
|
|
62
|
+
else if (directive === 'must-revalidate') {
|
|
63
|
+
result.mustRevalidate = true;
|
|
64
|
+
}
|
|
65
|
+
else if (directive === 'private') {
|
|
66
|
+
result.isPrivate = true;
|
|
67
|
+
}
|
|
68
|
+
else if (directive === 'public') {
|
|
69
|
+
result.isPublic = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
function isFresh(entry, now) {
|
|
75
|
+
const age = (now - entry.timestamp) / 1000;
|
|
76
|
+
if (entry.sMaxAge !== undefined) {
|
|
77
|
+
return age < entry.sMaxAge;
|
|
78
|
+
}
|
|
79
|
+
if (entry.maxAge !== undefined) {
|
|
80
|
+
return age < entry.maxAge;
|
|
81
|
+
}
|
|
82
|
+
if (entry.expires !== undefined) {
|
|
83
|
+
return now < entry.expires;
|
|
84
|
+
}
|
|
85
|
+
if (entry.lastModified) {
|
|
86
|
+
const lastModifiedTime = new Date(entry.lastModified).getTime();
|
|
87
|
+
if (!isNaN(lastModifiedTime)) {
|
|
88
|
+
const dateTime = entry.headers['date']
|
|
89
|
+
? new Date(entry.headers['date']).getTime()
|
|
90
|
+
: entry.timestamp;
|
|
91
|
+
if (!isNaN(dateTime) && dateTime > lastModifiedTime) {
|
|
92
|
+
const timeSinceModified = (dateTime - lastModifiedTime) / 1000;
|
|
93
|
+
const heuristicFreshness = timeSinceModified * 0.1;
|
|
94
|
+
return age < heuristicFreshness;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function satisfiesRequestDirectives(entry, now, reqDirectives) {
|
|
101
|
+
const age = (now - entry.timestamp) / 1000;
|
|
102
|
+
const responseMaxAge = entry.sMaxAge ?? entry.maxAge ?? Infinity;
|
|
103
|
+
const currentFreshness = responseMaxAge - age;
|
|
104
|
+
const fresh = isFresh(entry, now);
|
|
105
|
+
if (reqDirectives.maxAge !== undefined && age > reqDirectives.maxAge) {
|
|
106
|
+
return { allowed: false, reason: 'exceeds-request-max-age' };
|
|
107
|
+
}
|
|
108
|
+
if (reqDirectives.minFresh !== undefined) {
|
|
109
|
+
if (!fresh || currentFreshness < reqDirectives.minFresh) {
|
|
110
|
+
return { allowed: false, reason: 'insufficient-freshness' };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (reqDirectives.onlyIfCached) {
|
|
114
|
+
return { allowed: true };
|
|
115
|
+
}
|
|
116
|
+
if (!fresh && reqDirectives.maxStale === undefined) {
|
|
117
|
+
return { allowed: false, reason: 'stale-not-acceptable' };
|
|
118
|
+
}
|
|
119
|
+
if (!fresh && reqDirectives.maxStale !== undefined) {
|
|
120
|
+
const staleness = age - responseMaxAge;
|
|
121
|
+
if (reqDirectives.maxStale !== Infinity && staleness > reqDirectives.maxStale) {
|
|
122
|
+
return { allowed: false, reason: 'exceeds-max-stale' };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { allowed: true };
|
|
126
|
+
}
|
|
127
|
+
function canServeStale(entry, now) {
|
|
128
|
+
if (!entry.staleWhileRevalidate)
|
|
129
|
+
return false;
|
|
130
|
+
const age = (now - entry.timestamp) / 1000;
|
|
131
|
+
const maxAge = entry.sMaxAge ?? entry.maxAge ?? 0;
|
|
132
|
+
const staleTime = age - maxAge;
|
|
133
|
+
return staleTime < entry.staleWhileRevalidate;
|
|
134
|
+
}
|
|
135
|
+
function createConditionalRequest(req, entry) {
|
|
136
|
+
const conditionalHeaders = new Headers(req.headers);
|
|
137
|
+
if (entry.etag) {
|
|
138
|
+
conditionalHeaders.set('If-None-Match', entry.etag);
|
|
139
|
+
}
|
|
140
|
+
if (entry.lastModified) {
|
|
141
|
+
conditionalHeaders.set('If-Modified-Since', entry.lastModified);
|
|
142
|
+
}
|
|
143
|
+
return new HttpRequest(req.url, {
|
|
144
|
+
method: req.method,
|
|
145
|
+
headers: conditionalHeaders,
|
|
146
|
+
body: req.body,
|
|
147
|
+
signal: req.signal,
|
|
148
|
+
throwHttpErrors: false,
|
|
149
|
+
timeout: req.timeout,
|
|
150
|
+
onUploadProgress: req.onUploadProgress,
|
|
151
|
+
onDownloadProgress: req.onDownloadProgress,
|
|
152
|
+
maxResponseSize: req.maxResponseSize
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function storeCacheEntry(storage, baseKey, req, entry, ttl, keyGenerator) {
|
|
156
|
+
if (entry.vary) {
|
|
157
|
+
const varyKey = keyGenerator(req, entry.vary);
|
|
158
|
+
await storage.set(varyKey, entry, ttl);
|
|
159
|
+
const varyMarker = {
|
|
160
|
+
...entry,
|
|
161
|
+
body: '',
|
|
162
|
+
};
|
|
163
|
+
await storage.set(baseKey, varyMarker, ttl);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
await storage.set(baseKey, entry, ttl);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function createCacheEntry(response, body, now) {
|
|
170
|
+
const headers = {};
|
|
171
|
+
response.headers.forEach((v, k) => { headers[k] = v; });
|
|
172
|
+
const cacheControl = parseCacheControl(response.headers.get('Cache-Control'));
|
|
173
|
+
let expires;
|
|
174
|
+
const expiresHeader = response.headers.get('Expires');
|
|
175
|
+
if (expiresHeader) {
|
|
176
|
+
const expiresDate = new Date(expiresHeader);
|
|
177
|
+
if (!isNaN(expiresDate.getTime())) {
|
|
178
|
+
expires = expiresDate.getTime();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
status: response.status,
|
|
183
|
+
statusText: response.statusText,
|
|
184
|
+
headers,
|
|
185
|
+
body,
|
|
186
|
+
timestamp: now,
|
|
187
|
+
etag: response.headers.get('ETag') || undefined,
|
|
188
|
+
lastModified: response.headers.get('Last-Modified') || undefined,
|
|
189
|
+
vary: response.headers.get('Vary') || undefined,
|
|
190
|
+
expires,
|
|
191
|
+
...cacheControl
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function createCachedResponse(entry, cacheStatus) {
|
|
195
|
+
const headers = new Headers(entry.headers);
|
|
196
|
+
headers.set('X-Cache', cacheStatus);
|
|
197
|
+
headers.set('X-Cache-Age', String(Math.floor((Date.now() - entry.timestamp) / 1000)));
|
|
198
|
+
if (cacheStatus === 'stale' || cacheStatus === 'stale-error') {
|
|
199
|
+
const warningCode = cacheStatus === 'stale' ? 110 : 111;
|
|
200
|
+
const warningText = cacheStatus === 'stale' ? 'Response is Stale' : 'Revalidation Failed';
|
|
201
|
+
const existingWarning = headers.get('Warning');
|
|
202
|
+
if (existingWarning) {
|
|
203
|
+
headers.set('Warning', `${existingWarning}, ${warningCode} - "${warningText}"`);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
headers.set('Warning', `${warningCode} - "${warningText}"`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const response = new Response(entry.body, {
|
|
210
|
+
status: entry.status,
|
|
211
|
+
statusText: entry.statusText,
|
|
212
|
+
headers
|
|
213
|
+
});
|
|
214
|
+
const httpResponse = new HttpResponse(response);
|
|
215
|
+
return httpResponse;
|
|
216
|
+
}
|
|
3
217
|
export function cache(options = {}) {
|
|
4
218
|
const storage = options.storage || new MemoryStorage();
|
|
5
219
|
const strategy = options.strategy || 'cache-first';
|
|
6
220
|
const ttl = options.ttl || 60 * 1000;
|
|
7
221
|
const methods = options.methods || ['GET'];
|
|
8
|
-
const
|
|
9
|
-
|
|
222
|
+
const respectCacheControl = options.respectCacheControl !== false;
|
|
223
|
+
const forceRevalidate = options.forceRevalidate === true;
|
|
224
|
+
const generateKey = options.keyGenerator || ((req, varyHeaders) => {
|
|
225
|
+
let key = `${req.method}:${req.url}`;
|
|
226
|
+
if (req.method !== 'GET' && req.method !== 'HEAD' && req.body) {
|
|
227
|
+
try {
|
|
228
|
+
const bodyStr = typeof req.body === 'string'
|
|
229
|
+
? req.body
|
|
230
|
+
: (req.body instanceof Buffer ? req.body.toString() : JSON.stringify(req.body));
|
|
231
|
+
if (bodyStr) {
|
|
232
|
+
const hash = createHash('sha256').update(bodyStr).digest('hex').substring(0, 16);
|
|
233
|
+
key += `:${hash}`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (varyHeaders && options.respectVary !== false) {
|
|
240
|
+
const varyHeaderNames = varyHeaders.split(',').map(h => h.trim().toLowerCase());
|
|
241
|
+
const varyParts = [];
|
|
242
|
+
for (const headerName of varyHeaderNames) {
|
|
243
|
+
if (headerName === '*') {
|
|
244
|
+
return `${key}:vary-*:${Date.now()}`;
|
|
245
|
+
}
|
|
246
|
+
const headerValue = req.headers.get(headerName);
|
|
247
|
+
if (headerValue) {
|
|
248
|
+
varyParts.push(`${headerName}=${headerValue}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (varyParts.length > 0) {
|
|
252
|
+
key += `:vary:${varyParts.join('|')}`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return key;
|
|
10
256
|
});
|
|
11
257
|
const cacheMiddleware = async (req, next) => {
|
|
258
|
+
const unsafeMethods = [
|
|
259
|
+
'POST', 'PUT', 'PATCH', 'DELETE',
|
|
260
|
+
'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK',
|
|
261
|
+
'LINK', 'UNLINK', 'PURGE'
|
|
262
|
+
];
|
|
263
|
+
if (unsafeMethods.includes(req.method)) {
|
|
264
|
+
const response = await next(req);
|
|
265
|
+
if (response.ok) {
|
|
266
|
+
await storage.delete(`GET:${req.url}`);
|
|
267
|
+
await storage.delete(`HEAD:${req.url}`);
|
|
268
|
+
}
|
|
269
|
+
return response;
|
|
270
|
+
}
|
|
12
271
|
if (!methods.includes(req.method)) {
|
|
13
272
|
return next(req);
|
|
14
273
|
}
|
|
15
|
-
const
|
|
16
|
-
|
|
274
|
+
const reqCacheControl = respectCacheControl
|
|
275
|
+
? parseRequestCacheControl(req.headers.get('Cache-Control'))
|
|
276
|
+
: {};
|
|
277
|
+
if (respectCacheControl && reqCacheControl.noStore) {
|
|
17
278
|
return next(req);
|
|
18
279
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
280
|
+
if (respectCacheControl) {
|
|
281
|
+
const reqPragma = req.headers.get('Pragma');
|
|
282
|
+
if (reqPragma?.includes('no-cache')) {
|
|
283
|
+
return next(req);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const baseKey = generateKey(req);
|
|
287
|
+
let key = baseKey;
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
let cachedEntry;
|
|
290
|
+
if (options.respectVary !== false) {
|
|
291
|
+
const baseEntry = await storage.get(baseKey);
|
|
292
|
+
if (baseEntry?.vary) {
|
|
293
|
+
key = generateKey(req, baseEntry.vary);
|
|
294
|
+
cachedEntry = await storage.get(key);
|
|
295
|
+
}
|
|
296
|
+
else if (baseEntry && baseEntry.body) {
|
|
297
|
+
cachedEntry = baseEntry;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
cachedEntry = undefined;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
cachedEntry = await storage.get(key);
|
|
305
|
+
}
|
|
306
|
+
if (reqCacheControl.onlyIfCached) {
|
|
307
|
+
if (!cachedEntry) {
|
|
308
|
+
const response = new Response(null, {
|
|
309
|
+
status: 504,
|
|
310
|
+
statusText: 'Gateway Timeout'
|
|
311
|
+
});
|
|
312
|
+
return new HttpResponse(response);
|
|
313
|
+
}
|
|
314
|
+
const satisfaction = satisfiesRequestDirectives(cachedEntry, now, reqCacheControl);
|
|
315
|
+
if (!satisfaction.allowed) {
|
|
316
|
+
const response = new Response(null, {
|
|
317
|
+
status: 504,
|
|
318
|
+
statusText: 'Gateway Timeout'
|
|
26
319
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
320
|
+
return new HttpResponse(response);
|
|
321
|
+
}
|
|
322
|
+
return createCachedResponse(cachedEntry, isFresh(cachedEntry, now) ? 'hit' : 'stale');
|
|
323
|
+
}
|
|
324
|
+
const hasRequestDirectives = Object.keys(reqCacheControl).length > 0;
|
|
325
|
+
if (cachedEntry && respectCacheControl && hasRequestDirectives && strategy === 'network-first') {
|
|
326
|
+
const satisfaction = satisfiesRequestDirectives(cachedEntry, now, reqCacheControl);
|
|
327
|
+
if (!satisfaction.allowed) {
|
|
328
|
+
cachedEntry = undefined;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (strategy === 'network-only') {
|
|
332
|
+
const response = await next(req);
|
|
333
|
+
if (response.ok && respectCacheControl) {
|
|
334
|
+
const cacheControl = parseCacheControl(response.headers.get('Cache-Control'));
|
|
335
|
+
if (!cacheControl.noStore) {
|
|
336
|
+
const clonedResponse = response.raw.clone();
|
|
337
|
+
const body = await clonedResponse.text();
|
|
338
|
+
const entry = await createCacheEntry(response, body, now);
|
|
339
|
+
const entryTtl = (entry.sMaxAge ?? entry.maxAge ?? ttl / 1000) * 1000;
|
|
340
|
+
await storeCacheEntry(storage, baseKey, req, entry, entryTtl, generateKey);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return response;
|
|
344
|
+
}
|
|
345
|
+
if (strategy === 'rfc-compliant' || strategy === 'revalidate') {
|
|
346
|
+
if (cachedEntry) {
|
|
347
|
+
const fresh = isFresh(cachedEntry, now);
|
|
348
|
+
const satisfaction = satisfiesRequestDirectives(cachedEntry, now, reqCacheControl);
|
|
349
|
+
const hasValidators = cachedEntry.etag || cachedEntry.lastModified;
|
|
350
|
+
if (!satisfaction.allowed && !hasValidators) {
|
|
351
|
+
cachedEntry = undefined;
|
|
352
|
+
}
|
|
353
|
+
else if (fresh && !forceRevalidate && !cachedEntry.noCache && !reqCacheControl.noCache) {
|
|
354
|
+
return createCachedResponse(cachedEntry, 'hit');
|
|
355
|
+
}
|
|
356
|
+
else if (!fresh && reqCacheControl.maxStale !== undefined && !forceRevalidate && !reqCacheControl.noCache) {
|
|
357
|
+
return createCachedResponse(cachedEntry, 'stale');
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (cachedEntry) {
|
|
361
|
+
console.log('[DEBUG EXTREME] Revalidating. Entry:', !!cachedEntry);
|
|
362
|
+
const conditionalReq = createConditionalRequest(req, cachedEntry);
|
|
363
|
+
try {
|
|
364
|
+
console.log('[DEBUG EXTREME] Calling next(conditionalReq)');
|
|
365
|
+
const response = await next(conditionalReq);
|
|
366
|
+
console.log('[DEBUG EXTREME] Returned from next. Status:', response.status);
|
|
367
|
+
if (response.status === 304) {
|
|
368
|
+
console.log('[DEBUG EXTREME] Status is 304. Updating cache.');
|
|
369
|
+
const updatedEntry = {
|
|
370
|
+
...cachedEntry,
|
|
371
|
+
timestamp: now,
|
|
372
|
+
etag: response.headers.get('ETag') || cachedEntry.etag,
|
|
373
|
+
lastModified: response.headers.get('Last-Modified') || cachedEntry.lastModified,
|
|
374
|
+
};
|
|
375
|
+
const freshnessTtl = (updatedEntry.sMaxAge ?? updatedEntry.maxAge ?? ttl / 1000) * 1000;
|
|
376
|
+
const storageTtl = Math.max(freshnessTtl, ttl);
|
|
377
|
+
await storage.set(key, updatedEntry, storageTtl);
|
|
378
|
+
return createCachedResponse(updatedEntry, 'revalidated');
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
console.log('[DEBUG EXTREME] Status is NOT 304. Status:', response.status);
|
|
382
|
+
}
|
|
383
|
+
if (response.ok) {
|
|
384
|
+
const cacheControl = parseCacheControl(response.headers.get('Cache-Control'));
|
|
385
|
+
if (!cacheControl.noStore) {
|
|
386
|
+
const clonedResponse = response.raw.clone();
|
|
387
|
+
const body = await clonedResponse.text();
|
|
388
|
+
const entry = await createCacheEntry(response, body, now);
|
|
389
|
+
const freshnessTtl = (entry.sMaxAge ?? entry.maxAge ?? ttl / 1000) * 1000;
|
|
390
|
+
const storageTtl = Math.max(freshnessTtl, ttl);
|
|
391
|
+
await storeCacheEntry(storage, baseKey, req, entry, storageTtl, generateKey);
|
|
44
392
|
}
|
|
45
|
-
|
|
46
|
-
|
|
393
|
+
}
|
|
394
|
+
return response;
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (cachedEntry.staleIfError && canServeStale(cachedEntry, now)) {
|
|
398
|
+
return createCachedResponse(cachedEntry, 'stale-error');
|
|
399
|
+
}
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const response = await next(req);
|
|
404
|
+
if (response.ok) {
|
|
405
|
+
const cacheControl = parseCacheControl(response.headers.get('Cache-Control'));
|
|
406
|
+
if (!cacheControl.noStore) {
|
|
407
|
+
const clonedResponse = response.raw.clone();
|
|
408
|
+
const body = await clonedResponse.text();
|
|
409
|
+
const entry = await createCacheEntry(response, body, now);
|
|
410
|
+
const freshnessTtl = (entry.sMaxAge ?? entry.maxAge ?? ttl / 1000) * 1000;
|
|
411
|
+
const storageTtl = Math.max(freshnessTtl, ttl);
|
|
412
|
+
await storeCacheEntry(storage, baseKey, req, entry, storageTtl, generateKey);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return response;
|
|
416
|
+
}
|
|
417
|
+
if (strategy === 'cache-first') {
|
|
418
|
+
if (cachedEntry) {
|
|
419
|
+
return createCachedResponse(cachedEntry, 'hit');
|
|
420
|
+
}
|
|
421
|
+
const response = await next(req);
|
|
422
|
+
if (response.ok) {
|
|
423
|
+
const clonedResponse = response.raw.clone();
|
|
424
|
+
const body = await clonedResponse.text();
|
|
425
|
+
const entry = await createCacheEntry(response, body, now);
|
|
426
|
+
await storeCacheEntry(storage, baseKey, req, entry, ttl, generateKey);
|
|
427
|
+
}
|
|
428
|
+
return response;
|
|
429
|
+
}
|
|
430
|
+
if (strategy === 'stale-while-revalidate') {
|
|
431
|
+
if (cachedEntry) {
|
|
432
|
+
const cachedResponse = createCachedResponse(cachedEntry, 'stale');
|
|
433
|
+
(async () => {
|
|
434
|
+
try {
|
|
435
|
+
const conditionalReq = createConditionalRequest(req, cachedEntry);
|
|
436
|
+
const freshResponse = await next(conditionalReq);
|
|
437
|
+
if (freshResponse.status === 304) {
|
|
438
|
+
const updatedEntry = {
|
|
439
|
+
...cachedEntry,
|
|
440
|
+
timestamp: Date.now()
|
|
441
|
+
};
|
|
442
|
+
await storage.set(key, updatedEntry, ttl);
|
|
443
|
+
}
|
|
444
|
+
else if (freshResponse.ok) {
|
|
445
|
+
const clonedResponse = freshResponse.raw.clone();
|
|
446
|
+
const body = await clonedResponse.text();
|
|
447
|
+
const entry = await createCacheEntry(freshResponse, body, Date.now());
|
|
448
|
+
const freshnessTtl = (entry.sMaxAge ?? entry.maxAge ?? ttl / 1000) * 1000;
|
|
449
|
+
const storageTtl = Math.max(freshnessTtl, ttl);
|
|
450
|
+
await storeCacheEntry(storage, baseKey, req, entry, storageTtl, generateKey);
|
|
47
451
|
}
|
|
48
|
-
}
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
}
|
|
455
|
+
})();
|
|
456
|
+
return cachedResponse;
|
|
457
|
+
}
|
|
458
|
+
const response = await next(req);
|
|
459
|
+
if (response.ok) {
|
|
460
|
+
const clonedResponse = response.raw.clone();
|
|
461
|
+
const body = await clonedResponse.text();
|
|
462
|
+
const entry = await createCacheEntry(response, body, now);
|
|
463
|
+
await storeCacheEntry(storage, baseKey, req, entry, ttl, generateKey);
|
|
464
|
+
}
|
|
465
|
+
return response;
|
|
466
|
+
}
|
|
467
|
+
if (strategy === 'network-first') {
|
|
468
|
+
try {
|
|
469
|
+
const response = await next(req);
|
|
470
|
+
if (response.ok) {
|
|
471
|
+
const clonedResponse = response.raw.clone();
|
|
472
|
+
const body = await clonedResponse.text();
|
|
473
|
+
const entry = await createCacheEntry(response, body, now);
|
|
474
|
+
const freshnessTtl = (entry.sMaxAge ?? entry.maxAge ?? ttl / 1000) * 1000;
|
|
475
|
+
const storageTtl = Math.max(freshnessTtl, ttl);
|
|
476
|
+
await storeCacheEntry(storage, baseKey, req, entry, storageTtl, generateKey);
|
|
477
|
+
}
|
|
478
|
+
return response;
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
if (cachedEntry) {
|
|
482
|
+
return createCachedResponse(cachedEntry, 'stale-error');
|
|
49
483
|
}
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (response.ok) {
|
|
55
|
-
const clonedResponse = response.raw.clone();
|
|
56
|
-
const text = await clonedResponse.text();
|
|
57
|
-
const headers = {};
|
|
58
|
-
response.headers.forEach((v, k) => headers[k] = v);
|
|
59
|
-
await storage.set(key, {
|
|
60
|
-
status: response.status,
|
|
61
|
-
statusText: response.statusText,
|
|
62
|
-
headers: headers,
|
|
63
|
-
body: text,
|
|
64
|
-
timestamp: Date.now(),
|
|
65
|
-
}, ttl);
|
|
66
|
-
}
|
|
67
|
-
return response;
|
|
484
|
+
throw error;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return next(req);
|
|
68
488
|
};
|
|
69
489
|
return (client) => {
|
|
70
490
|
client.use(cacheMiddleware);
|
|
71
491
|
};
|
|
72
492
|
}
|
|
493
|
+
export { parseCacheControl };
|
|
@@ -8,7 +8,7 @@ export class CircuitBreakerError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
export function circuitBreaker(options = {}) {
|
|
10
10
|
const threshold = options.threshold || 5;
|
|
11
|
-
const resetTimeout = options.resetTimeout ||
|
|
11
|
+
const resetTimeout = options.resetTimeout || 30 * 1000;
|
|
12
12
|
const circuits = new Map();
|
|
13
13
|
const getCircuitKey = (req) => {
|
|
14
14
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../../src/plugins/compression.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../../src/plugins/compression.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAOnE,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;AAsI7D,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAmExE;AAKD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,OAAO,GAAG,kBAAkB,GACnC,UAAU,GAAG,IAAI,CAUnB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { gzip, deflate, brotliCompress } from 'node:zlib';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
|
+
import { ReckerError } from '../core/errors.js';
|
|
3
4
|
const gzipAsync = promisify(gzip);
|
|
4
5
|
const deflateAsync = promisify(deflate);
|
|
5
6
|
const brotliAsync = promisify(brotliCompress);
|
|
@@ -71,7 +72,7 @@ async function compress(data, algorithm) {
|
|
|
71
72
|
case 'br':
|
|
72
73
|
return await brotliAsync(data);
|
|
73
74
|
default:
|
|
74
|
-
throw new
|
|
75
|
+
throw new ReckerError(`Unsupported compression algorithm: ${algorithm}`, undefined, undefined, ['Use one of: gzip, br, deflate.', 'Remove compression setting or switch to a supported algorithm.']);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
export function compression(options = {}) {
|
|
@@ -111,8 +112,7 @@ export function compression(options = {}) {
|
|
|
111
112
|
};
|
|
112
113
|
return next(compressedReq);
|
|
113
114
|
}
|
|
114
|
-
catch
|
|
115
|
-
console.error('Compression failed:', error);
|
|
115
|
+
catch {
|
|
116
116
|
return next(req);
|
|
117
117
|
}
|
|
118
118
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../../src/plugins/dedup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,MAAM,CAAC;CAC/C;
|
|
1
|
+
{"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../../src/plugins/dedup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,MAAM,CAAC;CAC/C;AAKD,wBAAgB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,MAAM,CAmExD"}
|
package/dist/plugins/dedup.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const keyBuffer = { method: '', url: '' };
|
|
1
2
|
export function dedup(options = {}) {
|
|
2
3
|
const pendingRequests = new Map();
|
|
3
4
|
const generateKey = options.keyGenerator || ((req) => {
|
|
4
|
-
return
|
|
5
|
+
return req.method + ':' + req.url;
|
|
5
6
|
});
|
|
6
7
|
const dedupMiddleware = async (req, next) => {
|
|
7
8
|
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Plugin, ReckerResponse } from '../types/index.js';
|
|
1
|
+
import { Plugin, ReckerRequest, ReckerResponse } from '../types/index.js';
|
|
2
2
|
import { Client } from '../core/client.js';
|
|
3
|
-
|
|
3
|
+
import { ReckerError } from '../core/errors.js';
|
|
4
|
+
export declare class GraphQLError extends ReckerError {
|
|
4
5
|
errors: any[];
|
|
5
6
|
response: ReckerResponse;
|
|
6
|
-
constructor(errors: any[], response: ReckerResponse);
|
|
7
|
+
constructor(errors: any[], response: ReckerResponse, request?: ReckerRequest);
|
|
7
8
|
}
|
|
8
9
|
export interface GraphQLOptions {
|
|
9
10
|
throwOnErrors?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/plugins/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/plugins/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,qBAAa,YAAa,SAAQ,WAAW;IACxB,MAAM,EAAE,GAAG,EAAE;IAAS,QAAQ,EAAE,cAAc;gBAA9C,MAAM,EAAE,GAAG,EAAE,EAAS,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,aAAa;CAU3F;AAED,MAAM,WAAW,cAAc;IAE7B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAOD,wBAAgB,aAAa,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,CA4BlE;AAQD,wBAAsB,OAAO,CAAC,CAAC,GAAG,GAAG,EACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACnC,OAAO,GAAE,GAAQ,GAClB,OAAO,CAAC,CAAC,CAAC,CAuBZ"}
|
package/dist/plugins/graphql.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { ReckerError } from '../core/errors.js';
|
|
2
|
+
export class GraphQLError extends ReckerError {
|
|
2
3
|
errors;
|
|
3
4
|
response;
|
|
4
|
-
constructor(errors, response) {
|
|
5
|
-
|
|
5
|
+
constructor(errors, response, request) {
|
|
6
|
+
const message = errors?.[0]?.message || 'GraphQL response contains errors';
|
|
7
|
+
const suggestions = [
|
|
8
|
+
'Check the GraphQL query and variables for schema compliance.',
|
|
9
|
+
'Inspect the GraphQL errors array for details.',
|
|
10
|
+
'Fix validation errors before retrying; network errors may be retriable.'
|
|
11
|
+
];
|
|
12
|
+
super(message, request, response, suggestions, false);
|
|
6
13
|
this.errors = errors;
|
|
7
14
|
this.response = response;
|
|
8
15
|
this.name = 'GraphQLError';
|
|
@@ -33,7 +40,19 @@ export function graphqlPlugin(options = {}) {
|
|
|
33
40
|
};
|
|
34
41
|
}
|
|
35
42
|
export async function graphql(client, query, variables = {}, options = {}) {
|
|
36
|
-
const
|
|
37
|
-
const
|
|
43
|
+
const opMatch = query.match(/(query|mutation|subscription)\s+([a-zA-Z0-9_]+)/);
|
|
44
|
+
const operationName = opMatch ? opMatch[2] : undefined;
|
|
45
|
+
const payload = { query, variables, operationName };
|
|
46
|
+
if (options.method === 'GET') {
|
|
47
|
+
const params = {
|
|
48
|
+
query,
|
|
49
|
+
variables: JSON.stringify(variables),
|
|
50
|
+
...(operationName && { operationName })
|
|
51
|
+
};
|
|
52
|
+
options.params = { ...options.params, ...params };
|
|
53
|
+
const res = await client.get('', options).json();
|
|
54
|
+
return res.data;
|
|
55
|
+
}
|
|
56
|
+
const res = await client.post('', payload, options).json();
|
|
38
57
|
return res.data;
|
|
39
58
|
}
|