@constructive-io/graphql-codegen 3.0.3 → 3.1.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.
@@ -127,6 +127,31 @@ export function generateMainBarrel(tables, options = {}) {
127
127
  }
128
128
  return generateCode(statements);
129
129
  }
130
+ /**
131
+ * Generate the root index.ts barrel file for the output directory.
132
+ * Re-exports from subdirectories based on which generators are enabled.
133
+ */
134
+ export function generateRootBarrel(options = {}) {
135
+ const { hasTypes = false, hasHooks = false, hasOrm = false } = options;
136
+ const statements = [];
137
+ if (hasTypes) {
138
+ statements.push(exportAllFrom('./types'));
139
+ }
140
+ if (hasHooks) {
141
+ statements.push(exportAllFrom('./hooks'));
142
+ }
143
+ if (hasOrm) {
144
+ statements.push(exportAllFrom('./orm'));
145
+ }
146
+ // Add file header as leading comment on first statement
147
+ if (statements.length > 0) {
148
+ addJSDocComment(statements[0], [
149
+ 'Generated SDK - auto-generated, do not edit',
150
+ '@generated by @constructive-io/graphql-codegen',
151
+ ]);
152
+ }
153
+ return generateCode(statements);
154
+ }
130
155
  // ============================================================================
131
156
  // Custom operation barrels (includes both table and custom hooks)
132
157
  // ============================================================================
