@constructive-io/graphql-codegen 4.41.0 → 4.41.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/core/codegen/cli/executor-generator.d.ts +2 -6
- package/core/codegen/cli/executor-generator.js +12 -48
- package/core/codegen/cli/index.d.ts +0 -2
- package/core/codegen/cli/index.js +2 -16
- package/core/codegen/cli/utils-generator.d.ts +0 -8
- package/core/codegen/cli/utils-generator.js +0 -14
- package/core/codegen/orm/client-generator.d.ts +1 -3
- package/core/codegen/orm/client-generator.js +1 -1
- package/core/codegen/orm/index.js +1 -1
- package/core/generate.js +2 -19
- package/esm/core/codegen/cli/executor-generator.d.ts +2 -6
- package/esm/core/codegen/cli/executor-generator.js +12 -48
- package/esm/core/codegen/cli/index.d.ts +0 -2
- package/esm/core/codegen/cli/index.js +3 -17
- package/esm/core/codegen/cli/utils-generator.d.ts +0 -8
- package/esm/core/codegen/cli/utils-generator.js +0 -13
- package/esm/core/codegen/orm/client-generator.d.ts +1 -3
- package/esm/core/codegen/orm/client-generator.js +1 -1
- package/esm/core/codegen/orm/index.js +1 -1
- package/esm/core/generate.js +2 -19
- package/esm/types/config.d.ts +0 -18
- package/package.json +2 -2
- package/types/config.d.ts +0 -18
- package/core/codegen/templates/node-fetch.ts +0 -198
|
@@ -7,9 +7,5 @@ export interface MultiTargetExecutorInput {
|
|
|
7
7
|
endpoint: string;
|
|
8
8
|
ormImportPath: string;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
nodeHttpAdapter?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export declare function generateExecutorFile(toolName: string, options?: ExecutorOptions): GeneratedFile;
|
|
15
|
-
export declare function generateMultiTargetExecutorFile(toolName: string, targets: MultiTargetExecutorInput[], options?: ExecutorOptions): GeneratedFile;
|
|
10
|
+
export declare function generateExecutorFile(toolName: string): GeneratedFile;
|
|
11
|
+
export declare function generateMultiTargetExecutorFile(toolName: string, targets: MultiTargetExecutorInput[]): GeneratedFile;
|
|
@@ -44,12 +44,8 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
|
|
|
44
44
|
decl.importKind = typeOnly ? 'type' : 'value';
|
|
45
45
|
return decl;
|
|
46
46
|
}
|
|
47
|
-
function generateExecutorFile(toolName
|
|
47
|
+
function generateExecutorFile(toolName) {
|
|
48
48
|
const statements = [];
|
|
49
|
-
// Import NodeHttpAdapter for *.localhost subdomain routing
|
|
50
|
-
if (options?.nodeHttpAdapter) {
|
|
51
|
-
statements.push(createImportDeclaration('./node-fetch', ['NodeHttpAdapter']));
|
|
52
|
-
}
|
|
53
49
|
statements.push(createImportDeclaration('appstash', ['createConfigStore']));
|
|
54
50
|
statements.push(createImportDeclaration('../orm', ['createClient']));
|
|
55
51
|
statements.push(t.variableDeclaration('const', [
|
|
@@ -109,26 +105,12 @@ function generateExecutorFile(toolName, options) {
|
|
|
109
105
|
]))),
|
|
110
106
|
])),
|
|
111
107
|
])),
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
t.
|
|
116
|
-
t.objectExpression([
|
|
117
|
-
t.objectProperty(t.identifier('adapter'), t.newExpression(t.identifier('NodeHttpAdapter'), [
|
|
118
|
-
t.memberExpression(t.identifier('ctx'), t.identifier('endpoint')),
|
|
119
|
-
t.identifier('headers'),
|
|
120
|
-
])),
|
|
121
|
-
]),
|
|
122
|
-
])),
|
|
123
|
-
]
|
|
124
|
-
: [
|
|
125
|
-
t.returnStatement(t.callExpression(t.identifier('createClient'), [
|
|
126
|
-
t.objectExpression([
|
|
127
|
-
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('ctx'), t.identifier('endpoint'))),
|
|
128
|
-
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
129
|
-
]),
|
|
130
|
-
])),
|
|
108
|
+
t.returnStatement(t.callExpression(t.identifier('createClient'), [
|
|
109
|
+
t.objectExpression([
|
|
110
|
+
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('ctx'), t.identifier('endpoint'))),
|
|
111
|
+
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
131
112
|
]),
|
|
113
|
+
])),
|
|
132
114
|
]);
|
|
133
115
|
const getClientFunc = t.functionDeclaration(t.identifier('getClient'), [contextNameParam], getClientBody);
|
|
134
116
|
statements.push(t.exportNamedDeclaration(getClientFunc));
|
|
@@ -139,12 +121,8 @@ function generateExecutorFile(toolName, options) {
|
|
|
139
121
|
content: header + '\n' + code,
|
|
140
122
|
};
|
|
141
123
|
}
|
|
142
|
-
function generateMultiTargetExecutorFile(toolName, targets
|
|
124
|
+
function generateMultiTargetExecutorFile(toolName, targets) {
|
|
143
125
|
const statements = [];
|
|
144
|
-
// Import NodeHttpAdapter for *.localhost subdomain routing
|
|
145
|
-
if (options?.nodeHttpAdapter) {
|
|
146
|
-
statements.push(createImportDeclaration('./node-fetch', ['NodeHttpAdapter']));
|
|
147
|
-
}
|
|
148
126
|
statements.push(createImportDeclaration('appstash', ['createConfigStore']));
|
|
149
127
|
for (const target of targets) {
|
|
150
128
|
const aliasName = `create${target.name[0].toUpperCase()}${target.name.slice(1)}Client`;
|
|
@@ -235,26 +213,12 @@ function generateMultiTargetExecutorFile(toolName, targets, options) {
|
|
|
235
213
|
]), t.blockStatement([
|
|
236
214
|
t.expressionStatement(t.assignmentExpression('=', t.identifier('endpoint'), t.logicalExpression('||', t.memberExpression(t.identifier('defaultEndpoints'), t.identifier('targetName'), true), t.stringLiteral('')))),
|
|
237
215
|
])),
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
t.
|
|
242
|
-
t.objectExpression([
|
|
243
|
-
t.objectProperty(t.identifier('adapter'), t.newExpression(t.identifier('NodeHttpAdapter'), [
|
|
244
|
-
t.identifier('endpoint'),
|
|
245
|
-
t.identifier('headers'),
|
|
246
|
-
])),
|
|
247
|
-
]),
|
|
248
|
-
])),
|
|
249
|
-
]
|
|
250
|
-
: [
|
|
251
|
-
t.returnStatement(t.callExpression(t.identifier('createFn'), [
|
|
252
|
-
t.objectExpression([
|
|
253
|
-
t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')),
|
|
254
|
-
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
255
|
-
]),
|
|
256
|
-
])),
|
|
216
|
+
t.returnStatement(t.callExpression(t.identifier('createFn'), [
|
|
217
|
+
t.objectExpression([
|
|
218
|
+
t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')),
|
|
219
|
+
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
257
220
|
]),
|
|
221
|
+
])),
|
|
258
222
|
]);
|
|
259
223
|
const getClientFunc = t.functionDeclaration(t.identifier('getClient'), [targetNameParam, contextNameParam], getClientBody);
|
|
260
224
|
statements.push(t.exportNamedDeclaration(getClientFunc));
|
|
@@ -39,8 +39,6 @@ export interface GenerateMultiTargetCliOptions {
|
|
|
39
39
|
toolName: string;
|
|
40
40
|
builtinNames?: BuiltinNames;
|
|
41
41
|
targets: MultiTargetCliTarget[];
|
|
42
|
-
/** Enable NodeHttpAdapter for *.localhost subdomain routing */
|
|
43
|
-
nodeHttpAdapter?: boolean;
|
|
44
42
|
/** Generate a runnable index.ts entry point */
|
|
45
43
|
entryPoint?: boolean;
|
|
46
44
|
}
|
|
@@ -19,11 +19,7 @@ function generateCli(options) {
|
|
|
19
19
|
const toolName = typeof cliConfig === 'object' && cliConfig.toolName
|
|
20
20
|
? cliConfig.toolName
|
|
21
21
|
: 'app';
|
|
22
|
-
|
|
23
|
-
const useNodeHttpAdapter = !!config.nodeHttpAdapter;
|
|
24
|
-
const executorFile = (0, executor_generator_1.generateExecutorFile)(toolName, {
|
|
25
|
-
nodeHttpAdapter: useNodeHttpAdapter,
|
|
26
|
-
});
|
|
22
|
+
const executorFile = (0, executor_generator_1.generateExecutorFile)(toolName);
|
|
27
23
|
files.push(executorFile);
|
|
28
24
|
const utilsFile = (0, utils_generator_1.generateUtilsFile)();
|
|
29
25
|
files.push(utilsFile);
|
|
@@ -32,10 +28,6 @@ function generateCli(options) {
|
|
|
32
28
|
if (hasAnyEmbeddings) {
|
|
33
29
|
files.push((0, utils_generator_1.generateEmbedderFile)());
|
|
34
30
|
}
|
|
35
|
-
// Generate node HTTP adapter if configured (for *.localhost subdomain routing)
|
|
36
|
-
if (useNodeHttpAdapter) {
|
|
37
|
-
files.push((0, utils_generator_1.generateNodeFetchFile)());
|
|
38
|
-
}
|
|
39
31
|
const contextFile = (0, infra_generator_1.generateContextCommand)(toolName);
|
|
40
32
|
files.push(contextFile);
|
|
41
33
|
const authFile = (0, infra_generator_1.generateAuthCommand)(toolName);
|
|
@@ -97,9 +89,7 @@ function generateMultiTargetCli(options) {
|
|
|
97
89
|
endpoint: t.endpoint,
|
|
98
90
|
ormImportPath: t.ormImportPath,
|
|
99
91
|
}));
|
|
100
|
-
const executorFile = (0, executor_generator_1.generateMultiTargetExecutorFile)(toolName, executorInputs
|
|
101
|
-
nodeHttpAdapter: !!options.nodeHttpAdapter,
|
|
102
|
-
});
|
|
92
|
+
const executorFile = (0, executor_generator_1.generateMultiTargetExecutorFile)(toolName, executorInputs);
|
|
103
93
|
files.push(executorFile);
|
|
104
94
|
const utilsFile = (0, utils_generator_1.generateUtilsFile)();
|
|
105
95
|
files.push(utilsFile);
|
|
@@ -108,10 +98,6 @@ function generateMultiTargetCli(options) {
|
|
|
108
98
|
if (hasAnyMtEmbeddings) {
|
|
109
99
|
files.push((0, utils_generator_1.generateEmbedderFile)());
|
|
110
100
|
}
|
|
111
|
-
// Generate node HTTP adapter if configured (for *.localhost subdomain routing)
|
|
112
|
-
if (options.nodeHttpAdapter) {
|
|
113
|
-
files.push((0, utils_generator_1.generateNodeFetchFile)());
|
|
114
|
-
}
|
|
115
101
|
const contextFile = (0, infra_generator_1.generateMultiTargetContextCommand)(toolName, builtinNames.context, targets.map((t) => ({ name: t.name, endpoint: t.endpoint })));
|
|
116
102
|
files.push(contextFile);
|
|
117
103
|
const authFile = (0, infra_generator_1.generateAuthCommandWithName)(toolName, builtinNames.auth);
|
|
@@ -8,14 +8,6 @@ import type { GeneratedFile } from './executor-generator';
|
|
|
8
8
|
* and mutation input parsing.
|
|
9
9
|
*/
|
|
10
10
|
export declare function generateUtilsFile(): GeneratedFile;
|
|
11
|
-
/**
|
|
12
|
-
* Generate a node-fetch.ts file with NodeHttpAdapter for CLI.
|
|
13
|
-
*
|
|
14
|
-
* Provides a GraphQLAdapter implementation using node:http/node:https
|
|
15
|
-
* instead of the Fetch API. This cleanly handles *.localhost subdomain
|
|
16
|
-
* routing (DNS resolution + Host header) without any global patching.
|
|
17
|
-
*/
|
|
18
|
-
export declare function generateNodeFetchFile(): GeneratedFile;
|
|
19
11
|
/**
|
|
20
12
|
* Generate an index.ts entry point file for the CLI.
|
|
21
13
|
*
|
|
@@ -34,7 +34,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.generateUtilsFile = generateUtilsFile;
|
|
37
|
-
exports.generateNodeFetchFile = generateNodeFetchFile;
|
|
38
37
|
exports.generateEntryPointFile = generateEntryPointFile;
|
|
39
38
|
exports.generateEmbedderFile = generateEmbedderFile;
|
|
40
39
|
const fs = __importStar(require("fs"));
|
|
@@ -79,19 +78,6 @@ function generateUtilsFile() {
|
|
|
79
78
|
content: readTemplateFile('cli-utils.ts', 'CLI utility functions for type coercion and input handling'),
|
|
80
79
|
};
|
|
81
80
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Generate a node-fetch.ts file with NodeHttpAdapter for CLI.
|
|
84
|
-
*
|
|
85
|
-
* Provides a GraphQLAdapter implementation using node:http/node:https
|
|
86
|
-
* instead of the Fetch API. This cleanly handles *.localhost subdomain
|
|
87
|
-
* routing (DNS resolution + Host header) without any global patching.
|
|
88
|
-
*/
|
|
89
|
-
function generateNodeFetchFile() {
|
|
90
|
-
return {
|
|
91
|
-
fileName: 'node-fetch.ts',
|
|
92
|
-
content: readTemplateFile('node-fetch.ts', 'Node HTTP adapter for localhost subdomain routing'),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
81
|
/**
|
|
96
82
|
* Generate an index.ts entry point file for the CLI.
|
|
97
83
|
*
|
|
@@ -31,6 +31,4 @@ export declare function generateSelectTypesFile(): GeneratedClientFile;
|
|
|
31
31
|
/**
|
|
32
32
|
* Generate the main index.ts with createClient factory
|
|
33
33
|
*/
|
|
34
|
-
export declare function generateCreateClientFile(tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean
|
|
35
|
-
nodeHttpAdapter?: boolean;
|
|
36
|
-
}): GeneratedClientFile;
|
|
34
|
+
export declare function generateCreateClientFile(tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean): GeneratedClientFile;
|
|
@@ -126,7 +126,7 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
|
|
|
126
126
|
/**
|
|
127
127
|
* Generate the main index.ts with createClient factory
|
|
128
128
|
*/
|
|
129
|
-
function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations
|
|
129
|
+
function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations) {
|
|
130
130
|
const statements = [];
|
|
131
131
|
// Add imports
|
|
132
132
|
// Import OrmClient (value) and OrmClientConfig (type) separately
|
|
@@ -94,7 +94,7 @@ function generateOrm(options) {
|
|
|
94
94
|
const typesBarrel = (0, barrel_1.generateTypesBarrel)(useSharedTypes);
|
|
95
95
|
files.push({ path: typesBarrel.fileName, content: typesBarrel.content });
|
|
96
96
|
// 7. Generate main index.ts with createClient
|
|
97
|
-
const indexFile = (0, client_generator_1.generateCreateClientFile)(tables, hasCustomQueries, hasCustomMutations
|
|
97
|
+
const indexFile = (0, client_generator_1.generateCreateClientFile)(tables, hasCustomQueries, hasCustomMutations);
|
|
98
98
|
files.push({ path: indexFile.fileName, content: indexFile.content });
|
|
99
99
|
return {
|
|
100
100
|
files,
|
package/core/generate.js
CHANGED
|
@@ -89,9 +89,6 @@ async function generate(options = {}, internalOptions) {
|
|
|
89
89
|
const runReactQuery = config.reactQuery ?? false;
|
|
90
90
|
const runCli = internalOptions?.skipCli ? false : !!config.cli;
|
|
91
91
|
const runOrm = runReactQuery || !!config.cli || (options.orm !== undefined ? !!options.orm : false);
|
|
92
|
-
// Auto-enable nodeHttpAdapter when CLI is enabled, unless explicitly set to false
|
|
93
|
-
const useNodeHttpAdapter = options.nodeHttpAdapter === true ||
|
|
94
|
-
(runCli && options.nodeHttpAdapter !== false);
|
|
95
92
|
const schemaEnabled = !!options.schema?.enabled;
|
|
96
93
|
if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) {
|
|
97
94
|
return {
|
|
@@ -226,22 +223,13 @@ async function generate(options = {}, internalOptions) {
|
|
|
226
223
|
mutations: customOperations.mutations,
|
|
227
224
|
typeRegistry: customOperations.typeRegistry,
|
|
228
225
|
},
|
|
229
|
-
config
|
|
226
|
+
config,
|
|
230
227
|
sharedTypesPath: bothEnabled ? '..' : undefined,
|
|
231
228
|
});
|
|
232
229
|
filesToWrite.push(...files.map((file) => ({
|
|
233
230
|
...file,
|
|
234
231
|
path: node_path_1.default.posix.join('orm', file.path),
|
|
235
232
|
})));
|
|
236
|
-
// Generate NodeHttpAdapter in ORM output when enabled
|
|
237
|
-
if (useNodeHttpAdapter) {
|
|
238
|
-
const { generateNodeFetchFile } = await Promise.resolve().then(() => __importStar(require('./codegen/cli/utils-generator')));
|
|
239
|
-
const nodeFetchFile = generateNodeFetchFile();
|
|
240
|
-
filesToWrite.push({
|
|
241
|
-
path: node_path_1.default.posix.join('orm', nodeFetchFile.fileName),
|
|
242
|
-
content: nodeFetchFile.content,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
233
|
}
|
|
246
234
|
// Generate CLI commands
|
|
247
235
|
if (runCli) {
|
|
@@ -252,7 +240,7 @@ async function generate(options = {}, internalOptions) {
|
|
|
252
240
|
queries: customOperations.queries,
|
|
253
241
|
mutations: customOperations.mutations,
|
|
254
242
|
},
|
|
255
|
-
config
|
|
243
|
+
config,
|
|
256
244
|
typeRegistry: customOperations.typeRegistry,
|
|
257
245
|
});
|
|
258
246
|
filesToWrite.push(...files.map((file) => ({
|
|
@@ -582,16 +570,11 @@ async function generateMulti(options) {
|
|
|
582
570
|
if (useUnifiedCli && cliTargets.length > 0 && !dryRun) {
|
|
583
571
|
const cliConfig = typeof unifiedCli === 'object' ? unifiedCli : {};
|
|
584
572
|
const toolName = cliConfig.toolName ?? 'app';
|
|
585
|
-
// Auto-enable nodeHttpAdapter for unified CLI unless explicitly disabled
|
|
586
|
-
// Check first target config for explicit nodeHttpAdapter setting
|
|
587
573
|
const firstTargetConfig = configs[names[0]];
|
|
588
|
-
const multiNodeHttpAdapter = firstTargetConfig?.nodeHttpAdapter === true ||
|
|
589
|
-
(firstTargetConfig?.nodeHttpAdapter !== false);
|
|
590
574
|
const { files } = (0, cli_1.generateMultiTargetCli)({
|
|
591
575
|
toolName,
|
|
592
576
|
builtinNames: cliConfig.builtinNames,
|
|
593
577
|
targets: cliTargets,
|
|
594
|
-
nodeHttpAdapter: multiNodeHttpAdapter,
|
|
595
578
|
entryPoint: cliConfig.entryPoint,
|
|
596
579
|
});
|
|
597
580
|
const cliFilesToWrite = files.map((file) => ({
|
|
@@ -7,9 +7,5 @@ export interface MultiTargetExecutorInput {
|
|
|
7
7
|
endpoint: string;
|
|
8
8
|
ormImportPath: string;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
nodeHttpAdapter?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export declare function generateExecutorFile(toolName: string, options?: ExecutorOptions): GeneratedFile;
|
|
15
|
-
export declare function generateMultiTargetExecutorFile(toolName: string, targets: MultiTargetExecutorInput[], options?: ExecutorOptions): GeneratedFile;
|
|
10
|
+
export declare function generateExecutorFile(toolName: string): GeneratedFile;
|
|
11
|
+
export declare function generateMultiTargetExecutorFile(toolName: string, targets: MultiTargetExecutorInput[]): GeneratedFile;
|
|
@@ -7,12 +7,8 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
|
|
|
7
7
|
decl.importKind = typeOnly ? 'type' : 'value';
|
|
8
8
|
return decl;
|
|
9
9
|
}
|
|
10
|
-
export function generateExecutorFile(toolName
|
|
10
|
+
export function generateExecutorFile(toolName) {
|
|
11
11
|
const statements = [];
|
|
12
|
-
// Import NodeHttpAdapter for *.localhost subdomain routing
|
|
13
|
-
if (options?.nodeHttpAdapter) {
|
|
14
|
-
statements.push(createImportDeclaration('./node-fetch', ['NodeHttpAdapter']));
|
|
15
|
-
}
|
|
16
12
|
statements.push(createImportDeclaration('appstash', ['createConfigStore']));
|
|
17
13
|
statements.push(createImportDeclaration('../orm', ['createClient']));
|
|
18
14
|
statements.push(t.variableDeclaration('const', [
|
|
@@ -72,26 +68,12 @@ export function generateExecutorFile(toolName, options) {
|
|
|
72
68
|
]))),
|
|
73
69
|
])),
|
|
74
70
|
])),
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
t.
|
|
79
|
-
t.objectExpression([
|
|
80
|
-
t.objectProperty(t.identifier('adapter'), t.newExpression(t.identifier('NodeHttpAdapter'), [
|
|
81
|
-
t.memberExpression(t.identifier('ctx'), t.identifier('endpoint')),
|
|
82
|
-
t.identifier('headers'),
|
|
83
|
-
])),
|
|
84
|
-
]),
|
|
85
|
-
])),
|
|
86
|
-
]
|
|
87
|
-
: [
|
|
88
|
-
t.returnStatement(t.callExpression(t.identifier('createClient'), [
|
|
89
|
-
t.objectExpression([
|
|
90
|
-
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('ctx'), t.identifier('endpoint'))),
|
|
91
|
-
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
92
|
-
]),
|
|
93
|
-
])),
|
|
71
|
+
t.returnStatement(t.callExpression(t.identifier('createClient'), [
|
|
72
|
+
t.objectExpression([
|
|
73
|
+
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('ctx'), t.identifier('endpoint'))),
|
|
74
|
+
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
94
75
|
]),
|
|
76
|
+
])),
|
|
95
77
|
]);
|
|
96
78
|
const getClientFunc = t.functionDeclaration(t.identifier('getClient'), [contextNameParam], getClientBody);
|
|
97
79
|
statements.push(t.exportNamedDeclaration(getClientFunc));
|
|
@@ -102,12 +84,8 @@ export function generateExecutorFile(toolName, options) {
|
|
|
102
84
|
content: header + '\n' + code,
|
|
103
85
|
};
|
|
104
86
|
}
|
|
105
|
-
export function generateMultiTargetExecutorFile(toolName, targets
|
|
87
|
+
export function generateMultiTargetExecutorFile(toolName, targets) {
|
|
106
88
|
const statements = [];
|
|
107
|
-
// Import NodeHttpAdapter for *.localhost subdomain routing
|
|
108
|
-
if (options?.nodeHttpAdapter) {
|
|
109
|
-
statements.push(createImportDeclaration('./node-fetch', ['NodeHttpAdapter']));
|
|
110
|
-
}
|
|
111
89
|
statements.push(createImportDeclaration('appstash', ['createConfigStore']));
|
|
112
90
|
for (const target of targets) {
|
|
113
91
|
const aliasName = `create${target.name[0].toUpperCase()}${target.name.slice(1)}Client`;
|
|
@@ -198,26 +176,12 @@ export function generateMultiTargetExecutorFile(toolName, targets, options) {
|
|
|
198
176
|
]), t.blockStatement([
|
|
199
177
|
t.expressionStatement(t.assignmentExpression('=', t.identifier('endpoint'), t.logicalExpression('||', t.memberExpression(t.identifier('defaultEndpoints'), t.identifier('targetName'), true), t.stringLiteral('')))),
|
|
200
178
|
])),
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
t.
|
|
205
|
-
t.objectExpression([
|
|
206
|
-
t.objectProperty(t.identifier('adapter'), t.newExpression(t.identifier('NodeHttpAdapter'), [
|
|
207
|
-
t.identifier('endpoint'),
|
|
208
|
-
t.identifier('headers'),
|
|
209
|
-
])),
|
|
210
|
-
]),
|
|
211
|
-
])),
|
|
212
|
-
]
|
|
213
|
-
: [
|
|
214
|
-
t.returnStatement(t.callExpression(t.identifier('createFn'), [
|
|
215
|
-
t.objectExpression([
|
|
216
|
-
t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')),
|
|
217
|
-
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
218
|
-
]),
|
|
219
|
-
])),
|
|
179
|
+
t.returnStatement(t.callExpression(t.identifier('createFn'), [
|
|
180
|
+
t.objectExpression([
|
|
181
|
+
t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')),
|
|
182
|
+
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
220
183
|
]),
|
|
184
|
+
])),
|
|
221
185
|
]);
|
|
222
186
|
const getClientFunc = t.functionDeclaration(t.identifier('getClient'), [targetNameParam, contextNameParam], getClientBody);
|
|
223
187
|
statements.push(t.exportNamedDeclaration(getClientFunc));
|
|
@@ -39,8 +39,6 @@ export interface GenerateMultiTargetCliOptions {
|
|
|
39
39
|
toolName: string;
|
|
40
40
|
builtinNames?: BuiltinNames;
|
|
41
41
|
targets: MultiTargetCliTarget[];
|
|
42
|
-
/** Enable NodeHttpAdapter for *.localhost subdomain routing */
|
|
43
|
-
nodeHttpAdapter?: boolean;
|
|
44
42
|
/** Generate a runnable index.ts entry point */
|
|
45
43
|
entryPoint?: boolean;
|
|
46
44
|
}
|
|
@@ -5,7 +5,7 @@ import { generateExecutorFile, generateMultiTargetExecutorFile } from './executo
|
|
|
5
5
|
import { generateHelpersFile } from './helpers-generator';
|
|
6
6
|
import { generateAuthCommand, generateAuthCommandWithName, generateContextCommand, generateMultiTargetContextCommand, } from './infra-generator';
|
|
7
7
|
import { generateTableCommand } from './table-command-generator';
|
|
8
|
-
import { generateUtilsFile,
|
|
8
|
+
import { generateUtilsFile, generateEntryPointFile, generateEmbedderFile } from './utils-generator';
|
|
9
9
|
export function generateCli(options) {
|
|
10
10
|
const { tables, customOperations, config } = options;
|
|
11
11
|
const files = [];
|
|
@@ -13,11 +13,7 @@ export function generateCli(options) {
|
|
|
13
13
|
const toolName = typeof cliConfig === 'object' && cliConfig.toolName
|
|
14
14
|
? cliConfig.toolName
|
|
15
15
|
: 'app';
|
|
16
|
-
|
|
17
|
-
const useNodeHttpAdapter = !!config.nodeHttpAdapter;
|
|
18
|
-
const executorFile = generateExecutorFile(toolName, {
|
|
19
|
-
nodeHttpAdapter: useNodeHttpAdapter,
|
|
20
|
-
});
|
|
16
|
+
const executorFile = generateExecutorFile(toolName);
|
|
21
17
|
files.push(executorFile);
|
|
22
18
|
const utilsFile = generateUtilsFile();
|
|
23
19
|
files.push(utilsFile);
|
|
@@ -26,10 +22,6 @@ export function generateCli(options) {
|
|
|
26
22
|
if (hasAnyEmbeddings) {
|
|
27
23
|
files.push(generateEmbedderFile());
|
|
28
24
|
}
|
|
29
|
-
// Generate node HTTP adapter if configured (for *.localhost subdomain routing)
|
|
30
|
-
if (useNodeHttpAdapter) {
|
|
31
|
-
files.push(generateNodeFetchFile());
|
|
32
|
-
}
|
|
33
25
|
const contextFile = generateContextCommand(toolName);
|
|
34
26
|
files.push(contextFile);
|
|
35
27
|
const authFile = generateAuthCommand(toolName);
|
|
@@ -91,9 +83,7 @@ export function generateMultiTargetCli(options) {
|
|
|
91
83
|
endpoint: t.endpoint,
|
|
92
84
|
ormImportPath: t.ormImportPath,
|
|
93
85
|
}));
|
|
94
|
-
const executorFile = generateMultiTargetExecutorFile(toolName, executorInputs
|
|
95
|
-
nodeHttpAdapter: !!options.nodeHttpAdapter,
|
|
96
|
-
});
|
|
86
|
+
const executorFile = generateMultiTargetExecutorFile(toolName, executorInputs);
|
|
97
87
|
files.push(executorFile);
|
|
98
88
|
const utilsFile = generateUtilsFile();
|
|
99
89
|
files.push(utilsFile);
|
|
@@ -102,10 +92,6 @@ export function generateMultiTargetCli(options) {
|
|
|
102
92
|
if (hasAnyMtEmbeddings) {
|
|
103
93
|
files.push(generateEmbedderFile());
|
|
104
94
|
}
|
|
105
|
-
// Generate node HTTP adapter if configured (for *.localhost subdomain routing)
|
|
106
|
-
if (options.nodeHttpAdapter) {
|
|
107
|
-
files.push(generateNodeFetchFile());
|
|
108
|
-
}
|
|
109
95
|
const contextFile = generateMultiTargetContextCommand(toolName, builtinNames.context, targets.map((t) => ({ name: t.name, endpoint: t.endpoint })));
|
|
110
96
|
files.push(contextFile);
|
|
111
97
|
const authFile = generateAuthCommandWithName(toolName, builtinNames.auth);
|
|
@@ -8,14 +8,6 @@ import type { GeneratedFile } from './executor-generator';
|
|
|
8
8
|
* and mutation input parsing.
|
|
9
9
|
*/
|
|
10
10
|
export declare function generateUtilsFile(): GeneratedFile;
|
|
11
|
-
/**
|
|
12
|
-
* Generate a node-fetch.ts file with NodeHttpAdapter for CLI.
|
|
13
|
-
*
|
|
14
|
-
* Provides a GraphQLAdapter implementation using node:http/node:https
|
|
15
|
-
* instead of the Fetch API. This cleanly handles *.localhost subdomain
|
|
16
|
-
* routing (DNS resolution + Host header) without any global patching.
|
|
17
|
-
*/
|
|
18
|
-
export declare function generateNodeFetchFile(): GeneratedFile;
|
|
19
11
|
/**
|
|
20
12
|
* Generate an index.ts entry point file for the CLI.
|
|
21
13
|
*
|
|
@@ -40,19 +40,6 @@ export function generateUtilsFile() {
|
|
|
40
40
|
content: readTemplateFile('cli-utils.ts', 'CLI utility functions for type coercion and input handling'),
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Generate a node-fetch.ts file with NodeHttpAdapter for CLI.
|
|
45
|
-
*
|
|
46
|
-
* Provides a GraphQLAdapter implementation using node:http/node:https
|
|
47
|
-
* instead of the Fetch API. This cleanly handles *.localhost subdomain
|
|
48
|
-
* routing (DNS resolution + Host header) without any global patching.
|
|
49
|
-
*/
|
|
50
|
-
export function generateNodeFetchFile() {
|
|
51
|
-
return {
|
|
52
|
-
fileName: 'node-fetch.ts',
|
|
53
|
-
content: readTemplateFile('node-fetch.ts', 'Node HTTP adapter for localhost subdomain routing'),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
43
|
/**
|
|
57
44
|
* Generate an index.ts entry point file for the CLI.
|
|
58
45
|
*
|
|
@@ -31,6 +31,4 @@ export declare function generateSelectTypesFile(): GeneratedClientFile;
|
|
|
31
31
|
/**
|
|
32
32
|
* Generate the main index.ts with createClient factory
|
|
33
33
|
*/
|
|
34
|
-
export declare function generateCreateClientFile(tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean
|
|
35
|
-
nodeHttpAdapter?: boolean;
|
|
36
|
-
}): GeneratedClientFile;
|
|
34
|
+
export declare function generateCreateClientFile(tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean): GeneratedClientFile;
|
|
@@ -86,7 +86,7 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
|
|
|
86
86
|
/**
|
|
87
87
|
* Generate the main index.ts with createClient factory
|
|
88
88
|
*/
|
|
89
|
-
export function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations
|
|
89
|
+
export function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations) {
|
|
90
90
|
const statements = [];
|
|
91
91
|
// Add imports
|
|
92
92
|
// Import OrmClient (value) and OrmClientConfig (type) separately
|
|
@@ -90,7 +90,7 @@ export function generateOrm(options) {
|
|
|
90
90
|
const typesBarrel = generateTypesBarrel(useSharedTypes);
|
|
91
91
|
files.push({ path: typesBarrel.fileName, content: typesBarrel.content });
|
|
92
92
|
// 7. Generate main index.ts with createClient
|
|
93
|
-
const indexFile = generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations
|
|
93
|
+
const indexFile = generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations);
|
|
94
94
|
files.push({ path: indexFile.fileName, content: indexFile.content });
|
|
95
95
|
return {
|
|
96
96
|
files,
|
package/esm/core/generate.js
CHANGED
|
@@ -47,9 +47,6 @@ export async function generate(options = {}, internalOptions) {
|
|
|
47
47
|
const runReactQuery = config.reactQuery ?? false;
|
|
48
48
|
const runCli = internalOptions?.skipCli ? false : !!config.cli;
|
|
49
49
|
const runOrm = runReactQuery || !!config.cli || (options.orm !== undefined ? !!options.orm : false);
|
|
50
|
-
// Auto-enable nodeHttpAdapter when CLI is enabled, unless explicitly set to false
|
|
51
|
-
const useNodeHttpAdapter = options.nodeHttpAdapter === true ||
|
|
52
|
-
(runCli && options.nodeHttpAdapter !== false);
|
|
53
50
|
const schemaEnabled = !!options.schema?.enabled;
|
|
54
51
|
if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) {
|
|
55
52
|
return {
|
|
@@ -184,22 +181,13 @@ export async function generate(options = {}, internalOptions) {
|
|
|
184
181
|
mutations: customOperations.mutations,
|
|
185
182
|
typeRegistry: customOperations.typeRegistry,
|
|
186
183
|
},
|
|
187
|
-
config
|
|
184
|
+
config,
|
|
188
185
|
sharedTypesPath: bothEnabled ? '..' : undefined,
|
|
189
186
|
});
|
|
190
187
|
filesToWrite.push(...files.map((file) => ({
|
|
191
188
|
...file,
|
|
192
189
|
path: path.posix.join('orm', file.path),
|
|
193
190
|
})));
|
|
194
|
-
// Generate NodeHttpAdapter in ORM output when enabled
|
|
195
|
-
if (useNodeHttpAdapter) {
|
|
196
|
-
const { generateNodeFetchFile } = await import('./codegen/cli/utils-generator');
|
|
197
|
-
const nodeFetchFile = generateNodeFetchFile();
|
|
198
|
-
filesToWrite.push({
|
|
199
|
-
path: path.posix.join('orm', nodeFetchFile.fileName),
|
|
200
|
-
content: nodeFetchFile.content,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
191
|
}
|
|
204
192
|
// Generate CLI commands
|
|
205
193
|
if (runCli) {
|
|
@@ -210,7 +198,7 @@ export async function generate(options = {}, internalOptions) {
|
|
|
210
198
|
queries: customOperations.queries,
|
|
211
199
|
mutations: customOperations.mutations,
|
|
212
200
|
},
|
|
213
|
-
config
|
|
201
|
+
config,
|
|
214
202
|
typeRegistry: customOperations.typeRegistry,
|
|
215
203
|
});
|
|
216
204
|
filesToWrite.push(...files.map((file) => ({
|
|
@@ -540,16 +528,11 @@ export async function generateMulti(options) {
|
|
|
540
528
|
if (useUnifiedCli && cliTargets.length > 0 && !dryRun) {
|
|
541
529
|
const cliConfig = typeof unifiedCli === 'object' ? unifiedCli : {};
|
|
542
530
|
const toolName = cliConfig.toolName ?? 'app';
|
|
543
|
-
// Auto-enable nodeHttpAdapter for unified CLI unless explicitly disabled
|
|
544
|
-
// Check first target config for explicit nodeHttpAdapter setting
|
|
545
531
|
const firstTargetConfig = configs[names[0]];
|
|
546
|
-
const multiNodeHttpAdapter = firstTargetConfig?.nodeHttpAdapter === true ||
|
|
547
|
-
(firstTargetConfig?.nodeHttpAdapter !== false);
|
|
548
532
|
const { files } = generateMultiTargetCli({
|
|
549
533
|
toolName,
|
|
550
534
|
builtinNames: cliConfig.builtinNames,
|
|
551
535
|
targets: cliTargets,
|
|
552
|
-
nodeHttpAdapter: multiNodeHttpAdapter,
|
|
553
536
|
entryPoint: cliConfig.entryPoint,
|
|
554
537
|
});
|
|
555
538
|
const cliFilesToWrite = files.map((file) => ({
|
package/esm/types/config.d.ts
CHANGED
|
@@ -317,24 +317,6 @@ export interface GraphQLSDKConfigTarget {
|
|
|
317
317
|
* @default false
|
|
318
318
|
*/
|
|
319
319
|
orm?: boolean;
|
|
320
|
-
/**
|
|
321
|
-
* Enable NodeHttpAdapter generation for Node.js applications.
|
|
322
|
-
* When true, generates a node-fetch.ts with NodeHttpAdapter (implements GraphQLAdapter)
|
|
323
|
-
* using node:http/node:https for requests, enabling local development
|
|
324
|
-
* with subdomain-based routing (e.g. auth.localhost:3000).
|
|
325
|
-
* No global patching — the adapter is passed to createClient via the adapter option.
|
|
326
|
-
*
|
|
327
|
-
* When CLI generation is enabled (`cli: true`), this is auto-enabled unless
|
|
328
|
-
* explicitly set to `false`.
|
|
329
|
-
*
|
|
330
|
-
* Can also be used standalone with the ORM client for any Node.js application:
|
|
331
|
-
* ```ts
|
|
332
|
-
* import { NodeHttpAdapter } from './orm/node-fetch';
|
|
333
|
-
* const db = createClient({ adapter: new NodeHttpAdapter(endpoint, headers) });
|
|
334
|
-
* ```
|
|
335
|
-
* @default false
|
|
336
|
-
*/
|
|
337
|
-
nodeHttpAdapter?: boolean;
|
|
338
320
|
/**
|
|
339
321
|
* Whether to generate React Query hooks
|
|
340
322
|
* When enabled, generates React Query hooks to {output}/hooks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-codegen",
|
|
3
|
-
"version": "4.41.
|
|
3
|
+
"version": "4.41.1",
|
|
4
4
|
"description": "GraphQL SDK generator for Constructive databases with React Query hooks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"tsx": "^4.21.0",
|
|
101
101
|
"typescript": "^5.9.3"
|
|
102
102
|
},
|
|
103
|
-
"gitHead": "
|
|
103
|
+
"gitHead": "0b23c8c5c65e408576cd1fe3e21b20ae6db8f492"
|
|
104
104
|
}
|
package/types/config.d.ts
CHANGED
|
@@ -317,24 +317,6 @@ export interface GraphQLSDKConfigTarget {
|
|
|
317
317
|
* @default false
|
|
318
318
|
*/
|
|
319
319
|
orm?: boolean;
|
|
320
|
-
/**
|
|
321
|
-
* Enable NodeHttpAdapter generation for Node.js applications.
|
|
322
|
-
* When true, generates a node-fetch.ts with NodeHttpAdapter (implements GraphQLAdapter)
|
|
323
|
-
* using node:http/node:https for requests, enabling local development
|
|
324
|
-
* with subdomain-based routing (e.g. auth.localhost:3000).
|
|
325
|
-
* No global patching — the adapter is passed to createClient via the adapter option.
|
|
326
|
-
*
|
|
327
|
-
* When CLI generation is enabled (`cli: true`), this is auto-enabled unless
|
|
328
|
-
* explicitly set to `false`.
|
|
329
|
-
*
|
|
330
|
-
* Can also be used standalone with the ORM client for any Node.js application:
|
|
331
|
-
* ```ts
|
|
332
|
-
* import { NodeHttpAdapter } from './orm/node-fetch';
|
|
333
|
-
* const db = createClient({ adapter: new NodeHttpAdapter(endpoint, headers) });
|
|
334
|
-
* ```
|
|
335
|
-
* @default false
|
|
336
|
-
*/
|
|
337
|
-
nodeHttpAdapter?: boolean;
|
|
338
320
|
/**
|
|
339
321
|
* Whether to generate React Query hooks
|
|
340
322
|
* When enabled, generates React Query hooks to {output}/hooks
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Node HTTP Adapter for Node.js applications
|
|
3
|
-
*
|
|
4
|
-
* Implements the GraphQLAdapter interface using node:http / node:https
|
|
5
|
-
* instead of the Fetch API. This solves two Node.js limitations:
|
|
6
|
-
*
|
|
7
|
-
* 1. DNS: Node.js cannot resolve *.localhost subdomains (ENOTFOUND).
|
|
8
|
-
* Browsers handle this automatically, but Node requires manual resolution.
|
|
9
|
-
*
|
|
10
|
-
* 2. Host header: The Fetch API treats "Host" as a forbidden request header
|
|
11
|
-
* and silently drops it. The Constructive GraphQL server uses Host-header
|
|
12
|
-
* subdomain routing (enableServicesApi), so this header must be preserved.
|
|
13
|
-
*
|
|
14
|
-
* By using node:http.request directly, both issues are bypassed cleanly
|
|
15
|
-
* without any global patching.
|
|
16
|
-
*
|
|
17
|
-
* NOTE: This file is read at codegen time and written to output.
|
|
18
|
-
* Any changes here will affect all generated CLI node adapters.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import http from 'node:http';
|
|
22
|
-
import https from 'node:https';
|
|
23
|
-
|
|
24
|
-
import type {
|
|
25
|
-
GraphQLAdapter,
|
|
26
|
-
GraphQLError,
|
|
27
|
-
QueryResult,
|
|
28
|
-
} from '@constructive-io/graphql-types';
|
|
29
|
-
|
|
30
|
-
interface HttpResponse {
|
|
31
|
-
statusCode: number;
|
|
32
|
-
statusMessage: string;
|
|
33
|
-
data: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Check if a hostname is a localhost subdomain that needs special handling.
|
|
38
|
-
* Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost".
|
|
39
|
-
*/
|
|
40
|
-
function isLocalhostSubdomain(hostname: string): boolean {
|
|
41
|
-
return hostname.endsWith('.localhost') && hostname !== 'localhost';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Make an HTTP/HTTPS request using native Node modules.
|
|
46
|
-
* Supports optional AbortSignal for request cancellation.
|
|
47
|
-
*/
|
|
48
|
-
function makeRequest(
|
|
49
|
-
url: URL,
|
|
50
|
-
options: http.RequestOptions,
|
|
51
|
-
body: string,
|
|
52
|
-
signal?: AbortSignal,
|
|
53
|
-
): Promise<HttpResponse> {
|
|
54
|
-
return new Promise((resolve, reject) => {
|
|
55
|
-
if (signal?.aborted) {
|
|
56
|
-
reject(new Error('The operation was aborted'));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const protocol = url.protocol === 'https:' ? https : http;
|
|
61
|
-
|
|
62
|
-
const req = protocol.request(url, options, (res) => {
|
|
63
|
-
let data = '';
|
|
64
|
-
res.setEncoding('utf8');
|
|
65
|
-
res.on('data', (chunk: string) => {
|
|
66
|
-
data += chunk;
|
|
67
|
-
});
|
|
68
|
-
res.on('end', () => {
|
|
69
|
-
resolve({
|
|
70
|
-
statusCode: res.statusCode || 0,
|
|
71
|
-
statusMessage: res.statusMessage || '',
|
|
72
|
-
data,
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
req.on('error', reject);
|
|
78
|
-
|
|
79
|
-
if (signal) {
|
|
80
|
-
const onAbort = () => {
|
|
81
|
-
req.destroy(new Error('The operation was aborted'));
|
|
82
|
-
};
|
|
83
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
84
|
-
req.on('close', () => {
|
|
85
|
-
signal.removeEventListener('abort', onAbort);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
req.write(body);
|
|
90
|
-
req.end();
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Options for individual execute calls.
|
|
96
|
-
* Allows per-request header overrides and request cancellation.
|
|
97
|
-
*/
|
|
98
|
-
export interface NodeHttpExecuteOptions {
|
|
99
|
-
/** Additional headers to include in this request only */
|
|
100
|
-
headers?: Record<string, string>;
|
|
101
|
-
/** AbortSignal for request cancellation */
|
|
102
|
-
signal?: AbortSignal;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* GraphQL adapter that uses node:http/node:https for requests.
|
|
107
|
-
*
|
|
108
|
-
* Handles *.localhost subdomains by rewriting the hostname to "localhost"
|
|
109
|
-
* and injecting the original Host header for server-side subdomain routing.
|
|
110
|
-
*/
|
|
111
|
-
export class NodeHttpAdapter implements GraphQLAdapter {
|
|
112
|
-
private headers: Record<string, string>;
|
|
113
|
-
private url: URL;
|
|
114
|
-
|
|
115
|
-
constructor(
|
|
116
|
-
private endpoint: string,
|
|
117
|
-
headers?: Record<string, string>,
|
|
118
|
-
) {
|
|
119
|
-
this.headers = headers ?? {};
|
|
120
|
-
this.url = new URL(endpoint);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async execute<T>(
|
|
124
|
-
document: string,
|
|
125
|
-
variables?: Record<string, unknown>,
|
|
126
|
-
options?: NodeHttpExecuteOptions,
|
|
127
|
-
): Promise<QueryResult<T>> {
|
|
128
|
-
const requestUrl = new URL(this.url.href);
|
|
129
|
-
const requestHeaders: Record<string, string> = {
|
|
130
|
-
'Content-Type': 'application/json',
|
|
131
|
-
Accept: 'application/json',
|
|
132
|
-
...this.headers,
|
|
133
|
-
...options?.headers,
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// For *.localhost subdomains, rewrite hostname and inject Host header
|
|
137
|
-
if (isLocalhostSubdomain(requestUrl.hostname)) {
|
|
138
|
-
requestHeaders['Host'] = requestUrl.host;
|
|
139
|
-
requestUrl.hostname = 'localhost';
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const body = JSON.stringify({
|
|
143
|
-
query: document,
|
|
144
|
-
variables: variables ?? {},
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const requestOptions: http.RequestOptions = {
|
|
148
|
-
method: 'POST',
|
|
149
|
-
headers: requestHeaders,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const response = await makeRequest(
|
|
153
|
-
requestUrl,
|
|
154
|
-
requestOptions,
|
|
155
|
-
body,
|
|
156
|
-
options?.signal,
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
160
|
-
return {
|
|
161
|
-
ok: false,
|
|
162
|
-
data: null,
|
|
163
|
-
errors: [
|
|
164
|
-
{
|
|
165
|
-
message: `HTTP ${response.statusCode}: ${response.statusMessage}`,
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const json = JSON.parse(response.data) as {
|
|
172
|
-
data?: T;
|
|
173
|
-
errors?: GraphQLError[];
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
if (json.errors && json.errors.length > 0) {
|
|
177
|
-
return {
|
|
178
|
-
ok: false,
|
|
179
|
-
data: null,
|
|
180
|
-
errors: json.errors,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
ok: true,
|
|
186
|
-
data: json.data as T,
|
|
187
|
-
errors: undefined,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
setHeaders(headers: Record<string, string>): void {
|
|
192
|
-
this.headers = { ...this.headers, ...headers };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
getEndpoint(): string {
|
|
196
|
-
return this.endpoint;
|
|
197
|
-
}
|
|
198
|
-
}
|