@newmo/graphql-codegen-fake-server-client 0.22.0 → 0.23.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/graphql-codegen-fake-server-client.js +89 -207
- package/dist/runtime/fake-client-runtime.js +141 -0
- package/dist/runtime-template.js +175 -0
- package/dist/template-helpers.js +291 -0
- package/dist/templates/method-generators.js +294 -0
- package/dist/templates/runtime.js +175 -0
- package/package.json +2 -2
- package/src/graphql-codegen-fake-server-client.ts +192 -276
- package/src/templates/method-generators.ts +324 -0
- package/src/templates/runtime.ts +172 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRuntimeCode = getRuntimeCode;
|
|
4
|
+
/**
|
|
5
|
+
* Returns the runtime code as a string template
|
|
6
|
+
* This is used to embed the runtime code in the generated client
|
|
7
|
+
*/
|
|
8
|
+
function getRuntimeCode() {
|
|
9
|
+
return `// Runtime utilities for generated fake client
|
|
10
|
+
export type CreateFakeClientOptions = {
|
|
11
|
+
/**
|
|
12
|
+
* The URL of the fake server
|
|
13
|
+
* @example 'http://localhost:4000/fake'
|
|
14
|
+
*/
|
|
15
|
+
fakeServerEndpoint: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Request queue implementation for rate limiting
|
|
19
|
+
class RequestQueue {
|
|
20
|
+
private queue: Array<() => Promise<any>> = [];
|
|
21
|
+
private running = 0;
|
|
22
|
+
private maxConcurrent: number = 10; // Reduced default for better stability
|
|
23
|
+
private requestDelay: number = 10; // Small delay to prevent overwhelming the server
|
|
24
|
+
private lastRequestTime = 0;
|
|
25
|
+
|
|
26
|
+
async add<T>(fn: () => Promise<T>): Promise<T> {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
this.queue.push(async () => {
|
|
29
|
+
try {
|
|
30
|
+
// Apply request delay if configured
|
|
31
|
+
if (this.requestDelay > 0) {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
const timeSinceLastRequest = now - this.lastRequestTime;
|
|
34
|
+
if (timeSinceLastRequest < this.requestDelay) {
|
|
35
|
+
await new Promise(r => setTimeout(r, this.requestDelay - timeSinceLastRequest));
|
|
36
|
+
}
|
|
37
|
+
this.lastRequestTime = Date.now();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result = await fn();
|
|
41
|
+
resolve(result);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
reject(error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
this.process();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async process() {
|
|
51
|
+
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.running++;
|
|
56
|
+
const fn = this.queue.shift();
|
|
57
|
+
if (fn) {
|
|
58
|
+
await fn();
|
|
59
|
+
this.running--;
|
|
60
|
+
this.process();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Retry helper function with exponential backoff
|
|
66
|
+
async function fetchWithRetry(
|
|
67
|
+
url: string,
|
|
68
|
+
options: RequestInit
|
|
69
|
+
): Promise<Response> {
|
|
70
|
+
const maxAttempts = 3;
|
|
71
|
+
const initialDelay = 100;
|
|
72
|
+
const maxDelay = 2000;
|
|
73
|
+
const backoffFactor = 2;
|
|
74
|
+
|
|
75
|
+
// Apply HTTP options with sensible defaults
|
|
76
|
+
const fetchOptions: RequestInit = {
|
|
77
|
+
...options,
|
|
78
|
+
// Enable keepalive for connection reuse
|
|
79
|
+
keepalive: true,
|
|
80
|
+
// Set a reasonable timeout (30 seconds)
|
|
81
|
+
signal: AbortSignal.timeout(30000),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
let lastError: Error | undefined;
|
|
85
|
+
let lastResponse: Response | undefined;
|
|
86
|
+
|
|
87
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(url, fetchOptions);
|
|
90
|
+
lastResponse = response;
|
|
91
|
+
|
|
92
|
+
// Success (2xx) or client error (4xx) - don't retry
|
|
93
|
+
if (response.status < 500) {
|
|
94
|
+
return response;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Server error (5xx) - should retry
|
|
98
|
+
if (attempt < maxAttempts - 1) {
|
|
99
|
+
const requestInfo = {
|
|
100
|
+
url,
|
|
101
|
+
status: response.status,
|
|
102
|
+
statusText: response.statusText,
|
|
103
|
+
attempt: attempt + 1,
|
|
104
|
+
maxAttempts,
|
|
105
|
+
operationName: JSON.parse(options.body as string)?.operationName,
|
|
106
|
+
sequenceId: (options.headers as any)?.['sequence-id'],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
console.error(\`[FakeClient] Server error, will retry:\`, requestInfo);
|
|
110
|
+
|
|
111
|
+
// Calculate delay with exponential backoff and jitter
|
|
112
|
+
const baseDelay = Math.min(initialDelay * Math.pow(backoffFactor, attempt), maxDelay);
|
|
113
|
+
const jitter = Math.random() * 0.1 * baseDelay; // 10% jitter
|
|
114
|
+
const delay = baseDelay + jitter;
|
|
115
|
+
|
|
116
|
+
console.log(\`[FakeClient] Retrying in \${Math.round(delay)}ms...\`);
|
|
117
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Last attempt and still server error
|
|
122
|
+
return response;
|
|
123
|
+
|
|
124
|
+
} catch (error) {
|
|
125
|
+
lastError = error as Error;
|
|
126
|
+
|
|
127
|
+
// Determine if error is retryable
|
|
128
|
+
let shouldRetry = false;
|
|
129
|
+
let errorType = 'unknown';
|
|
130
|
+
|
|
131
|
+
if (error instanceof TypeError) {
|
|
132
|
+
// Network errors from fetch (connection failures)
|
|
133
|
+
shouldRetry = true;
|
|
134
|
+
errorType = 'network';
|
|
135
|
+
} else if (error instanceof Error && error.name === 'AbortError') {
|
|
136
|
+
// Timeout errors
|
|
137
|
+
shouldRetry = true;
|
|
138
|
+
errorType = 'timeout';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const requestInfo = {
|
|
142
|
+
url,
|
|
143
|
+
errorType,
|
|
144
|
+
error: error instanceof Error ? error.message : String(error),
|
|
145
|
+
attempt: attempt + 1,
|
|
146
|
+
maxAttempts,
|
|
147
|
+
operationName: JSON.parse(options.body as string)?.operationName,
|
|
148
|
+
sequenceId: (options.headers as any)?.['sequence-id'],
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
console.error(\`[FakeClient] Request failed:\`, requestInfo);
|
|
152
|
+
|
|
153
|
+
if (shouldRetry && attempt < maxAttempts - 1) {
|
|
154
|
+
// Calculate delay with exponential backoff and jitter
|
|
155
|
+
const baseDelay = Math.min(initialDelay * Math.pow(backoffFactor, attempt), maxDelay);
|
|
156
|
+
const jitter = Math.random() * 0.1 * baseDelay; // 10% jitter
|
|
157
|
+
const delay = baseDelay + jitter;
|
|
158
|
+
|
|
159
|
+
console.log(\`[FakeClient] Retrying in \${Math.round(delay)}ms...\`);
|
|
160
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Not retryable or max attempts reached
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Should not reach here, but just in case
|
|
170
|
+
if (lastResponse) {
|
|
171
|
+
return lastResponse;
|
|
172
|
+
}
|
|
173
|
+
throw new Error('Max retry attempts reached', { cause: lastError });
|
|
174
|
+
}`;
|
|
175
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newmo/graphql-codegen-fake-server-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GraphQL Codegen plugin for generating a fake server client",
|
|
6
6
|
"keywords": [
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"access": "public",
|
|
63
63
|
"registry": "https://registry.npmjs.org/"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "9f78beca5016a585f3de1ca2cc0a3ccb24fd743e"
|
|
66
66
|
}
|