From 23d1a53b10fa2d7be210e4257373a02211a882c4 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Wed, 13 May 2026 22:59:48 +0000 Subject: [PATCH 1/2] refactor: remove NodeHttpAdapter in favor of @constructive-io/fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ORM client's FetchAdapter already uses createFetch() from @constructive-io/fetch, which handles *.localhost DNS rewriting and Host header preservation in Node.js. The separate NodeHttpAdapter (using node:http/node:https directly) is redundant. - Remove node-fetch.ts template and all generated node-fetch.ts files - Remove generateNodeFetchFile() from utils-generator - Simplify executor generators to always use { endpoint, headers } - Remove nodeHttpAdapter config plumbing from generate.ts - Deprecate nodeHttpAdapter option in config types - Update all 4 SDK executor.ts files to use endpoint/headers The external API (createClient, getClient) is unchanged — consumers pass endpoint + headers, and FetchAdapter handles the rest. --- .../core/codegen/cli/executor-generator.ts | 94 ++------- graphql/codegen/src/core/codegen/cli/index.ts | 25 +-- .../src/core/codegen/cli/utils-generator.ts | 17 -- .../src/core/codegen/templates/node-fetch.ts | 198 ------------------ graphql/codegen/src/core/generate.ts | 26 +-- graphql/codegen/src/types/config.ts | 18 +- .../src/admin/cli/executor.ts | 4 +- .../src/admin/cli/node-fetch.ts | 174 --------------- .../src/admin/orm/node-fetch.ts | 174 --------------- sdk/constructive-cli/src/auth/cli/executor.ts | 4 +- .../src/auth/cli/node-fetch.ts | 174 --------------- .../src/auth/orm/node-fetch.ts | 174 --------------- .../src/objects/cli/executor.ts | 4 +- .../src/objects/cli/node-fetch.ts | 174 --------------- .../src/objects/orm/node-fetch.ts | 174 --------------- .../src/public/cli/executor.ts | 4 +- .../src/public/cli/node-fetch.ts | 174 --------------- .../src/public/orm/node-fetch.ts | 174 --------------- 18 files changed, 38 insertions(+), 1748 deletions(-) delete mode 100644 graphql/codegen/src/core/codegen/templates/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/admin/cli/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/admin/orm/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/auth/cli/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/auth/orm/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/objects/cli/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/objects/orm/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/public/cli/node-fetch.ts delete mode 100644 sdk/constructive-cli/src/public/orm/node-fetch.ts diff --git a/graphql/codegen/src/core/codegen/cli/executor-generator.ts b/graphql/codegen/src/core/codegen/cli/executor-generator.ts index 50cfeb0070..9c2147d5d7 100644 --- a/graphql/codegen/src/core/codegen/cli/executor-generator.ts +++ b/graphql/codegen/src/core/codegen/cli/executor-generator.ts @@ -31,20 +31,13 @@ function createImportDeclaration( } export interface ExecutorOptions { - /** Enable NodeHttpAdapter for *.localhost subdomain routing */ + /** @deprecated NodeHttpAdapter has been removed; createClient uses @constructive-io/fetch automatically */ nodeHttpAdapter?: boolean; } -export function generateExecutorFile(toolName: string, options?: ExecutorOptions): GeneratedFile { +export function generateExecutorFile(toolName: string, _options?: ExecutorOptions): GeneratedFile { const statements: t.Statement[] = []; - // Import NodeHttpAdapter for *.localhost subdomain routing - if (options?.nodeHttpAdapter) { - statements.push( - createImportDeclaration('./node-fetch', ['NodeHttpAdapter']), - ); - } - statements.push( createImportDeclaration('appstash', ['createConfigStore']), ); @@ -215,39 +208,20 @@ export function generateExecutorFile(toolName: string, options?: ExecutorOptions ]), ), - // Build createClient config — use NodeHttpAdapter for *.localhost endpoints - ...(options?.nodeHttpAdapter - ? [ - t.returnStatement( - t.callExpression(t.identifier('createClient'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('adapter'), - t.newExpression(t.identifier('NodeHttpAdapter'), [ - t.memberExpression(t.identifier('ctx'), t.identifier('endpoint')), - t.identifier('headers'), - ]), - ), - ]), - ]), + t.returnStatement( + t.callExpression(t.identifier('createClient'), [ + t.objectExpression([ + t.objectProperty( + t.identifier('endpoint'), + t.memberExpression(t.identifier('ctx'), t.identifier('endpoint')), ), - ] - : [ - t.returnStatement( - t.callExpression(t.identifier('createClient'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('endpoint'), - t.memberExpression(t.identifier('ctx'), t.identifier('endpoint')), - ), - t.objectProperty( - t.identifier('headers'), - t.identifier('headers'), - ), - ]), - ]), + t.objectProperty( + t.identifier('headers'), + t.identifier('headers'), ), ]), + ]), + ), ]); const getClientFunc = t.functionDeclaration( @@ -269,17 +243,10 @@ export function generateExecutorFile(toolName: string, options?: ExecutorOptions export function generateMultiTargetExecutorFile( toolName: string, targets: MultiTargetExecutorInput[], - options?: ExecutorOptions, + _options?: ExecutorOptions, ): GeneratedFile { const statements: t.Statement[] = []; - // Import NodeHttpAdapter for *.localhost subdomain routing - if (options?.nodeHttpAdapter) { - statements.push( - createImportDeclaration('./node-fetch', ['NodeHttpAdapter']), - ); - } - statements.push( createImportDeclaration('appstash', ['createConfigStore']), ); @@ -538,33 +505,14 @@ export function generateMultiTargetExecutorFile( ), ]), ), - // Build createClient config — use NodeHttpAdapter for *.localhost endpoints - ...(options?.nodeHttpAdapter - ? [ - t.returnStatement( - t.callExpression(t.identifier('createFn'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('adapter'), - t.newExpression(t.identifier('NodeHttpAdapter'), [ - t.identifier('endpoint'), - t.identifier('headers'), - ]), - ), - ]), - ]), - ), - ] - : [ - t.returnStatement( - t.callExpression(t.identifier('createFn'), [ - t.objectExpression([ - t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')), - t.objectProperty(t.identifier('headers'), t.identifier('headers')), - ]), - ]), - ), + t.returnStatement( + t.callExpression(t.identifier('createFn'), [ + t.objectExpression([ + t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')), + t.objectProperty(t.identifier('headers'), t.identifier('headers')), ]), + ]), + ), ]); const getClientFunc = t.functionDeclaration( diff --git a/graphql/codegen/src/core/codegen/cli/index.ts b/graphql/codegen/src/core/codegen/cli/index.ts index 9e107db1aa..493c56368d 100644 --- a/graphql/codegen/src/core/codegen/cli/index.ts +++ b/graphql/codegen/src/core/codegen/cli/index.ts @@ -14,7 +14,7 @@ import { generateMultiTargetContextCommand, } from './infra-generator'; import { generateTableCommand } from './table-command-generator'; -import { generateUtilsFile, generateNodeFetchFile, generateEntryPointFile, generateEmbedderFile } from './utils-generator'; +import { generateUtilsFile, generateEntryPointFile, generateEmbedderFile } from './utils-generator'; export interface GenerateCliOptions { tables: Table[]; @@ -48,12 +48,7 @@ export function generateCli(options: GenerateCliOptions): GenerateCliResult { ? cliConfig.toolName : 'app'; - // Use top-level nodeHttpAdapter from config (auto-enabled for CLI by generate.ts) - const useNodeHttpAdapter = !!config.nodeHttpAdapter; - - const executorFile = generateExecutorFile(toolName, { - nodeHttpAdapter: useNodeHttpAdapter, - }); + const executorFile = generateExecutorFile(toolName); files.push(executorFile); const utilsFile = generateUtilsFile(); @@ -67,11 +62,6 @@ export function generateCli(options: GenerateCliOptions): GenerateCliResult { files.push(generateEmbedderFile()); } - // Generate node HTTP adapter if configured (for *.localhost subdomain routing) - if (useNodeHttpAdapter) { - files.push(generateNodeFetchFile()); - } - const contextFile = generateContextCommand(toolName); files.push(contextFile); @@ -139,7 +129,7 @@ export interface GenerateMultiTargetCliOptions { toolName: string; builtinNames?: BuiltinNames; targets: MultiTargetCliTarget[]; - /** Enable NodeHttpAdapter for *.localhost subdomain routing */ + /** @deprecated NodeHttpAdapter removed; createClient uses @constructive-io/fetch */ nodeHttpAdapter?: boolean; /** Generate a runnable index.ts entry point */ entryPoint?: boolean; @@ -180,9 +170,7 @@ export function generateMultiTargetCli( endpoint: t.endpoint, ormImportPath: t.ormImportPath, })); - const executorFile = generateMultiTargetExecutorFile(toolName, executorInputs, { - nodeHttpAdapter: !!options.nodeHttpAdapter, - }); + const executorFile = generateMultiTargetExecutorFile(toolName, executorInputs); files.push(executorFile); const utilsFile = generateUtilsFile(); @@ -198,11 +186,6 @@ export function generateMultiTargetCli( files.push(generateEmbedderFile()); } - // Generate node HTTP adapter if configured (for *.localhost subdomain routing) - if (options.nodeHttpAdapter) { - files.push(generateNodeFetchFile()); - } - const contextFile = generateMultiTargetContextCommand( toolName, builtinNames.context, diff --git a/graphql/codegen/src/core/codegen/cli/utils-generator.ts b/graphql/codegen/src/core/codegen/cli/utils-generator.ts index 3860d94a72..eb1fc0274d 100644 --- a/graphql/codegen/src/core/codegen/cli/utils-generator.ts +++ b/graphql/codegen/src/core/codegen/cli/utils-generator.ts @@ -60,23 +60,6 @@ export function generateUtilsFile(): GeneratedFile { }; } -/** - * Generate a node-fetch.ts file with NodeHttpAdapter for CLI. - * - * Provides a GraphQLAdapter implementation using node:http/node:https - * instead of the Fetch API. This cleanly handles *.localhost subdomain - * routing (DNS resolution + Host header) without any global patching. - */ -export function generateNodeFetchFile(): GeneratedFile { - return { - fileName: 'node-fetch.ts', - content: readTemplateFile( - 'node-fetch.ts', - 'Node HTTP adapter for localhost subdomain routing', - ), - }; -} - /** * Generate an index.ts entry point file for the CLI. * diff --git a/graphql/codegen/src/core/codegen/templates/node-fetch.ts b/graphql/codegen/src/core/codegen/templates/node-fetch.ts deleted file mode 100644 index a8304b4f5b..0000000000 --- a/graphql/codegen/src/core/codegen/templates/node-fetch.ts +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Node HTTP Adapter for Node.js applications - * - * Implements the GraphQLAdapter interface using node:http / node:https - * instead of the Fetch API. This solves two Node.js limitations: - * - * 1. DNS: Node.js cannot resolve *.localhost subdomains (ENOTFOUND). - * Browsers handle this automatically, but Node requires manual resolution. - * - * 2. Host header: The Fetch API treats "Host" as a forbidden request header - * and silently drops it. The Constructive GraphQL server uses Host-header - * subdomain routing (enableServicesApi), so this header must be preserved. - * - * By using node:http.request directly, both issues are bypassed cleanly - * without any global patching. - * - * NOTE: This file is read at codegen time and written to output. - * Any changes here will affect all generated CLI node adapters. - */ - -import http from 'node:http'; -import https from 'node:https'; - -import type { - GraphQLAdapter, - GraphQLError, - QueryResult, -} from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal, -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record, - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions, - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest( - requestUrl, - requestOptions, - body, - options?.signal, - ); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index f2f7609919..ee5a3130fc 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -126,11 +126,6 @@ export async function generate( const runOrm = runReactQuery || !!config.cli || (options.orm !== undefined ? !!options.orm : false); - // Auto-enable nodeHttpAdapter when CLI is enabled, unless explicitly set to false - const useNodeHttpAdapter = - options.nodeHttpAdapter === true || - (runCli && options.nodeHttpAdapter !== false); - const schemaEnabled = !!options.schema?.enabled; if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) { @@ -280,7 +275,7 @@ export async function generate( mutations: customOperations.mutations, typeRegistry: customOperations.typeRegistry, }, - config: { ...config, nodeHttpAdapter: useNodeHttpAdapter }, + config, sharedTypesPath: bothEnabled ? '..' : undefined, }); filesToWrite.push( @@ -289,16 +284,6 @@ export async function generate( path: path.posix.join('orm', file.path), })), ); - - // Generate NodeHttpAdapter in ORM output when enabled - if (useNodeHttpAdapter) { - const { generateNodeFetchFile } = await import('./codegen/cli/utils-generator'); - const nodeFetchFile = generateNodeFetchFile(); - filesToWrite.push({ - path: path.posix.join('orm', nodeFetchFile.fileName), - content: nodeFetchFile.content, - }); - } } // Generate CLI commands @@ -310,7 +295,7 @@ export async function generate( queries: customOperations.queries, mutations: customOperations.mutations, }, - config: { ...config, nodeHttpAdapter: useNodeHttpAdapter }, + config, typeRegistry: customOperations.typeRegistry, }); filesToWrite.push( @@ -707,18 +692,11 @@ export async function generateMulti( if (useUnifiedCli && cliTargets.length > 0 && !dryRun) { const cliConfig = typeof unifiedCli === 'object' ? unifiedCli : {}; const toolName = cliConfig.toolName ?? 'app'; - // Auto-enable nodeHttpAdapter for unified CLI unless explicitly disabled - // Check first target config for explicit nodeHttpAdapter setting const firstTargetConfig = configs[names[0]]; - const multiNodeHttpAdapter = - firstTargetConfig?.nodeHttpAdapter === true || - (firstTargetConfig?.nodeHttpAdapter !== false); - const { files } = generateMultiTargetCli({ toolName, builtinNames: cliConfig.builtinNames, targets: cliTargets, - nodeHttpAdapter: multiNodeHttpAdapter, entryPoint: cliConfig.entryPoint, }); diff --git a/graphql/codegen/src/types/config.ts b/graphql/codegen/src/types/config.ts index 790b11be73..13b18f0614 100644 --- a/graphql/codegen/src/types/config.ts +++ b/graphql/codegen/src/types/config.ts @@ -366,21 +366,9 @@ export interface GraphQLSDKConfigTarget { orm?: boolean; /** - * Enable NodeHttpAdapter generation for Node.js applications. - * When true, generates a node-fetch.ts with NodeHttpAdapter (implements GraphQLAdapter) - * using node:http/node:https for requests, enabling local development - * with subdomain-based routing (e.g. auth.localhost:3000). - * No global patching — the adapter is passed to createClient via the adapter option. - * - * When CLI generation is enabled (`cli: true`), this is auto-enabled unless - * explicitly set to `false`. - * - * Can also be used standalone with the ORM client for any Node.js application: - * ```ts - * import { NodeHttpAdapter } from './orm/node-fetch'; - * const db = createClient({ adapter: new NodeHttpAdapter(endpoint, headers) }); - * ``` - * @default false + * @deprecated NodeHttpAdapter has been removed. The ORM client now uses + * @constructive-io/fetch automatically, which handles *.localhost subdomain + * routing in Node.js without a separate adapter. */ nodeHttpAdapter?: boolean; diff --git a/sdk/constructive-cli/src/admin/cli/executor.ts b/sdk/constructive-cli/src/admin/cli/executor.ts index 721e8e68e1..fdfdc796d7 100644 --- a/sdk/constructive-cli/src/admin/cli/executor.ts +++ b/sdk/constructive-cli/src/admin/cli/executor.ts @@ -3,7 +3,6 @@ * @generated by @constructive-io/graphql-codegen * DO NOT EDIT - changes will be overwritten */ -import { NodeHttpAdapter } from './node-fetch'; import { createConfigStore } from 'appstash'; import { createClient } from '../orm'; const store = createConfigStore('csdk'); @@ -29,6 +28,7 @@ export function getClient(contextName?: string) { } } return createClient({ - adapter: new NodeHttpAdapter(ctx.endpoint, headers), + endpoint: ctx.endpoint, + headers, }); } diff --git a/sdk/constructive-cli/src/admin/cli/node-fetch.ts b/sdk/constructive-cli/src/admin/cli/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/admin/cli/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/admin/orm/node-fetch.ts b/sdk/constructive-cli/src/admin/orm/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/admin/orm/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/auth/cli/executor.ts b/sdk/constructive-cli/src/auth/cli/executor.ts index 721e8e68e1..fdfdc796d7 100644 --- a/sdk/constructive-cli/src/auth/cli/executor.ts +++ b/sdk/constructive-cli/src/auth/cli/executor.ts @@ -3,7 +3,6 @@ * @generated by @constructive-io/graphql-codegen * DO NOT EDIT - changes will be overwritten */ -import { NodeHttpAdapter } from './node-fetch'; import { createConfigStore } from 'appstash'; import { createClient } from '../orm'; const store = createConfigStore('csdk'); @@ -29,6 +28,7 @@ export function getClient(contextName?: string) { } } return createClient({ - adapter: new NodeHttpAdapter(ctx.endpoint, headers), + endpoint: ctx.endpoint, + headers, }); } diff --git a/sdk/constructive-cli/src/auth/cli/node-fetch.ts b/sdk/constructive-cli/src/auth/cli/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/auth/cli/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/auth/orm/node-fetch.ts b/sdk/constructive-cli/src/auth/orm/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/auth/orm/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/objects/cli/executor.ts b/sdk/constructive-cli/src/objects/cli/executor.ts index 721e8e68e1..fdfdc796d7 100644 --- a/sdk/constructive-cli/src/objects/cli/executor.ts +++ b/sdk/constructive-cli/src/objects/cli/executor.ts @@ -3,7 +3,6 @@ * @generated by @constructive-io/graphql-codegen * DO NOT EDIT - changes will be overwritten */ -import { NodeHttpAdapter } from './node-fetch'; import { createConfigStore } from 'appstash'; import { createClient } from '../orm'; const store = createConfigStore('csdk'); @@ -29,6 +28,7 @@ export function getClient(contextName?: string) { } } return createClient({ - adapter: new NodeHttpAdapter(ctx.endpoint, headers), + endpoint: ctx.endpoint, + headers, }); } diff --git a/sdk/constructive-cli/src/objects/cli/node-fetch.ts b/sdk/constructive-cli/src/objects/cli/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/objects/cli/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/objects/orm/node-fetch.ts b/sdk/constructive-cli/src/objects/orm/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/objects/orm/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/public/cli/executor.ts b/sdk/constructive-cli/src/public/cli/executor.ts index 721e8e68e1..fdfdc796d7 100644 --- a/sdk/constructive-cli/src/public/cli/executor.ts +++ b/sdk/constructive-cli/src/public/cli/executor.ts @@ -3,7 +3,6 @@ * @generated by @constructive-io/graphql-codegen * DO NOT EDIT - changes will be overwritten */ -import { NodeHttpAdapter } from './node-fetch'; import { createConfigStore } from 'appstash'; import { createClient } from '../orm'; const store = createConfigStore('csdk'); @@ -29,6 +28,7 @@ export function getClient(contextName?: string) { } } return createClient({ - adapter: new NodeHttpAdapter(ctx.endpoint, headers), + endpoint: ctx.endpoint, + headers, }); } diff --git a/sdk/constructive-cli/src/public/cli/node-fetch.ts b/sdk/constructive-cli/src/public/cli/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/public/cli/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} diff --git a/sdk/constructive-cli/src/public/orm/node-fetch.ts b/sdk/constructive-cli/src/public/orm/node-fetch.ts deleted file mode 100644 index 81bb05834c..0000000000 --- a/sdk/constructive-cli/src/public/orm/node-fetch.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Node HTTP adapter for localhost subdomain routing - * @generated by @constructive-io/graphql-codegen - * DO NOT EDIT - changes will be overwritten - */ -import http from 'node:http'; -import https from 'node:https'; - -import type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types'; - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Check if a hostname is a localhost subdomain that needs special handling. - * Returns true for *.localhost (e.g. auth.localhost) but not bare "localhost". - */ -function isLocalhostSubdomain(hostname: string): boolean { - return hostname.endsWith('.localhost') && hostname !== 'localhost'; -} - -/** - * Make an HTTP/HTTPS request using native Node modules. - * Supports optional AbortSignal for request cancellation. - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string, - signal?: AbortSignal -): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - reject(new Error('The operation was aborted')); - return; - } - - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - - if (signal) { - const onAbort = () => { - req.destroy(new Error('The operation was aborted')); - }; - signal.addEventListener('abort', onAbort, { once: true }); - req.on('close', () => { - signal.removeEventListener('abort', onAbort); - }); - } - - req.write(body); - req.end(); - }); -} - -/** - * Options for individual execute calls. - * Allows per-request header overrides and request cancellation. - */ -export interface NodeHttpExecuteOptions { - /** Additional headers to include in this request only */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * GraphQL adapter that uses node:http/node:https for requests. - * - * Handles *.localhost subdomains by rewriting the hostname to "localhost" - * and injecting the original Host header for server-side subdomain routing. - */ -export class NodeHttpAdapter implements GraphQLAdapter { - private headers: Record; - private url: URL; - - constructor( - private endpoint: string, - headers?: Record - ) { - this.headers = headers ?? {}; - this.url = new URL(endpoint); - } - - async execute( - document: string, - variables?: Record, - options?: NodeHttpExecuteOptions - ): Promise> { - const requestUrl = new URL(this.url.href); - const requestHeaders: Record = { - 'Content-Type': 'application/json', - Accept: 'application/json', - ...this.headers, - ...options?.headers, - }; - - // For *.localhost subdomains, rewrite hostname and inject Host header - if (isLocalhostSubdomain(requestUrl.hostname)) { - requestHeaders['Host'] = requestUrl.host; - requestUrl.hostname = 'localhost'; - } - - const body = JSON.stringify({ - query: document, - variables: variables ?? {}, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: requestHeaders, - }; - - const response = await makeRequest(requestUrl, requestOptions, body, options?.signal); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - ok: false, - data: null, - errors: [ - { - message: `HTTP ${response.statusCode}: ${response.statusMessage}`, - }, - ], - }; - } - - const json = JSON.parse(response.data) as { - data?: T; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - return { - ok: false, - data: null, - errors: json.errors, - }; - } - - return { - ok: true, - data: json.data as T, - errors: undefined, - }; - } - - setHeaders(headers: Record): void { - this.headers = { ...this.headers, ...headers }; - } - - getEndpoint(): string { - return this.endpoint; - } -} From 2bd3b18c556cad93f96624ce5714460161d3b03f Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Wed, 13 May 2026 23:13:55 +0000 Subject: [PATCH 2/2] chore: fully remove nodeHttpAdapter option instead of deprecating --- .../codegen/src/core/codegen/cli/executor-generator.ts | 8 +------- graphql/codegen/src/core/codegen/cli/index.ts | 2 -- graphql/codegen/src/core/codegen/orm/client-generator.ts | 1 - graphql/codegen/src/core/codegen/orm/index.ts | 1 - graphql/codegen/src/types/config.ts | 7 ------- 5 files changed, 1 insertion(+), 18 deletions(-) diff --git a/graphql/codegen/src/core/codegen/cli/executor-generator.ts b/graphql/codegen/src/core/codegen/cli/executor-generator.ts index 9c2147d5d7..a4cc2e0ede 100644 --- a/graphql/codegen/src/core/codegen/cli/executor-generator.ts +++ b/graphql/codegen/src/core/codegen/cli/executor-generator.ts @@ -30,12 +30,7 @@ function createImportDeclaration( return decl; } -export interface ExecutorOptions { - /** @deprecated NodeHttpAdapter has been removed; createClient uses @constructive-io/fetch automatically */ - nodeHttpAdapter?: boolean; -} - -export function generateExecutorFile(toolName: string, _options?: ExecutorOptions): GeneratedFile { +export function generateExecutorFile(toolName: string): GeneratedFile { const statements: t.Statement[] = []; statements.push( @@ -243,7 +238,6 @@ export function generateExecutorFile(toolName: string, _options?: ExecutorOption export function generateMultiTargetExecutorFile( toolName: string, targets: MultiTargetExecutorInput[], - _options?: ExecutorOptions, ): GeneratedFile { const statements: t.Statement[] = []; diff --git a/graphql/codegen/src/core/codegen/cli/index.ts b/graphql/codegen/src/core/codegen/cli/index.ts index 493c56368d..fdd48989e2 100644 --- a/graphql/codegen/src/core/codegen/cli/index.ts +++ b/graphql/codegen/src/core/codegen/cli/index.ts @@ -129,8 +129,6 @@ export interface GenerateMultiTargetCliOptions { toolName: string; builtinNames?: BuiltinNames; targets: MultiTargetCliTarget[]; - /** @deprecated NodeHttpAdapter removed; createClient uses @constructive-io/fetch */ - nodeHttpAdapter?: boolean; /** Generate a runnable index.ts entry point */ entryPoint?: boolean; } diff --git a/graphql/codegen/src/core/codegen/orm/client-generator.ts b/graphql/codegen/src/core/codegen/orm/client-generator.ts index efec017cab..3920ec5b38 100644 --- a/graphql/codegen/src/core/codegen/orm/client-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/client-generator.ts @@ -137,7 +137,6 @@ export function generateCreateClientFile( tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean, - options?: { nodeHttpAdapter?: boolean }, ): GeneratedClientFile { const statements: t.Statement[] = []; diff --git a/graphql/codegen/src/core/codegen/orm/index.ts b/graphql/codegen/src/core/codegen/orm/index.ts index 2d2c9b866c..f9be00d932 100644 --- a/graphql/codegen/src/core/codegen/orm/index.ts +++ b/graphql/codegen/src/core/codegen/orm/index.ts @@ -177,7 +177,6 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { tables, hasCustomQueries, hasCustomMutations, - { nodeHttpAdapter: !!options.config.nodeHttpAdapter }, ); files.push({ path: indexFile.fileName, content: indexFile.content }); diff --git a/graphql/codegen/src/types/config.ts b/graphql/codegen/src/types/config.ts index 13b18f0614..962bf78da9 100644 --- a/graphql/codegen/src/types/config.ts +++ b/graphql/codegen/src/types/config.ts @@ -365,13 +365,6 @@ export interface GraphQLSDKConfigTarget { */ orm?: boolean; - /** - * @deprecated NodeHttpAdapter has been removed. The ORM client now uses - * @constructive-io/fetch automatically, which handles *.localhost subdomain - * routing in Node.js without a separate adapter. - */ - nodeHttpAdapter?: boolean; - /** * Whether to generate React Query hooks * When enabled, generates React Query hooks to {output}/hooks