@@ -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;
@@ -1,261 +1,43 @@
1
1
  /**
2
2
  * Client generator - generates client.ts with configure() and execute()
3
- */
4
- import { getGeneratedFileHeader } from './utils';
5
- /**
6
- * Generate client.ts content
7
- */
8
- export function generateClientFile() {
9
- return `${getGeneratedFileHeader('GraphQL client configuration and execution')}
10
-
11
- // ============================================================================
12
- // Configuration
13
- // ============================================================================
14
-
15
- export interface GraphQLClientConfig {
16
- /** GraphQL endpoint URL */
17
- endpoint: string;
18
- /** Default headers to include in all requests */
19
- headers?: Record<string, string>;
20
- }
21
-
22
- let globalConfig: GraphQLClientConfig | null = null;
23
-
24
- /**
25
- * Configure the GraphQL client
26
- *
27
- * @example
28
- * \`\`\`ts
29
- * import { configure } from './generated';
30
3
  *
31
- * configure({
32
- * endpoint: 'https://api.example.com/graphql',
33
- * headers: {
34
- * Authorization: 'Bearer <token>',
35
- * },
36
- * });
37
- * \`\`\`
4
+ * Reads from template files in the templates/ directory for proper type checking.
38
5
  */
39
- export function configure(config: GraphQLClientConfig): void {
40
- globalConfig = config;
41
- }
42
-
43
- /**
44
- * Get the current configuration
45
- * @throws Error if not configured
46
- */
47
- export function getConfig(): GraphQLClientConfig {
48
- if (!globalConfig) {
49
- throw new Error(
50
- 'GraphQL client not configured. Call configure() before making requests.'
51
- );
52
- }
53
- return globalConfig;
54
- }
55
-
56
- /**
57
- * Set a single header value
58
- * Useful for updating Authorization after login
59
- *
60
- * @example
61
- * \`\`\`ts
62
- * setHeader('Authorization', 'Bearer <new-token>');
63
- * \`\`\`
64
- */
65
- export function setHeader(key: string, value: string): void {
66
- const config = getConfig();
67
- globalConfig = {
68
- ...config,
69
- headers: { ...config.headers, [key]: value },
70
- };
71
- }
72
-
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { getGeneratedFileHeader } from './utils';
73
9
  /**
74
- * Merge multiple headers into the current configuration
75
- *
76
- * @example
77
- * \`\`\`ts
78
- * setHeaders({ Authorization: 'Bearer <token>', 'X-Custom': 'value' });
79
- * \`\`\`
10
+ * Find a template file path.
11
+ * Templates are at ./templates/ relative to this file in both src/ and dist/.
80
12
  */
81
- export function setHeaders(headers: Record<string, string>): void {
82
- const config = getConfig();
83
- globalConfig = {
84
- ...config,
85
- headers: { ...config.headers, ...headers },
86
- };
87
- }
88
-
89
- // ============================================================================
90
- // Error handling
91
- // ============================================================================
92
-
93
- export interface GraphQLError {
94
- message: string;
95
- locations?: Array<{ line: number; column: number }>;
96
- path?: Array<string | number>;
97
- extensions?: Record<string, unknown>;
98
- }
99
-
100
- export class GraphQLClientError extends Error {
101
- constructor(
102
- message: string,
103
- public errors: GraphQLError[],
104
- public response?: Response
105
- ) {
106
- super(message);
107
- this.name = 'GraphQLClientError';
108
- }
13
+ function findTemplateFile(templateName) {
14
+ const templatePath = path.join(__dirname, 'templates', templateName);
15
+ if (fs.existsSync(templatePath)) {
16
+ return templatePath;
17
+ }
18
+ throw new Error(`Could not find template file: ${templateName}. ` +
19
+ `Searched in: ${templatePath}`);
109
20
  }
110
-
111
- // ============================================================================
112
- // Execution
113
- // ============================================================================
114
-
115
- export interface ExecuteOptions {
116
- /** Override headers for this request */
117
- headers?: Record<string, string>;
118
- /** AbortSignal for request cancellation */
119
- signal?: AbortSignal;
120
- }
121
-
122
21
  /**
123
- * Execute a GraphQL operation
124
- *
125
- * @example
126
- * \`\`\`ts
127
- * const result = await execute<CarsQueryResult, CarsQueryVariables>(
128
- * carsQueryDocument,
129
- * { first: 10 }
130
- * );
131
- * \`\`\`
22
+ * Read a template file and replace the header with generated file header
132
23
  */
133
- export async function execute<TData = unknown, TVariables = Record<string, unknown>>(
134
- document: string,
135
- variables?: TVariables,
136
- options?: ExecuteOptions
137
- ): Promise<TData> {
138
- const config = getConfig();
139
-
140
- const response = await fetch(config.endpoint, {
141
- method: 'POST',
142
- headers: {
143
- 'Content-Type': 'application/json',
144
- ...config.headers,
145
- ...options?.headers,
146
- },
147
- body: JSON.stringify({
148
- query: document,
149
- variables,
150
- }),
151
- signal: options?.signal,
152
- });
153
-
154
- const json = await response.json();
155
-
156
- if (json.errors && json.errors.length > 0) {
157
- throw new GraphQLClientError(
158
- json.errors[0].message || 'GraphQL request failed',
159
- json.errors,
160
- response
161
- );
162
- }
163
-
164
- return json.data as TData;
24
+ function readTemplateFile(templateName, description) {
25
+ const templatePath = findTemplateFile(templateName);
26
+ let content = fs.readFileSync(templatePath, 'utf-8');
27
+ // Replace the source file header comment with the generated file header
28
+ // Match the header pattern used in template files
29
+ const headerPattern = /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/;
30
+ content = content.replace(headerPattern, getGeneratedFileHeader(description) + '\n');
31
+ return content;
165
32
  }
166
-
167
33
  /**
168
- * Execute a GraphQL operation with full response (data + errors)
169
- * Useful when you want to handle partial data with errors
170
- */
171
- export async function executeWithErrors<TData = unknown, TVariables = Record<string, unknown>>(
172
- document: string,
173
- variables?: TVariables,
174
- options?: ExecuteOptions
175
- ): Promise<{ data: TData | null; errors: GraphQLError[] | null }> {
176
- const config = getConfig();
177
-
178
- const response = await fetch(config.endpoint, {
179
- method: 'POST',
180
- headers: {
181
- 'Content-Type': 'application/json',
182
- ...config.headers,
183
- ...options?.headers,
184
- },
185
- body: JSON.stringify({
186
- query: document,
187
- variables,
188
- }),
189
- signal: options?.signal,
190
- });
191
-
192
- const json = await response.json();
193
-
194
- return {
195
- data: json.data ?? null,
196
- errors: json.errors ?? null,
197
- };
198
- }
199
-
200
- // ============================================================================
201
- // QueryClient Factory
202
- // ============================================================================
203
-
204
- /**
205
- * Default QueryClient configuration optimized for GraphQL
206
- *
207
- * These defaults provide a good balance between freshness and performance:
208
- * - staleTime: 1 minute - data considered fresh, won't refetch
209
- * - gcTime: 5 minutes - unused data kept in cache
210
- * - refetchOnWindowFocus: false - don't refetch when tab becomes active
211
- * - retry: 1 - retry failed requests once
212
- */
213
- export const defaultQueryClientOptions = {
214
- defaultOptions: {
215
- queries: {
216
- staleTime: 1000 * 60, // 1 minute
217
- gcTime: 1000 * 60 * 5, // 5 minutes
218
- refetchOnWindowFocus: false,
219
- retry: 1,
220
- },
221
- },
222
- };
223
-
224
- /**
225
- * QueryClient options type for createQueryClient
226
- */
227
- export interface CreateQueryClientOptions {
228
- defaultOptions?: {
229
- queries?: {
230
- staleTime?: number;
231
- gcTime?: number;
232
- refetchOnWindowFocus?: boolean;
233
- retry?: number | boolean;
234
- retryDelay?: number | ((attemptIndex: number) => number);
235
- };
236
- mutations?: {
237
- retry?: number | boolean;
238
- retryDelay?: number | ((attemptIndex: number) => number);
239
- };
240
- };
241
- }
242
-
243
- // Note: createQueryClient is available when using with @tanstack/react-query
244
- // Import QueryClient from '@tanstack/react-query' and use these options:
245
- //
246
- // import { QueryClient } from '@tanstack/react-query';
247
- // const queryClient = new QueryClient(defaultQueryClientOptions);
248
- //
249
- // Or merge with your own options:
250
- // const queryClient = new QueryClient({
251
- // ...defaultQueryClientOptions,
252
- // defaultOptions: {
253
- // ...defaultQueryClientOptions.defaultOptions,
254
- // queries: {
255
- // ...defaultQueryClientOptions.defaultOptions.queries,
256
- // staleTime: 30000, // Override specific options
257
- // },
258
- // },
259
- // });
260
- `;
34
+ * Generate client.ts content
35
+ * @param options - Generation options
36
+ */
37
+ export function generateClientFile(options = {}) {
38
+ const { browserCompatible = true } = options;
39
+ const templateName = browserCompatible
40
+ ? 'client.browser.ts'
41
+ : 'client.node.ts';
42
+ return readTemplateFile(templateName, 'GraphQL client configuration and execution');
261
43
  }
@@ -40,7 +40,9 @@ export function generate(options) {
40
40
  // 1. Generate client.ts
41
41
  files.push({
42
42
  path: 'client.ts',
43
- content: generateClientFile(),
43
+ content: generateClientFile({
44
+ browserCompatible: config.browserCompatible ?? true,
45
+ }),
44
46
  });
45
47
  // Collect table type names for import path resolution
46
48
  const tableTypeNames = new Set(tables.map((t) => 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 actual TypeScript file in the source directory,
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
  /**