@constructive-io/graphql-codegen 2.22.1 → 2.23.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/cli/codegen/barrel.d.ts +5 -1
- package/cli/codegen/barrel.js +13 -11
- package/cli/codegen/index.d.ts +3 -3
- package/cli/codegen/index.js +15 -9
- package/cli/codegen/orm/client-generator.js +3 -2
- package/cli/codegen/orm/custom-ops-generator.js +17 -4
- package/cli/codegen/orm/input-types-generator.js +129 -18
- package/cli/codegen/orm/model-generator.js +2 -1
- package/cli/codegen/orm/query-builder.d.ts +1 -1
- package/cli/codegen/orm/query-builder.js +2 -2
- package/cli/codegen/schema-types-generator.js +5 -5
- package/cli/codegen/utils.d.ts +6 -1
- package/cli/codegen/utils.js +23 -8
- package/cli/commands/generate-orm.d.ts +5 -3
- package/cli/commands/generate-orm.js +65 -84
- package/cli/commands/generate.d.ts +2 -0
- package/cli/commands/generate.js +66 -87
- package/cli/commands/shared.d.ts +74 -0
- package/cli/commands/shared.js +88 -0
- package/cli/index.js +75 -45
- package/cli/introspect/index.d.ts +8 -5
- package/cli/introspect/index.js +19 -7
- package/cli/introspect/infer-tables.d.ts +51 -0
- package/cli/introspect/infer-tables.js +550 -0
- package/cli/introspect/source/endpoint.d.ts +34 -0
- package/cli/introspect/source/endpoint.js +35 -0
- package/cli/introspect/source/file.d.ts +20 -0
- package/cli/introspect/source/file.js +103 -0
- package/cli/introspect/source/index.d.ts +48 -0
- package/cli/introspect/source/index.js +72 -0
- package/cli/introspect/source/types.d.ts +58 -0
- package/cli/introspect/source/types.js +27 -0
- package/cli/introspect/transform.d.ts +5 -6
- package/cli/introspect/transform.js +0 -173
- package/cli/watch/cache.d.ts +3 -4
- package/cli/watch/cache.js +6 -10
- package/cli/watch/poller.d.ts +1 -2
- package/cli/watch/poller.js +27 -45
- package/cli/watch/types.d.ts +0 -3
- package/core/ast.js +4 -4
- package/core/query-builder.js +12 -12
- package/esm/cli/codegen/barrel.d.ts +5 -1
- package/esm/cli/codegen/barrel.js +13 -11
- package/esm/cli/codegen/index.d.ts +3 -3
- package/esm/cli/codegen/index.js +18 -12
- package/esm/cli/codegen/orm/client-generator.js +3 -2
- package/esm/cli/codegen/orm/custom-ops-generator.js +18 -5
- package/esm/cli/codegen/orm/input-types-generator.js +130 -19
- package/esm/cli/codegen/orm/model-generator.js +3 -2
- package/esm/cli/codegen/orm/query-builder.d.ts +1 -1
- package/esm/cli/codegen/orm/query-builder.js +2 -2
- package/esm/cli/codegen/schema-types-generator.js +6 -6
- package/esm/cli/codegen/utils.d.ts +6 -1
- package/esm/cli/codegen/utils.js +22 -8
- package/esm/cli/commands/generate-orm.d.ts +5 -3
- package/esm/cli/commands/generate-orm.js +65 -84
- package/esm/cli/commands/generate.d.ts +2 -0
- package/esm/cli/commands/generate.js +66 -87
- package/esm/cli/commands/shared.d.ts +74 -0
- package/esm/cli/commands/shared.js +84 -0
- package/esm/cli/index.js +76 -46
- package/esm/cli/introspect/index.d.ts +8 -5
- package/esm/cli/introspect/index.js +10 -3
- package/esm/cli/introspect/infer-tables.d.ts +51 -0
- package/esm/cli/introspect/infer-tables.js +547 -0
- package/esm/cli/introspect/source/endpoint.d.ts +34 -0
- package/esm/cli/introspect/source/endpoint.js +31 -0
- package/esm/cli/introspect/source/file.d.ts +20 -0
- package/esm/cli/introspect/source/file.js +66 -0
- package/esm/cli/introspect/source/index.d.ts +48 -0
- package/esm/cli/introspect/source/index.js +54 -0
- package/esm/cli/introspect/source/types.d.ts +58 -0
- package/esm/cli/introspect/source/types.js +23 -0
- package/esm/cli/introspect/transform.d.ts +5 -6
- package/esm/cli/introspect/transform.js +0 -172
- package/esm/cli/watch/cache.d.ts +3 -4
- package/esm/cli/watch/cache.js +7 -11
- package/esm/cli/watch/poller.d.ts +1 -2
- package/esm/cli/watch/poller.js +28 -46
- package/esm/cli/watch/types.d.ts +0 -3
- package/esm/core/ast.js +4 -4
- package/esm/core/query-builder.js +12 -12
- package/esm/generators/mutations.js +3 -3
- package/esm/generators/select.js +7 -7
- package/esm/types/config.d.ts +21 -5
- package/esm/types/config.js +2 -1
- package/generators/mutations.js +3 -3
- package/generators/select.js +7 -7
- package/package.json +5 -3
- package/types/config.d.ts +21 -5
- package/types/config.js +2 -1
- package/cli/introspect/fetch-meta.d.ts +0 -31
- package/cli/introspect/fetch-meta.js +0 -108
- package/cli/introspect/meta-query.d.ts +0 -111
- package/cli/introspect/meta-query.js +0 -191
- package/esm/cli/introspect/fetch-meta.d.ts +0 -31
- package/esm/cli/introspect/fetch-meta.js +0 -104
- package/esm/cli/introspect/meta-query.d.ts +0 -111
- package/esm/cli/introspect/meta-query.js +0 -188
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Schema Source
|
|
3
|
+
*
|
|
4
|
+
* Loads GraphQL schema from a local .graphql SDL file and converts it
|
|
5
|
+
* to introspection format using the graphql package.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { buildSchema, introspectionFromSchema } from 'graphql';
|
|
10
|
+
import { SchemaSourceError } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Schema source that loads from a local GraphQL schema file
|
|
13
|
+
*
|
|
14
|
+
* Supports .graphql files containing SDL (Schema Definition Language).
|
|
15
|
+
* The SDL is parsed using graphql-js and converted to introspection format.
|
|
16
|
+
*/
|
|
17
|
+
export class FileSchemaSource {
|
|
18
|
+
options;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
async fetch() {
|
|
23
|
+
const absolutePath = path.isAbsolute(this.options.schemaPath)
|
|
24
|
+
? this.options.schemaPath
|
|
25
|
+
: path.resolve(process.cwd(), this.options.schemaPath);
|
|
26
|
+
// Check file exists
|
|
27
|
+
if (!fs.existsSync(absolutePath)) {
|
|
28
|
+
throw new SchemaSourceError(`Schema file not found: ${absolutePath}`, this.describe());
|
|
29
|
+
}
|
|
30
|
+
// Read file content
|
|
31
|
+
let sdl;
|
|
32
|
+
try {
|
|
33
|
+
sdl = await fs.promises.readFile(absolutePath, 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw new SchemaSourceError(`Failed to read schema file: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), err instanceof Error ? err : undefined);
|
|
37
|
+
}
|
|
38
|
+
// Validate non-empty
|
|
39
|
+
if (!sdl.trim()) {
|
|
40
|
+
throw new SchemaSourceError('Schema file is empty', this.describe());
|
|
41
|
+
}
|
|
42
|
+
// Parse SDL to GraphQL schema
|
|
43
|
+
let schema;
|
|
44
|
+
try {
|
|
45
|
+
schema = buildSchema(sdl);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
throw new SchemaSourceError(`Invalid GraphQL SDL: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), err instanceof Error ? err : undefined);
|
|
49
|
+
}
|
|
50
|
+
// Convert to introspection format
|
|
51
|
+
let introspectionResult;
|
|
52
|
+
try {
|
|
53
|
+
introspectionResult = introspectionFromSchema(schema);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
throw new SchemaSourceError(`Failed to generate introspection: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), err instanceof Error ? err : undefined);
|
|
57
|
+
}
|
|
58
|
+
// Convert graphql-js introspection result to our mutable type
|
|
59
|
+
// The graphql-js types are readonly, but our types are mutable
|
|
60
|
+
const introspection = JSON.parse(JSON.stringify(introspectionResult));
|
|
61
|
+
return { introspection };
|
|
62
|
+
}
|
|
63
|
+
describe() {
|
|
64
|
+
return `file: ${this.options.schemaPath}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Source Module
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for loading GraphQL schemas from different sources:
|
|
5
|
+
* - Live GraphQL endpoints (via introspection)
|
|
6
|
+
* - Static .graphql schema files
|
|
7
|
+
*/
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from './endpoint';
|
|
10
|
+
export * from './file';
|
|
11
|
+
import type { SchemaSource } from './types';
|
|
12
|
+
export interface CreateSchemaSourceOptions {
|
|
13
|
+
/**
|
|
14
|
+
* GraphQL endpoint URL (for live introspection)
|
|
15
|
+
*/
|
|
16
|
+
endpoint?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Path to GraphQL schema file (.graphql)
|
|
19
|
+
*/
|
|
20
|
+
schema?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional authorization header for endpoint requests
|
|
23
|
+
*/
|
|
24
|
+
authorization?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Optional additional headers for endpoint requests
|
|
27
|
+
*/
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Request timeout in milliseconds (for endpoint requests)
|
|
31
|
+
*/
|
|
32
|
+
timeout?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a schema source based on configuration
|
|
36
|
+
*
|
|
37
|
+
* @param options - Source configuration
|
|
38
|
+
* @returns Appropriate SchemaSource implementation
|
|
39
|
+
* @throws Error if neither endpoint nor schema is provided
|
|
40
|
+
*/
|
|
41
|
+
export declare function createSchemaSource(options: CreateSchemaSourceOptions): SchemaSource;
|
|
42
|
+
/**
|
|
43
|
+
* Validate that source options are valid (at least one source specified)
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateSourceOptions(options: CreateSchemaSourceOptions): {
|
|
46
|
+
valid: boolean;
|
|
47
|
+
error?: string;
|
|
48
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Source Module
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for loading GraphQL schemas from different sources:
|
|
5
|
+
* - Live GraphQL endpoints (via introspection)
|
|
6
|
+
* - Static .graphql schema files
|
|
7
|
+
*/
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from './endpoint';
|
|
10
|
+
export * from './file';
|
|
11
|
+
import { EndpointSchemaSource } from './endpoint';
|
|
12
|
+
import { FileSchemaSource } from './file';
|
|
13
|
+
/**
|
|
14
|
+
* Create a schema source based on configuration
|
|
15
|
+
*
|
|
16
|
+
* @param options - Source configuration
|
|
17
|
+
* @returns Appropriate SchemaSource implementation
|
|
18
|
+
* @throws Error if neither endpoint nor schema is provided
|
|
19
|
+
*/
|
|
20
|
+
export function createSchemaSource(options) {
|
|
21
|
+
if (options.schema) {
|
|
22
|
+
return new FileSchemaSource({
|
|
23
|
+
schemaPath: options.schema,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (options.endpoint) {
|
|
27
|
+
return new EndpointSchemaSource({
|
|
28
|
+
endpoint: options.endpoint,
|
|
29
|
+
authorization: options.authorization,
|
|
30
|
+
headers: options.headers,
|
|
31
|
+
timeout: options.timeout,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
throw new Error('Either endpoint or schema must be provided. ' +
|
|
35
|
+
'Use --endpoint for live introspection or --schema for a local file.');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate that source options are valid (at least one source specified)
|
|
39
|
+
*/
|
|
40
|
+
export function validateSourceOptions(options) {
|
|
41
|
+
if (!options.endpoint && !options.schema) {
|
|
42
|
+
return {
|
|
43
|
+
valid: false,
|
|
44
|
+
error: 'Either endpoint or schema must be provided',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (options.endpoint && options.schema) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
error: 'Cannot use both endpoint and schema. Choose one source.',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { valid: true };
|
|
54
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Source Types
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface for schema sources, allowing the codegen pipeline
|
|
5
|
+
* to work with both live GraphQL endpoints and static schema files.
|
|
6
|
+
*/
|
|
7
|
+
import type { IntrospectionQueryResponse } from '../../../types/introspection';
|
|
8
|
+
/**
|
|
9
|
+
* Result from fetching a schema source
|
|
10
|
+
*/
|
|
11
|
+
export interface SchemaSourceResult {
|
|
12
|
+
/**
|
|
13
|
+
* The GraphQL introspection data
|
|
14
|
+
*/
|
|
15
|
+
introspection: IntrospectionQueryResponse;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Abstract interface for schema sources
|
|
19
|
+
*
|
|
20
|
+
* Implementations:
|
|
21
|
+
* - EndpointSchemaSource: Fetches from a live GraphQL endpoint
|
|
22
|
+
* - FileSchemaSource: Loads from a local .graphql schema file
|
|
23
|
+
*/
|
|
24
|
+
export interface SchemaSource {
|
|
25
|
+
/**
|
|
26
|
+
* Fetch or load the GraphQL introspection data
|
|
27
|
+
* @throws SchemaSourceError if fetching fails
|
|
28
|
+
*/
|
|
29
|
+
fetch(): Promise<SchemaSourceResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Human-readable description of the source (for logging)
|
|
32
|
+
* @example "endpoint: https://api.example.com/graphql"
|
|
33
|
+
* @example "file: ./schema.graphql"
|
|
34
|
+
*/
|
|
35
|
+
describe(): string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Error thrown when a schema source fails to fetch
|
|
39
|
+
*/
|
|
40
|
+
export declare class SchemaSourceError extends Error {
|
|
41
|
+
/**
|
|
42
|
+
* Description of the source that failed
|
|
43
|
+
*/
|
|
44
|
+
readonly source: string;
|
|
45
|
+
/**
|
|
46
|
+
* Original error that caused the failure
|
|
47
|
+
*/
|
|
48
|
+
readonly cause?: Error;
|
|
49
|
+
constructor(message: string,
|
|
50
|
+
/**
|
|
51
|
+
* Description of the source that failed
|
|
52
|
+
*/
|
|
53
|
+
source: string,
|
|
54
|
+
/**
|
|
55
|
+
* Original error that caused the failure
|
|
56
|
+
*/
|
|
57
|
+
cause?: Error);
|
|
58
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when a schema source fails to fetch
|
|
3
|
+
*/
|
|
4
|
+
export class SchemaSourceError extends Error {
|
|
5
|
+
source;
|
|
6
|
+
cause;
|
|
7
|
+
constructor(message,
|
|
8
|
+
/**
|
|
9
|
+
* Description of the source that failed
|
|
10
|
+
*/
|
|
11
|
+
source,
|
|
12
|
+
/**
|
|
13
|
+
* Original error that caused the failure
|
|
14
|
+
*/
|
|
15
|
+
cause) {
|
|
16
|
+
super(`${message} (source: ${source})`);
|
|
17
|
+
this.source = source;
|
|
18
|
+
this.cause = cause;
|
|
19
|
+
this.name = 'SchemaSourceError';
|
|
20
|
+
// Maintain proper prototype chain
|
|
21
|
+
Object.setPrototypeOf(this, SchemaSourceError.prototype);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Table utility functions for CleanTable[]
|
|
3
|
+
*
|
|
4
|
+
* Note: The _meta transform functions have been removed.
|
|
5
|
+
* Tables are now inferred from standard GraphQL introspection
|
|
6
|
+
* using inferTablesFromIntrospection() in ./infer-tables.ts
|
|
3
7
|
*/
|
|
4
8
|
import type { CleanTable } from '../../types/schema';
|
|
5
|
-
import type { MetaQueryResponse } from './meta-query';
|
|
6
|
-
/**
|
|
7
|
-
* Transform _meta response to CleanTable array
|
|
8
|
-
*/
|
|
9
|
-
export declare function transformMetaToCleanTables(metaResponse: MetaQueryResponse): CleanTable[];
|
|
10
9
|
/**
|
|
11
10
|
* Get table names from CleanTable array
|
|
12
11
|
*/
|
|
@@ -1,175 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transform _meta response to CleanTable array
|
|
3
|
-
*/
|
|
4
|
-
export function transformMetaToCleanTables(metaResponse) {
|
|
5
|
-
const { tables } = metaResponse._meta;
|
|
6
|
-
return tables.map((table) => transformTable(table));
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Transform a single MetaTable to CleanTable
|
|
10
|
-
*/
|
|
11
|
-
function transformTable(table) {
|
|
12
|
-
return {
|
|
13
|
-
name: table.name,
|
|
14
|
-
fields: transformFields(table.fields),
|
|
15
|
-
relations: transformRelations(table.relations),
|
|
16
|
-
inflection: transformInflection(table.inflection),
|
|
17
|
-
query: transformQuery(table.query),
|
|
18
|
-
constraints: transformConstraints(table),
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Transform MetaField[] to CleanField[]
|
|
23
|
-
*/
|
|
24
|
-
function transformFields(fields) {
|
|
25
|
-
return fields.map((field) => ({
|
|
26
|
-
name: field.name,
|
|
27
|
-
type: transformFieldType(field.type),
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Transform MetaFieldType to CleanFieldType
|
|
32
|
-
*/
|
|
33
|
-
function transformFieldType(type) {
|
|
34
|
-
return {
|
|
35
|
-
gqlType: type.gqlType,
|
|
36
|
-
isArray: type.isArray,
|
|
37
|
-
modifier: type.modifier,
|
|
38
|
-
pgAlias: type.pgAlias,
|
|
39
|
-
pgType: type.pgType,
|
|
40
|
-
subtype: type.subtype,
|
|
41
|
-
typmod: type.typmod,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Transform table relations
|
|
46
|
-
*/
|
|
47
|
-
function transformRelations(relations) {
|
|
48
|
-
return {
|
|
49
|
-
belongsTo: relations.belongsTo.map(transformBelongsTo),
|
|
50
|
-
hasOne: relations.hasOne.map(transformHasOne),
|
|
51
|
-
hasMany: relations.hasMany.map(transformHasMany),
|
|
52
|
-
manyToMany: relations.manyToMany.map(transformManyToMany),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Transform belongsTo relation
|
|
57
|
-
*/
|
|
58
|
-
function transformBelongsTo(rel) {
|
|
59
|
-
return {
|
|
60
|
-
fieldName: rel.fieldName,
|
|
61
|
-
isUnique: rel.isUnique,
|
|
62
|
-
referencesTable: rel.references.name,
|
|
63
|
-
type: rel.type,
|
|
64
|
-
keys: transformFields(rel.keys),
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Transform hasOne relation
|
|
69
|
-
*/
|
|
70
|
-
function transformHasOne(rel) {
|
|
71
|
-
return {
|
|
72
|
-
fieldName: rel.fieldName,
|
|
73
|
-
isUnique: rel.isUnique,
|
|
74
|
-
referencedByTable: rel.referencedBy.name,
|
|
75
|
-
type: rel.type,
|
|
76
|
-
keys: transformFields(rel.keys),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Transform hasMany relation
|
|
81
|
-
*/
|
|
82
|
-
function transformHasMany(rel) {
|
|
83
|
-
return {
|
|
84
|
-
fieldName: rel.fieldName,
|
|
85
|
-
isUnique: rel.isUnique,
|
|
86
|
-
referencedByTable: rel.referencedBy.name,
|
|
87
|
-
type: rel.type,
|
|
88
|
-
keys: transformFields(rel.keys),
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Transform manyToMany relation
|
|
93
|
-
*/
|
|
94
|
-
function transformManyToMany(rel) {
|
|
95
|
-
return {
|
|
96
|
-
fieldName: rel.fieldName,
|
|
97
|
-
rightTable: rel.rightTable.name,
|
|
98
|
-
junctionTable: rel.junctionTable.name,
|
|
99
|
-
type: rel.type,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Transform inflection data
|
|
104
|
-
*/
|
|
105
|
-
function transformInflection(inflection) {
|
|
106
|
-
return {
|
|
107
|
-
allRows: inflection.allRows,
|
|
108
|
-
allRowsSimple: inflection.allRowsSimple,
|
|
109
|
-
conditionType: inflection.conditionType,
|
|
110
|
-
connection: inflection.connection,
|
|
111
|
-
createField: inflection.createField,
|
|
112
|
-
createInputType: inflection.createInputType,
|
|
113
|
-
createPayloadType: inflection.createPayloadType,
|
|
114
|
-
deleteByPrimaryKey: inflection.deleteByPrimaryKey,
|
|
115
|
-
deletePayloadType: inflection.deletePayloadType,
|
|
116
|
-
edge: inflection.edge,
|
|
117
|
-
edgeField: inflection.edgeField,
|
|
118
|
-
enumType: inflection.enumType,
|
|
119
|
-
filterType: inflection.filterType,
|
|
120
|
-
inputType: inflection.inputType,
|
|
121
|
-
orderByType: inflection.orderByType,
|
|
122
|
-
patchField: inflection.patchField,
|
|
123
|
-
patchType: inflection.patchType,
|
|
124
|
-
tableFieldName: inflection.tableFieldName,
|
|
125
|
-
tableType: inflection.tableType,
|
|
126
|
-
typeName: inflection.typeName,
|
|
127
|
-
updateByPrimaryKey: inflection.updateByPrimaryKey,
|
|
128
|
-
updatePayloadType: inflection.updatePayloadType,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Transform query names
|
|
133
|
-
*/
|
|
134
|
-
function transformQuery(query) {
|
|
135
|
-
return {
|
|
136
|
-
all: query.all,
|
|
137
|
-
one: query.one,
|
|
138
|
-
create: query.create,
|
|
139
|
-
update: query.update,
|
|
140
|
-
delete: query.delete,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Transform constraints
|
|
145
|
-
*/
|
|
146
|
-
function transformConstraints(table) {
|
|
147
|
-
return {
|
|
148
|
-
primaryKey: table.primaryKeyConstraints.map(transformConstraint),
|
|
149
|
-
foreignKey: table.foreignKeyConstraints.map(transformForeignKeyConstraint),
|
|
150
|
-
unique: table.uniqueConstraints.map(transformConstraint),
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Transform a basic constraint
|
|
155
|
-
*/
|
|
156
|
-
function transformConstraint(constraint) {
|
|
157
|
-
return {
|
|
158
|
-
name: constraint.name,
|
|
159
|
-
fields: transformFields(constraint.fields),
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Transform a foreign key constraint
|
|
164
|
-
*/
|
|
165
|
-
function transformForeignKeyConstraint(constraint) {
|
|
166
|
-
return {
|
|
167
|
-
name: constraint.name,
|
|
168
|
-
fields: transformFields(constraint.fields),
|
|
169
|
-
refTable: constraint.refTable.name,
|
|
170
|
-
refFields: transformFields(constraint.refFields),
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
1
|
/**
|
|
174
2
|
* Get table names from CleanTable array
|
|
175
3
|
*/
|
package/esm/cli/watch/cache.d.ts
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
* Only stores the hash in memory - no file I/O during normal operation.
|
|
5
5
|
* Touch file feature is optional and only writes when explicitly configured.
|
|
6
6
|
*/
|
|
7
|
-
import type { MetaQueryResponse } from '../introspect/meta-query';
|
|
8
7
|
import type { IntrospectionQueryResponse } from '../../types/introspection';
|
|
9
8
|
/**
|
|
10
9
|
* Lightweight in-memory schema cache
|
|
11
|
-
* Only stores the
|
|
10
|
+
* Only stores the schema hash string to detect changes
|
|
12
11
|
*/
|
|
13
12
|
export declare class SchemaCache {
|
|
14
13
|
/** Current schema hash (in-memory only) */
|
|
@@ -17,7 +16,7 @@ export declare class SchemaCache {
|
|
|
17
16
|
* Check if schema has changed by comparing hashes
|
|
18
17
|
* This is the hot path - must be efficient
|
|
19
18
|
*/
|
|
20
|
-
hasChanged(
|
|
19
|
+
hasChanged(schema: IntrospectionQueryResponse): Promise<{
|
|
21
20
|
changed: boolean;
|
|
22
21
|
newHash: string;
|
|
23
22
|
}>;
|
|
@@ -34,7 +33,7 @@ export declare class SchemaCache {
|
|
|
34
33
|
*/
|
|
35
34
|
clear(): void;
|
|
36
35
|
/**
|
|
37
|
-
* Compute hash from
|
|
36
|
+
* Compute hash from schema response
|
|
38
37
|
*/
|
|
39
38
|
private computeHash;
|
|
40
39
|
}
|
package/esm/cli/watch/cache.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'node:fs';
|
|
8
8
|
import * as path from 'node:path';
|
|
9
|
-
import { hashObject
|
|
9
|
+
import { hashObject } from './hash';
|
|
10
10
|
/**
|
|
11
11
|
* Lightweight in-memory schema cache
|
|
12
|
-
* Only stores the
|
|
12
|
+
* Only stores the schema hash string to detect changes
|
|
13
13
|
*/
|
|
14
14
|
export class SchemaCache {
|
|
15
15
|
/** Current schema hash (in-memory only) */
|
|
@@ -18,8 +18,8 @@ export class SchemaCache {
|
|
|
18
18
|
* Check if schema has changed by comparing hashes
|
|
19
19
|
* This is the hot path - must be efficient
|
|
20
20
|
*/
|
|
21
|
-
async hasChanged(
|
|
22
|
-
const newHash = await this.computeHash(
|
|
21
|
+
async hasChanged(schema) {
|
|
22
|
+
const newHash = await this.computeHash(schema);
|
|
23
23
|
const changed = this.currentHash === null || this.currentHash !== newHash;
|
|
24
24
|
return { changed, newHash };
|
|
25
25
|
}
|
|
@@ -42,14 +42,10 @@ export class SchemaCache {
|
|
|
42
42
|
this.currentHash = null;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
-
* Compute hash from
|
|
45
|
+
* Compute hash from schema response
|
|
46
46
|
*/
|
|
47
|
-
async computeHash(
|
|
48
|
-
|
|
49
|
-
// This is more efficient than stringifying both together
|
|
50
|
-
const metaHash = await hashObject(meta);
|
|
51
|
-
const schemaHash = await hashObject(schema);
|
|
52
|
-
return combineHashes(metaHash, schemaHash);
|
|
47
|
+
async computeHash(schema) {
|
|
48
|
+
return hashObject(schema);
|
|
53
49
|
}
|
|
54
50
|
}
|
|
55
51
|
/**
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* No file I/O during normal polling - only touchFile is optional file write.
|
|
6
6
|
*/
|
|
7
7
|
import { EventEmitter } from 'node:events';
|
|
8
|
-
import type { MetaQueryResponse } from '../introspect/meta-query';
|
|
9
8
|
import type { IntrospectionQueryResponse } from '../../types/introspection';
|
|
10
9
|
import { SchemaCache } from './cache';
|
|
11
10
|
import type { PollResult, WatchOptions } from './types';
|
|
@@ -62,4 +61,4 @@ export declare class SchemaPoller extends EventEmitter {
|
|
|
62
61
|
/**
|
|
63
62
|
* Utility to compute schema hash without full poll
|
|
64
63
|
*/
|
|
65
|
-
export declare function computeSchemaHash(
|
|
64
|
+
export declare function computeSchemaHash(schema: IntrospectionQueryResponse): Promise<string>;
|
package/esm/cli/watch/poller.js
CHANGED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* No file I/O during normal polling - only touchFile is optional file write.
|
|
6
6
|
*/
|
|
7
7
|
import { EventEmitter } from 'node:events';
|
|
8
|
-
import { fetchMeta } from '../introspect/fetch-meta';
|
|
9
8
|
import { fetchSchema } from '../introspect/fetch-schema';
|
|
10
9
|
import { SchemaCache, touchFile } from './cache';
|
|
11
|
-
import { hashObject
|
|
10
|
+
import { hashObject } from './hash';
|
|
12
11
|
/**
|
|
13
12
|
* Schema poller that periodically introspects a GraphQL endpoint
|
|
14
13
|
* and emits events when the schema changes.
|
|
@@ -56,39 +55,31 @@ export class SchemaPoller extends EventEmitter {
|
|
|
56
55
|
async poll() {
|
|
57
56
|
// Prevent concurrent polls
|
|
58
57
|
if (this.isPolling) {
|
|
59
|
-
return {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
changed: false,
|
|
61
|
+
error: 'Poll already in progress',
|
|
62
|
+
};
|
|
60
63
|
}
|
|
61
64
|
this.isPolling = true;
|
|
62
65
|
const startTime = Date.now();
|
|
63
66
|
this.emit('poll-start', this.createEvent('poll-start'));
|
|
64
67
|
try {
|
|
65
|
-
// Fetch
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}),
|
|
73
|
-
fetchSchema({
|
|
74
|
-
endpoint: this.options.endpoint,
|
|
75
|
-
authorization: this.options.authorization,
|
|
76
|
-
headers: this.options.headers,
|
|
77
|
-
timeout: 30000,
|
|
78
|
-
}),
|
|
79
|
-
]);
|
|
68
|
+
// Fetch __schema via standard introspection
|
|
69
|
+
const schemaResult = await fetchSchema({
|
|
70
|
+
endpoint: this.options.endpoint,
|
|
71
|
+
authorization: this.options.authorization,
|
|
72
|
+
headers: this.options.headers,
|
|
73
|
+
timeout: 30000,
|
|
74
|
+
});
|
|
80
75
|
const duration = Date.now() - startTime;
|
|
81
76
|
// Check for errors
|
|
82
|
-
if (!metaResult.success) {
|
|
83
|
-
return this.handleError(`_meta fetch failed: ${metaResult.error}`, duration);
|
|
84
|
-
}
|
|
85
77
|
if (!schemaResult.success) {
|
|
86
78
|
return this.handleError(`__schema fetch failed: ${schemaResult.error}`, duration);
|
|
87
79
|
}
|
|
88
|
-
const meta = metaResult.data;
|
|
89
80
|
const schema = schemaResult.data;
|
|
90
81
|
// Check if schema changed
|
|
91
|
-
const { changed, newHash } = await this.cache.hasChanged(
|
|
82
|
+
const { changed, newHash } = await this.cache.hasChanged(schema);
|
|
92
83
|
// Reset error counter on success
|
|
93
84
|
this.consecutiveErrors = 0;
|
|
94
85
|
if (changed) {
|
|
@@ -99,11 +90,11 @@ export class SchemaPoller extends EventEmitter {
|
|
|
99
90
|
touchFile(this.options.touchFile);
|
|
100
91
|
}
|
|
101
92
|
this.emit('schema-changed', this.createEvent('schema-changed', { hash: newHash, duration }));
|
|
102
|
-
return { success: true, changed: true, hash: newHash,
|
|
93
|
+
return { success: true, changed: true, hash: newHash, schema };
|
|
103
94
|
}
|
|
104
95
|
this.emit('schema-unchanged', this.createEvent('schema-unchanged', { duration }));
|
|
105
96
|
this.emit('poll-success', this.createEvent('poll-success', { duration }));
|
|
106
|
-
return { success: true, changed: false, hash: newHash,
|
|
97
|
+
return { success: true, changed: false, hash: newHash, schema };
|
|
107
98
|
}
|
|
108
99
|
catch (err) {
|
|
109
100
|
const duration = Date.now() - startTime;
|
|
@@ -126,22 +117,14 @@ export class SchemaPoller extends EventEmitter {
|
|
|
126
117
|
*/
|
|
127
118
|
async seedCache() {
|
|
128
119
|
try {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
endpoint: this.options.endpoint,
|
|
138
|
-
authorization: this.options.authorization,
|
|
139
|
-
headers: this.options.headers,
|
|
140
|
-
timeout: 30000,
|
|
141
|
-
}),
|
|
142
|
-
]);
|
|
143
|
-
if (metaResult.success && schemaResult.success) {
|
|
144
|
-
const { newHash } = await this.cache.hasChanged(metaResult.data, schemaResult.data);
|
|
120
|
+
const schemaResult = await fetchSchema({
|
|
121
|
+
endpoint: this.options.endpoint,
|
|
122
|
+
authorization: this.options.authorization,
|
|
123
|
+
headers: this.options.headers,
|
|
124
|
+
timeout: 30000,
|
|
125
|
+
});
|
|
126
|
+
if (schemaResult.success) {
|
|
127
|
+
const { newHash } = await this.cache.hasChanged(schemaResult.data);
|
|
145
128
|
this.cache.updateHash(newHash);
|
|
146
129
|
}
|
|
147
130
|
}
|
|
@@ -171,7 +154,8 @@ export class SchemaPoller extends EventEmitter {
|
|
|
171
154
|
this.consecutiveErrors++;
|
|
172
155
|
this.emit('poll-error', this.createEvent('poll-error', { error, duration }));
|
|
173
156
|
// Slow down polling after multiple consecutive errors
|
|
174
|
-
if (this.consecutiveErrors >= this.MAX_CONSECUTIVE_ERRORS &&
|
|
157
|
+
if (this.consecutiveErrors >= this.MAX_CONSECUTIVE_ERRORS &&
|
|
158
|
+
this.pollTimer) {
|
|
175
159
|
this.stop();
|
|
176
160
|
const newInterval = this.options.pollInterval * 2;
|
|
177
161
|
this.pollTimer = setInterval(() => {
|
|
@@ -191,8 +175,6 @@ export class SchemaPoller extends EventEmitter {
|
|
|
191
175
|
/**
|
|
192
176
|
* Utility to compute schema hash without full poll
|
|
193
177
|
*/
|
|
194
|
-
export async function computeSchemaHash(
|
|
195
|
-
|
|
196
|
-
const schemaHash = await hashObject(schema);
|
|
197
|
-
return combineHashes(metaHash, schemaHash);
|
|
178
|
+
export async function computeSchemaHash(schema) {
|
|
179
|
+
return hashObject(schema);
|
|
198
180
|
}
|
package/esm/cli/watch/types.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Watch mode types
|
|
3
3
|
*/
|
|
4
|
-
import type { MetaQueryResponse } from '../introspect/meta-query';
|
|
5
4
|
import type { IntrospectionQueryResponse } from '../../types/introspection';
|
|
6
5
|
/**
|
|
7
6
|
* Result of a schema poll operation
|
|
@@ -14,8 +13,6 @@ export interface PollResult {
|
|
|
14
13
|
error?: string;
|
|
15
14
|
/** The new hash if successful */
|
|
16
15
|
hash?: string;
|
|
17
|
-
/** Meta response if successful */
|
|
18
|
-
meta?: MetaQueryResponse;
|
|
19
16
|
/** Schema response if successful */
|
|
20
17
|
schema?: IntrospectionQueryResponse;
|
|
21
18
|
}
|