@constructive-io/graphql-codegen 3.0.4 → 3.1.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/README.md +4 -0
- package/cli/index.js +4 -1
- package/cli/shared.d.ts +1 -0
- package/cli/shared.js +8 -0
- package/core/codegen/client.d.ts +11 -1
- package/core/codegen/client.js +63 -248
- package/core/codegen/index.js +3 -1
- package/core/codegen/orm/client-generator.d.ts +5 -2
- package/core/codegen/orm/client-generator.js +28 -289
- package/core/codegen/templates/client.browser.ts +265 -0
- package/core/codegen/templates/client.node.ts +350 -0
- package/core/codegen/templates/orm-client.ts +158 -0
- package/core/codegen/templates/select-types.ts +116 -0
- package/core/introspect/fetch-schema.js +10 -3
- package/esm/cli/index.js +4 -1
- package/esm/cli/shared.d.ts +1 -0
- package/esm/cli/shared.js +8 -0
- package/esm/core/codegen/client.d.ts +11 -1
- package/esm/core/codegen/client.js +31 -249
- package/esm/core/codegen/index.js +3 -1
- package/esm/core/codegen/orm/client-generator.d.ts +5 -2
- package/esm/core/codegen/orm/client-generator.js +28 -289
- package/esm/core/introspect/fetch-schema.js +10 -3
- package/esm/types/config.d.ts +8 -0
- package/esm/types/config.js +1 -0
- package/package.json +5 -5
- package/types/config.d.ts +8 -0
- package/types/config.js +1 -0
- package/core/codegen/orm/query-builder.d.ts +0 -85
- package/core/codegen/orm/query-builder.js +0 -485
- package/esm/core/codegen/orm/query-builder.d.ts +0 -85
- package/esm/core/codegen/orm/query-builder.js +0 -441
- /package/core/codegen/{orm → templates}/query-builder.ts +0 -0
package/README.md
CHANGED
|
@@ -183,6 +183,8 @@ interface GraphQLSDKConfigTarget {
|
|
|
183
183
|
// Generator flags
|
|
184
184
|
reactQuery?: boolean; // Generate React Query hooks (output: {output}/hooks)
|
|
185
185
|
orm?: boolean; // Generate ORM client (output: {output}/orm)
|
|
186
|
+
browserCompatible?: boolean; // Generate browser-compatible code (default: true)
|
|
187
|
+
// Set to false for Node.js with localhost DNS fix
|
|
186
188
|
|
|
187
189
|
// Table filtering (for CRUD operations from _meta)
|
|
188
190
|
tables?: {
|
|
@@ -683,6 +685,8 @@ Generator Options:
|
|
|
683
685
|
-t, --target <name> Target name in config file
|
|
684
686
|
-o, --output <dir> Output directory
|
|
685
687
|
-a, --authorization <token> Authorization header value
|
|
688
|
+
--browser-compatible Generate browser-compatible code (default: true)
|
|
689
|
+
Set to false for Node.js with localhost DNS fix
|
|
686
690
|
--skip-custom-operations Only generate table CRUD operations
|
|
687
691
|
--dry-run Preview without writing files
|
|
688
692
|
--keep-db Keep ephemeral database after generation (pgpm modes)
|
package/cli/index.js
CHANGED
|
@@ -33,6 +33,8 @@ Generator Options:
|
|
|
33
33
|
-o, --output <dir> Output directory
|
|
34
34
|
-t, --target <name> Target name (for multi-target configs)
|
|
35
35
|
-a, --authorization <token> Authorization header value
|
|
36
|
+
--browser-compatible Generate browser-compatible code (default: true)
|
|
37
|
+
Set to false for Node.js with localhost DNS fix
|
|
36
38
|
--dry-run Preview without writing files
|
|
37
39
|
-v, --verbose Show detailed output
|
|
38
40
|
|
|
@@ -105,6 +107,7 @@ const commands = async (argv, prompter, _options) => {
|
|
|
105
107
|
authorization: answers.authorization,
|
|
106
108
|
reactQuery: answers.reactQuery,
|
|
107
109
|
orm: answers.orm,
|
|
110
|
+
browserCompatible: answers.browserCompatible,
|
|
108
111
|
dryRun: answers.dryRun,
|
|
109
112
|
verbose: answers.verbose,
|
|
110
113
|
});
|
|
@@ -126,7 +129,7 @@ exports.options = {
|
|
|
126
129
|
v: 'verbose',
|
|
127
130
|
},
|
|
128
131
|
boolean: [
|
|
129
|
-
'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db',
|
|
132
|
+
'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db', 'browser-compatible',
|
|
130
133
|
],
|
|
131
134
|
string: [
|
|
132
135
|
'config', 'endpoint', 'schema-file', 'output', 'target', 'authorization',
|
package/cli/shared.d.ts
CHANGED
package/cli/shared.js
CHANGED
|
@@ -65,6 +65,14 @@ exports.codegenQuestions = [
|
|
|
65
65
|
default: false,
|
|
66
66
|
useDefault: true,
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
name: 'browserCompatible',
|
|
70
|
+
message: 'Generate browser-compatible code?',
|
|
71
|
+
type: 'confirm',
|
|
72
|
+
required: false,
|
|
73
|
+
default: true,
|
|
74
|
+
useDefault: true,
|
|
75
|
+
},
|
|
68
76
|
{
|
|
69
77
|
name: 'authorization',
|
|
70
78
|
message: 'Authorization header value',
|
package/core/codegen/client.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
export interface GenerateClientFileOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Generate browser-compatible code using native fetch
|
|
4
|
+
* When true (default), uses native W3C fetch API
|
|
5
|
+
* When false, uses undici fetch with dispatcher support for localhost DNS resolution
|
|
6
|
+
* @default true
|
|
7
|
+
*/
|
|
8
|
+
browserCompatible?: boolean;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* Generate client.ts content
|
|
12
|
+
* @param options - Generation options
|
|
3
13
|
*/
|
|
4
|
-
export declare function generateClientFile(): string;
|
|
14
|
+
export declare function generateClientFile(options?: GenerateClientFileOptions): string;
|
package/core/codegen/client.js
CHANGED
|
@@ -1,264 +1,79 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.generateClientFile = generateClientFile;
|
|
4
37
|
/**
|
|
5
38
|
* Client generator - generates client.ts with configure() and execute()
|
|
6
|
-
*/
|
|
7
|
-
const utils_1 = require("./utils");
|
|
8
|
-
/**
|
|
9
|
-
* Generate client.ts content
|
|
10
|
-
*/
|
|
11
|
-
function generateClientFile() {
|
|
12
|
-
return `${(0, utils_1.getGeneratedFileHeader)('GraphQL client configuration and execution')}
|
|
13
|
-
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// Configuration
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
export interface GraphQLClientConfig {
|
|
19
|
-
/** GraphQL endpoint URL */
|
|
20
|
-
endpoint: string;
|
|
21
|
-
/** Default headers to include in all requests */
|
|
22
|
-
headers?: Record<string, string>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let globalConfig: GraphQLClientConfig | null = null;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Configure the GraphQL client
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* \`\`\`ts
|
|
32
|
-
* import { configure } from './generated';
|
|
33
|
-
*
|
|
34
|
-
* configure({
|
|
35
|
-
* endpoint: 'https://api.example.com/graphql',
|
|
36
|
-
* headers: {
|
|
37
|
-
* Authorization: 'Bearer <token>',
|
|
38
|
-
* },
|
|
39
|
-
* });
|
|
40
|
-
* \`\`\`
|
|
41
|
-
*/
|
|
42
|
-
export function configure(config: GraphQLClientConfig): void {
|
|
43
|
-
globalConfig = config;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get the current configuration
|
|
48
|
-
* @throws Error if not configured
|
|
49
|
-
*/
|
|
50
|
-
export function getConfig(): GraphQLClientConfig {
|
|
51
|
-
if (!globalConfig) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
'GraphQL client not configured. Call configure() before making requests.'
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
return globalConfig;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Set a single header value
|
|
61
|
-
* Useful for updating Authorization after login
|
|
62
39
|
*
|
|
63
|
-
*
|
|
64
|
-
* \`\`\`ts
|
|
65
|
-
* setHeader('Authorization', 'Bearer <new-token>');
|
|
66
|
-
* \`\`\`
|
|
40
|
+
* Reads from template files in the templates/ directory for proper type checking.
|
|
67
41
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
...config,
|
|
72
|
-
headers: { ...config.headers, [key]: value },
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const utils_1 = require("./utils");
|
|
76
45
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* \`\`\`ts
|
|
81
|
-
* setHeaders({ Authorization: 'Bearer <token>', 'X-Custom': 'value' });
|
|
82
|
-
* \`\`\`
|
|
46
|
+
* Find a template file path.
|
|
47
|
+
* Templates are at ./templates/ relative to this file in both src/ and dist/.
|
|
83
48
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// ============================================================================
|
|
93
|
-
// Error handling
|
|
94
|
-
// ============================================================================
|
|
95
|
-
|
|
96
|
-
export interface GraphQLError {
|
|
97
|
-
message: string;
|
|
98
|
-
locations?: Array<{ line: number; column: number }>;
|
|
99
|
-
path?: Array<string | number>;
|
|
100
|
-
extensions?: Record<string, unknown>;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export class GraphQLClientError extends Error {
|
|
104
|
-
constructor(
|
|
105
|
-
message: string,
|
|
106
|
-
public errors: GraphQLError[],
|
|
107
|
-
public response?: Response
|
|
108
|
-
) {
|
|
109
|
-
super(message);
|
|
110
|
-
this.name = 'GraphQLClientError';
|
|
111
|
-
}
|
|
49
|
+
function findTemplateFile(templateName) {
|
|
50
|
+
const templatePath = path.join(__dirname, 'templates', templateName);
|
|
51
|
+
if (fs.existsSync(templatePath)) {
|
|
52
|
+
return templatePath;
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Could not find template file: ${templateName}. ` +
|
|
55
|
+
`Searched in: ${templatePath}`);
|
|
112
56
|
}
|
|
113
|
-
|
|
114
|
-
// ============================================================================
|
|
115
|
-
// Execution
|
|
116
|
-
// ============================================================================
|
|
117
|
-
|
|
118
|
-
export interface ExecuteOptions {
|
|
119
|
-
/** Override headers for this request */
|
|
120
|
-
headers?: Record<string, string>;
|
|
121
|
-
/** AbortSignal for request cancellation */
|
|
122
|
-
signal?: AbortSignal;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
57
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* \`\`\`ts
|
|
130
|
-
* const result = await execute<CarsQueryResult, CarsQueryVariables>(
|
|
131
|
-
* carsQueryDocument,
|
|
132
|
-
* { first: 10 }
|
|
133
|
-
* );
|
|
134
|
-
* \`\`\`
|
|
58
|
+
* Read a template file and replace the header with generated file header
|
|
135
59
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
method: 'POST',
|
|
145
|
-
headers: {
|
|
146
|
-
'Content-Type': 'application/json',
|
|
147
|
-
...config.headers,
|
|
148
|
-
...options?.headers,
|
|
149
|
-
},
|
|
150
|
-
body: JSON.stringify({
|
|
151
|
-
query: document,
|
|
152
|
-
variables,
|
|
153
|
-
}),
|
|
154
|
-
signal: options?.signal,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const json = await response.json();
|
|
158
|
-
|
|
159
|
-
if (json.errors && json.errors.length > 0) {
|
|
160
|
-
throw new GraphQLClientError(
|
|
161
|
-
json.errors[0].message || 'GraphQL request failed',
|
|
162
|
-
json.errors,
|
|
163
|
-
response
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return json.data as TData;
|
|
60
|
+
function readTemplateFile(templateName, description) {
|
|
61
|
+
const templatePath = findTemplateFile(templateName);
|
|
62
|
+
let content = fs.readFileSync(templatePath, 'utf-8');
|
|
63
|
+
// Replace the source file header comment with the generated file header
|
|
64
|
+
// Match the header pattern used in template files
|
|
65
|
+
const headerPattern = /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/;
|
|
66
|
+
content = content.replace(headerPattern, (0, utils_1.getGeneratedFileHeader)(description) + '\n');
|
|
67
|
+
return content;
|
|
168
68
|
}
|
|
169
|
-
|
|
170
69
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*/
|
|
174
|
-
export async function executeWithErrors<TData = unknown, TVariables = Record<string, unknown>>(
|
|
175
|
-
document: string,
|
|
176
|
-
variables?: TVariables,
|
|
177
|
-
options?: ExecuteOptions
|
|
178
|
-
): Promise<{ data: TData | null; errors: GraphQLError[] | null }> {
|
|
179
|
-
const config = getConfig();
|
|
180
|
-
|
|
181
|
-
const response = await fetch(config.endpoint, {
|
|
182
|
-
method: 'POST',
|
|
183
|
-
headers: {
|
|
184
|
-
'Content-Type': 'application/json',
|
|
185
|
-
...config.headers,
|
|
186
|
-
...options?.headers,
|
|
187
|
-
},
|
|
188
|
-
body: JSON.stringify({
|
|
189
|
-
query: document,
|
|
190
|
-
variables,
|
|
191
|
-
}),
|
|
192
|
-
signal: options?.signal,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const json = await response.json();
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
data: json.data ?? null,
|
|
199
|
-
errors: json.errors ?? null,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// ============================================================================
|
|
204
|
-
// QueryClient Factory
|
|
205
|
-
// ============================================================================
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Default QueryClient configuration optimized for GraphQL
|
|
209
|
-
*
|
|
210
|
-
* These defaults provide a good balance between freshness and performance:
|
|
211
|
-
* - staleTime: 1 minute - data considered fresh, won't refetch
|
|
212
|
-
* - gcTime: 5 minutes - unused data kept in cache
|
|
213
|
-
* - refetchOnWindowFocus: false - don't refetch when tab becomes active
|
|
214
|
-
* - retry: 1 - retry failed requests once
|
|
215
|
-
*/
|
|
216
|
-
export const defaultQueryClientOptions = {
|
|
217
|
-
defaultOptions: {
|
|
218
|
-
queries: {
|
|
219
|
-
staleTime: 1000 * 60, // 1 minute
|
|
220
|
-
gcTime: 1000 * 60 * 5, // 5 minutes
|
|
221
|
-
refetchOnWindowFocus: false,
|
|
222
|
-
retry: 1,
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* QueryClient options type for createQueryClient
|
|
70
|
+
* Generate client.ts content
|
|
71
|
+
* @param options - Generation options
|
|
229
72
|
*/
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
retry?: number | boolean;
|
|
237
|
-
retryDelay?: number | ((attemptIndex: number) => number);
|
|
238
|
-
};
|
|
239
|
-
mutations?: {
|
|
240
|
-
retry?: number | boolean;
|
|
241
|
-
retryDelay?: number | ((attemptIndex: number) => number);
|
|
242
|
-
};
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Note: createQueryClient is available when using with @tanstack/react-query
|
|
247
|
-
// Import QueryClient from '@tanstack/react-query' and use these options:
|
|
248
|
-
//
|
|
249
|
-
// import { QueryClient } from '@tanstack/react-query';
|
|
250
|
-
// const queryClient = new QueryClient(defaultQueryClientOptions);
|
|
251
|
-
//
|
|
252
|
-
// Or merge with your own options:
|
|
253
|
-
// const queryClient = new QueryClient({
|
|
254
|
-
// ...defaultQueryClientOptions,
|
|
255
|
-
// defaultOptions: {
|
|
256
|
-
// ...defaultQueryClientOptions.defaultOptions,
|
|
257
|
-
// queries: {
|
|
258
|
-
// ...defaultQueryClientOptions.defaultOptions.queries,
|
|
259
|
-
// staleTime: 30000, // Override specific options
|
|
260
|
-
// },
|
|
261
|
-
// },
|
|
262
|
-
// });
|
|
263
|
-
`;
|
|
73
|
+
function generateClientFile(options = {}) {
|
|
74
|
+
const { browserCompatible = true } = options;
|
|
75
|
+
const templateName = browserCompatible
|
|
76
|
+
? 'client.browser.ts'
|
|
77
|
+
: 'client.node.ts';
|
|
78
|
+
return readTemplateFile(templateName, 'GraphQL client configuration and execution');
|
|
264
79
|
}
|
package/core/codegen/index.js
CHANGED
|
@@ -45,7 +45,9 @@ function generate(options) {
|
|
|
45
45
|
// 1. Generate client.ts
|
|
46
46
|
files.push({
|
|
47
47
|
path: 'client.ts',
|
|
48
|
-
content: (0, client_1.generateClientFile)(
|
|
48
|
+
content: (0, client_1.generateClientFile)({
|
|
49
|
+
browserCompatible: config.browserCompatible ?? true,
|
|
50
|
+
}),
|
|
49
51
|
});
|
|
50
52
|
// Collect table type names for import path resolution
|
|
51
53
|
const tableTypeNames = new Set(tables.map((t) => (0, utils_1.getTableNames)(t).typeName));
|
|
@@ -11,17 +11,20 @@ export interface GeneratedClientFile {
|
|
|
11
11
|
/**
|
|
12
12
|
* Generate the main client.ts file (OrmClient class)
|
|
13
13
|
* This is the runtime client that handles GraphQL execution
|
|
14
|
+
*
|
|
15
|
+
* Reads from the templates directory for proper type checking.
|
|
14
16
|
*/
|
|
15
17
|
export declare function generateOrmClientFile(): GeneratedClientFile;
|
|
16
18
|
/**
|
|
17
19
|
* Generate the query-builder.ts file (runtime query builder)
|
|
18
20
|
*
|
|
19
|
-
* Reads from the
|
|
20
|
-
* which enables proper type checking and testability.
|
|
21
|
+
* Reads from the templates directory for proper type checking and testability.
|
|
21
22
|
*/
|
|
22
23
|
export declare function generateQueryBuilderFile(): GeneratedClientFile;
|
|
23
24
|
/**
|
|
24
25
|
* Generate the select-types.ts file
|
|
26
|
+
*
|
|
27
|
+
* Reads from the templates directory for proper type checking.
|
|
25
28
|
*/
|
|
26
29
|
export declare function generateSelectTypesFile(): GeneratedClientFile;
|
|
27
30
|
/**
|