@constructive-io/graphql-codegen 4.38.1 → 4.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,6 +44,17 @@ export function generateOrmClientFile() {
44
44
  content: readTemplateFile('orm-client.ts', 'ORM Client - Runtime GraphQL executor'),
45
45
  };
46
46
  }
47
+ /**
48
+ * Generate the realtime.ts file (RealtimeManager + subscription types)
49
+ *
50
+ * Reads from the templates directory for proper type checking.
51
+ */
52
+ export function generateRealtimeFile() {
53
+ return {
54
+ fileName: 'realtime.ts',
55
+ content: readTemplateFile('orm-realtime.ts', 'Realtime Manager - WebSocket subscription support'),
56
+ };
57
+ }
47
58
  /**
48
59
  * Generate the query-builder.ts file (runtime query builder)
49
60
  *
@@ -45,6 +45,37 @@ export declare class FetchAdapter implements GraphQLAdapter {
45
45
  setHeaders(headers: Record<string, string>): void;
46
46
  getEndpoint(): string;
47
47
  }
48
+ export type SubscriptionOperation = 'INSERT' | 'UPDATE' | 'DELETE';
49
+ export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
50
+ export type ConnectionStateListener = (state: ConnectionState) => void;
51
+ export type Unsubscribe = () => void;
52
+ export interface SubscriptionEvent<T> {
53
+ operation: SubscriptionOperation;
54
+ data: T | null;
55
+ previousValues?: Partial<T>;
56
+ timestamp: string;
57
+ }
58
+ export interface SubscriptionFieldMeta {
59
+ fieldName: string;
60
+ tableName: string;
61
+ dataFieldName: string;
62
+ }
63
+ export interface SubscribeOptions<T, TFilter = Record<string, unknown>> {
64
+ filter?: TFilter;
65
+ onEvent: (event: SubscriptionEvent<T>) => void;
66
+ onError?: (error: Error) => void;
67
+ onComplete?: () => void;
68
+ }
69
+ export interface RealtimeConfig {
70
+ url: string;
71
+ getToken?: () => string | Promise<string>;
72
+ connectionParams?: Record<string, unknown>;
73
+ lazy?: boolean;
74
+ retryAttempts?: number;
75
+ retryWait?: number | ((retryCount: number) => number | Promise<number>);
76
+ onConnected?: () => void;
77
+ onDisconnected?: (reason?: unknown) => void;
78
+ }
48
79
  /**
49
80
  * Configuration for creating an ORM client.
50
81
  */
@@ -55,6 +86,8 @@ export interface OrmClientConfig {
55
86
  headers?: Record<string, string>;
56
87
  /** Custom adapter for GraphQL execution (overrides endpoint/headers) */
57
88
  adapter?: GraphQLAdapter;
89
+ /** Optional realtime (WebSocket) configuration */
90
+ realtime?: RealtimeConfig;
58
91
  }
59
92
  export declare class GraphQLRequestError extends Error {
60
93
  readonly errors: GraphQLError[];
@@ -65,6 +98,16 @@ export declare class OrmClient {
65
98
  private adapter;
66
99
  constructor(config: OrmClientConfig);
67
100
  execute<T>(document: string, variables?: Record<string, unknown>): Promise<QueryResult<T>>;
101
+ subscribe<T>(meta: SubscriptionFieldMeta, document: string, variables: Record<string, unknown>, options: {
102
+ onEvent: (event: SubscriptionEvent<T>) => void;
103
+ onError?: (error: Error) => void;
104
+ onComplete?: () => void;
105
+ }): Unsubscribe;
68
106
  setHeaders(headers: Record<string, string>): void;
69
107
  getEndpoint(): string;
108
+ getConnectionState(): ConnectionState;
109
+ onConnectionStateChange(listener: ConnectionStateListener): Unsubscribe;
110
+ getActiveSubscriptionCount(): number;
111
+ get isRealtimeEnabled(): boolean;
112
+ dispose(): void;
70
113
  }
@@ -87,6 +87,9 @@ export class OrmClient {
87
87
  async execute(document, variables) {
88
88
  return this.adapter.execute(document, variables);
89
89
  }
90
+ subscribe(meta, document, variables, options) {
91
+ throw new Error('Realtime not configured');
92
+ }
90
93
  setHeaders(headers) {
91
94
  if (this.adapter.setHeaders) {
92
95
  this.adapter.setHeaders(headers);
@@ -95,4 +98,17 @@ export class OrmClient {
95
98
  getEndpoint() {
96
99
  return this.adapter.getEndpoint?.() ?? '';
97
100
  }
101
+ getConnectionState() {
102
+ return 'disconnected';
103
+ }
104
+ onConnectionStateChange(listener) {
105
+ return () => { };
106
+ }
107
+ getActiveSubscriptionCount() {
108
+ return 0;
109
+ }
110
+ get isRealtimeEnabled() {
111
+ return false;
112
+ }
113
+ dispose() { }
98
114
  }
@@ -40,6 +40,6 @@ export interface GenerateOrmResult {
40
40
  */
41
41
  export declare function generateOrm(options: GenerateOrmOptions): GenerateOrmResult;
42
42
  export { generateModelsBarrel, generateTypesBarrel } from './barrel';
43
- export { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
43
+ export { generateOrmClientFile, generateQueryBuilderFile, generateRealtimeFile, generateSelectTypesFile, } from './client-generator';
44
44
  export { generateCustomMutationOpsFile, generateCustomQueryOpsFile, } from './custom-ops-generator';
45
45
  export { generateAllModelFiles, generateModelFile } from './model-generator';
@@ -1,5 +1,5 @@
1
1
  import { generateModelsBarrel, generateTypesBarrel } from './barrel';
2
- import { generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
2
+ import { generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, generateRealtimeFile, generateSelectTypesFile, } from './client-generator';
3
3
  import { generateCustomMutationOpsFile, generateCustomQueryOpsFile, } from './custom-ops-generator';
4
4
  import { collectInputTypeNames, collectPayloadTypeNames, generateInputTypesFile, } from './input-types-generator';
5
5
  import { generateAllModelFiles } from './model-generator';
@@ -15,9 +15,11 @@ export function generateOrm(options) {
15
15
  const hasCustomQueries = (customOperations?.queries.length ?? 0) > 0;
16
16
  const hasCustomMutations = (customOperations?.mutations.length ?? 0) > 0;
17
17
  const typeRegistry = customOperations?.typeRegistry;
18
- // 1. Generate runtime files (client, query-builder, select-types)
18
+ // 1. Generate runtime files (client, query-builder, select-types, realtime)
19
19
  const clientFile = generateOrmClientFile();
20
20
  files.push({ path: clientFile.fileName, content: clientFile.content });
21
+ const realtimeFile = generateRealtimeFile();
22
+ files.push({ path: realtimeFile.fileName, content: realtimeFile.content });
21
23
  const queryBuilderFile = generateQueryBuilderFile();
22
24
  files.push({
23
25
  path: queryBuilderFile.fileName,
@@ -102,6 +104,6 @@ export function generateOrm(options) {
102
104
  }
103
105
  // Re-export generators for direct use
104
106
  export { generateModelsBarrel, generateTypesBarrel } from './barrel';
105
- export { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
107
+ export { generateOrmClientFile, generateQueryBuilderFile, generateRealtimeFile, generateSelectTypesFile, } from './client-generator';
106
108
  export { generateCustomMutationOpsFile, generateCustomQueryOpsFile, } from './custom-ops-generator';
107
109
  export { generateAllModelFiles, generateModelFile } from './model-generator';
@@ -0,0 +1,30 @@
1
+ import type { Table } from '../../types/schema';
2
+ export interface GeneratedSubscriptionFile {
3
+ fileName: string;
4
+ content: string;
5
+ }
6
+ /**
7
+ * Generate a subscription hook for a table.
8
+ *
9
+ * Produces a React hook that calls `getClient().subscribe()` with the
10
+ * correct subscription document, field metadata, and typed callbacks.
11
+ *
12
+ * Example generated output:
13
+ * ```ts
14
+ * export function useContactSubscription(options: ContactSubscriptionOptions): Unsubscribe {
15
+ * ...
16
+ * }
17
+ * ```
18
+ */
19
+ export declare function generateSubscriptionHook(table: Table): GeneratedSubscriptionFile;
20
+ /**
21
+ * Generate the useConnectionState hook file.
22
+ *
23
+ * This hook exposes the WebSocket connection state from the ORM client
24
+ * so UI components can show connection indicators.
25
+ */
26
+ export declare function generateConnectionStateHook(): GeneratedSubscriptionFile;
27
+ /**
28
+ * Generate subscription hooks for all tables
29
+ */
30
+ export declare function generateAllSubscriptionHooks(tables: Table[]): GeneratedSubscriptionFile[];
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Subscription hook generators - delegates to ORM client subscribe (Babel AST-based)
3
+ *
4
+ * Output structure:
5
+ * subscriptions/
6
+ * useContactSubscription.ts - Subscription hook -> ORM client.subscribe()
7
+ * useConnectionState.ts - Connection state hook
8
+ */
9
+ import * as t from '@babel/types';
10
+ import { addJSDocComment, callExpr, constDecl, createFunctionParam, createImportDeclaration, createTypeReExport, exportFunction, generateHookFileCode, objectProp, typeRef, } from './hooks-ast';
11
+ import { getSubscriptionFieldName, getSubscriptionFileName, getSubscriptionHookName, getTableNames, lcFirst, } from './utils';
12
+ /**
13
+ * Generate a subscription hook for a table.
14
+ *
15
+ * Produces a React hook that calls `getClient().subscribe()` with the
16
+ * correct subscription document, field metadata, and typed callbacks.
17
+ *
18
+ * Example generated output:
19
+ * ```ts
20
+ * export function useContactSubscription(options: ContactSubscriptionOptions): Unsubscribe {
21
+ * ...
22
+ * }
23
+ * ```
24
+ */
25
+ export function generateSubscriptionHook(table) {
26
+ const { typeName, singularName } = getTableNames(table);
27
+ const hookName = getSubscriptionHookName(table);
28
+ const subscriptionFieldName = getSubscriptionFieldName(table);
29
+ const keysName = `${lcFirst(typeName)}Keys`;
30
+ const statements = [];
31
+ // Imports
32
+ statements.push(createImportDeclaration('react', ['useEffect', 'useRef', 'useCallback']));
33
+ statements.push(createImportDeclaration('@tanstack/react-query', ['useQueryClient']));
34
+ statements.push(createImportDeclaration('@tanstack/react-query', ['QueryClient'], true));
35
+ statements.push(createImportDeclaration('../client', ['getClient']));
36
+ statements.push(createImportDeclaration('../../orm/client', [
37
+ 'SubscriptionEvent',
38
+ 'SubscriptionFieldMeta',
39
+ 'Unsubscribe',
40
+ ], true));
41
+ statements.push(createImportDeclaration('../../orm/input-types', [typeName], true));
42
+ statements.push(createImportDeclaration('../query-keys', [keysName]));
43
+ // Re-export SubscriptionEvent for consumer convenience
44
+ statements.push(createTypeReExport(['SubscriptionEvent', 'Unsubscribe'], '../../orm/client'));
45
+ // Subscription document constant
46
+ const subscriptionDoc = `subscription On${typeName}Changed {
47
+ ${subscriptionFieldName} {
48
+ event
49
+ ${singularName} { __typename }
50
+ timestamp
51
+ }
52
+ }`;
53
+ const docDecl = constDecl('SUBSCRIPTION_DOCUMENT', t.stringLiteral(subscriptionDoc));
54
+ statements.push(docDecl);
55
+ // Field metadata constant
56
+ const metaDecl = t.variableDeclaration('const', [
57
+ t.variableDeclarator(t.identifier('FIELD_META'), t.objectExpression([
58
+ objectProp('fieldName', t.stringLiteral(subscriptionFieldName)),
59
+ objectProp('tableName', t.stringLiteral(singularName)),
60
+ objectProp('dataFieldName', t.stringLiteral(singularName)),
61
+ ])),
62
+ ]);
63
+ // Add type annotation: SubscriptionFieldMeta
64
+ const metaId = metaDecl.declarations[0].id;
65
+ metaId.typeAnnotation = t.tsTypeAnnotation(typeRef('SubscriptionFieldMeta'));
66
+ statements.push(metaDecl);
67
+ // Options interface
68
+ const optionsTypeName = `${typeName}SubscriptionOptions`;
69
+ const optionsInterface = t.tsInterfaceDeclaration(t.identifier(optionsTypeName), null, null, t.tsInterfaceBody([
70
+ (() => {
71
+ const p = t.tsPropertySignature(t.identifier('onEvent'), t.tsTypeAnnotation(t.tsFunctionType(null, [
72
+ createFunctionParam('event', typeRef('SubscriptionEvent', [typeRef(typeName)])),
73
+ ], t.tsTypeAnnotation(t.tsVoidKeyword()))));
74
+ return p;
75
+ })(),
76
+ (() => {
77
+ const p = t.tsPropertySignature(t.identifier('onError'), t.tsTypeAnnotation(t.tsFunctionType(null, [createFunctionParam('error', typeRef('Error'))], t.tsTypeAnnotation(t.tsVoidKeyword()))));
78
+ p.optional = true;
79
+ return p;
80
+ })(),
81
+ (() => {
82
+ const p = t.tsPropertySignature(t.identifier('enabled'), t.tsTypeAnnotation(t.tsBooleanKeyword()));
83
+ p.optional = true;
84
+ return p;
85
+ })(),
86
+ (() => {
87
+ const p = t.tsPropertySignature(t.identifier('invalidateQueries'), t.tsTypeAnnotation(t.tsBooleanKeyword()));
88
+ p.optional = true;
89
+ return p;
90
+ })(),
91
+ ]));
92
+ statements.push(t.exportNamedDeclaration(optionsInterface));
93
+ // Hook implementation
94
+ const hookBody = [];
95
+ // const queryClient = useQueryClient();
96
+ hookBody.push(constDecl('queryClient', callExpr('useQueryClient', [])));
97
+ // const optionsRef = useRef(options);
98
+ hookBody.push(constDecl('optionsRef', callExpr('useRef', [t.identifier('options')])));
99
+ // optionsRef.current = options;
100
+ hookBody.push(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('options'))));
101
+ // useEffect with subscribe
102
+ const effectBody = [];
103
+ // if (options.enabled === false) return;
104
+ effectBody.push(t.ifStatement(t.binaryExpression('===', t.memberExpression(t.identifier('options'), t.identifier('enabled')), t.booleanLiteral(false)), t.returnStatement(null)));
105
+ // const client = getClient();
106
+ effectBody.push(constDecl('client', callExpr('getClient', [])));
107
+ // if (!client.isRealtimeEnabled) return;
108
+ effectBody.push(t.ifStatement(t.unaryExpression('!', t.memberExpression(t.identifier('client'), t.identifier('isRealtimeEnabled'))), t.returnStatement(null)));
109
+ // const unsubscribe = client.subscribe(FIELD_META, SUBSCRIPTION_DOCUMENT, {}, { onEvent, onError, onComplete });
110
+ const subscribeCall = t.callExpression(t.memberExpression(t.identifier('client'), t.identifier('subscribe')), [
111
+ t.identifier('FIELD_META'),
112
+ t.identifier('SUBSCRIPTION_DOCUMENT'),
113
+ t.objectExpression([]),
114
+ t.objectExpression([
115
+ objectProp('onEvent', t.arrowFunctionExpression([t.identifier('event')], t.blockStatement([
116
+ // optionsRef.current.onEvent(event);
117
+ t.expressionStatement(callExpr(t.memberExpression(t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('onEvent')), [t.identifier('event')])),
118
+ // if (optionsRef.current.invalidateQueries !== false) { queryClient.invalidateQueries({ queryKey: keysName.all }); }
119
+ t.ifStatement(t.binaryExpression('!==', t.memberExpression(t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('invalidateQueries')), t.booleanLiteral(false)), t.expressionStatement(callExpr(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [
120
+ t.objectExpression([
121
+ objectProp('queryKey', t.memberExpression(t.identifier(keysName), t.identifier('all'))),
122
+ ]),
123
+ ]))),
124
+ ]))),
125
+ objectProp('onError', t.arrowFunctionExpression([t.identifier('err')], t.blockStatement([
126
+ t.expressionStatement(t.optionalCallExpression(t.optionalMemberExpression(t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('onError'), false, true), [t.identifier('err')], false)),
127
+ ]))),
128
+ ]),
129
+ ]);
130
+ effectBody.push(constDecl('unsubscribe', subscribeCall));
131
+ // return () => unsubscribe();
132
+ effectBody.push(t.returnStatement(t.arrowFunctionExpression([], t.callExpression(t.identifier('unsubscribe'), []))));
133
+ // useEffect(() => { ... }, [options.enabled]);
134
+ const effectFn = t.arrowFunctionExpression([], t.blockStatement(effectBody));
135
+ hookBody.push(t.expressionStatement(callExpr('useEffect', [
136
+ effectFn,
137
+ t.arrayExpression([
138
+ t.memberExpression(t.identifier('options'), t.identifier('enabled')),
139
+ t.identifier('queryClient'),
140
+ ]),
141
+ ])));
142
+ // Hook declaration
143
+ const hookParam = createFunctionParam('options', typeRef(optionsTypeName));
144
+ const hookDecl = exportFunction(hookName, null, [hookParam], hookBody, t.tsVoidKeyword());
145
+ addJSDocComment(hookDecl, [
146
+ `Subscription hook for ${typeName} realtime events`,
147
+ '',
148
+ 'Subscribes to realtime changes on the server and automatically',
149
+ 'invalidates React Query cache when events are received.',
150
+ '',
151
+ '@example',
152
+ '```tsx',
153
+ `${hookName}({`,
154
+ ' onEvent: (event) => {',
155
+ ` console.log(event.operation, event.data);`,
156
+ ' },',
157
+ '});',
158
+ '```',
159
+ ]);
160
+ statements.push(hookDecl);
161
+ return {
162
+ fileName: getSubscriptionFileName(table),
163
+ content: generateHookFileCode(`Subscription hook for ${typeName}`, statements),
164
+ };
165
+ }
166
+ /**
167
+ * Generate the useConnectionState hook file.
168
+ *
169
+ * This hook exposes the WebSocket connection state from the ORM client
170
+ * so UI components can show connection indicators.
171
+ */
172
+ export function generateConnectionStateHook() {
173
+ const statements = [];
174
+ // Imports
175
+ statements.push(createImportDeclaration('react', ['useState', 'useEffect']));
176
+ statements.push(createImportDeclaration('../client', ['getClient']));
177
+ statements.push(createImportDeclaration('../../orm/client', ['ConnectionState'], true));
178
+ // Re-export ConnectionState
179
+ statements.push(createTypeReExport(['ConnectionState'], '../../orm/client'));
180
+ // Hook body
181
+ const hookBody = [];
182
+ // const [state, setState] = useState<ConnectionState>(() => getClient().getConnectionState());
183
+ const initFn = t.arrowFunctionExpression([], callExpr(t.memberExpression(callExpr('getClient', []), t.identifier('getConnectionState')), []));
184
+ const useStateCall = callExpr('useState', [initFn]);
185
+ // @ts-ignore - typeParameters on CallExpression for TS
186
+ useStateCall.typeParameters = t.tsTypeParameterInstantiation([
187
+ typeRef('ConnectionState'),
188
+ ]);
189
+ hookBody.push(t.variableDeclaration('const', [
190
+ t.variableDeclarator(t.arrayPattern([t.identifier('state'), t.identifier('setState')]), useStateCall),
191
+ ]));
192
+ // useEffect
193
+ const effectBody = [];
194
+ effectBody.push(constDecl('client', callExpr('getClient', [])));
195
+ // if (!client.isRealtimeEnabled) return;
196
+ effectBody.push(t.ifStatement(t.unaryExpression('!', t.memberExpression(t.identifier('client'), t.identifier('isRealtimeEnabled'))), t.returnStatement(null)));
197
+ // const unsubscribe = client.onConnectionStateChange(setState);
198
+ effectBody.push(constDecl('unsubscribe', callExpr(t.memberExpression(t.identifier('client'), t.identifier('onConnectionStateChange')), [t.identifier('setState')])));
199
+ // return () => unsubscribe();
200
+ effectBody.push(t.returnStatement(t.arrowFunctionExpression([], t.callExpression(t.identifier('unsubscribe'), []))));
201
+ hookBody.push(t.expressionStatement(callExpr('useEffect', [
202
+ t.arrowFunctionExpression([], t.blockStatement(effectBody)),
203
+ t.arrayExpression([]),
204
+ ])));
205
+ // return state;
206
+ hookBody.push(t.returnStatement(t.identifier('state')));
207
+ // Hook declaration
208
+ const hookDecl = exportFunction('useConnectionState', null, [], hookBody, typeRef('ConnectionState'));
209
+ addJSDocComment(hookDecl, [
210
+ 'Hook to observe the WebSocket connection state.',
211
+ '',
212
+ 'Returns the current connection state of the realtime WebSocket.',
213
+ "Returns 'disconnected' if realtime is not configured.",
214
+ '',
215
+ '@example',
216
+ '```tsx',
217
+ 'const state = useConnectionState();',
218
+ "// state: 'disconnected' | 'connecting' | 'connected' | 'reconnecting'",
219
+ '```',
220
+ ]);
221
+ statements.push(hookDecl);
222
+ return {
223
+ fileName: 'useConnectionState.ts',
224
+ content: generateHookFileCode('WebSocket connection state hook', statements),
225
+ };
226
+ }
227
+ /**
228
+ * Generate subscription hooks for all tables
229
+ */
230
+ export function generateAllSubscriptionHooks(tables) {
231
+ return tables.map((table) => generateSubscriptionHook(table));
232
+ }
@@ -65,6 +65,21 @@ export declare function getUpdateMutationFileName(table: Table): string;
65
65
  * Generate file name for delete mutation hook
66
66
  */
67
67
  export declare function getDeleteMutationFileName(table: Table): string;
68
+ /**
69
+ * Generate hook function name for subscription
70
+ * e.g., "useContactSubscription"
71
+ */
72
+ export declare function getSubscriptionHookName(table: Table): string;
73
+ /**
74
+ * Generate file name for subscription hook
75
+ * e.g., "useContactSubscription.ts"
76
+ */
77
+ export declare function getSubscriptionFileName(table: Table): string;
78
+ /**
79
+ * Generate the GraphQL subscription field name
80
+ * e.g., "onContactChanged"
81
+ */
82
+ export declare function getSubscriptionFieldName(table: Table): string;
68
83
  /**
69
84
  * Get the GraphQL query name for fetching all rows
70
85
  * Uses inflection from introspection, falls back to convention
@@ -94,6 +94,29 @@ export function getUpdateMutationFileName(table) {
94
94
  export function getDeleteMutationFileName(table) {
95
95
  return `${getDeleteMutationHookName(table)}.ts`;
96
96
  }
97
+ /**
98
+ * Generate hook function name for subscription
99
+ * e.g., "useContactSubscription"
100
+ */
101
+ export function getSubscriptionHookName(table) {
102
+ const { singularName } = getTableNames(table);
103
+ return `use${ucFirst(singularName)}Subscription`;
104
+ }
105
+ /**
106
+ * Generate file name for subscription hook
107
+ * e.g., "useContactSubscription.ts"
108
+ */
109
+ export function getSubscriptionFileName(table) {
110
+ return `${getSubscriptionHookName(table)}.ts`;
111
+ }
112
+ /**
113
+ * Generate the GraphQL subscription field name
114
+ * e.g., "onContactChanged"
115
+ */
116
+ export function getSubscriptionFieldName(table) {
117
+ const { singularName } = getTableNames(table);
118
+ return `on${ucFirst(singularName)}Changed`;
119
+ }
97
120
  // ============================================================================
98
121
  // GraphQL operation names
99
122
  // ============================================================================
@@ -17,6 +17,8 @@ export interface Table {
17
17
  query?: TableQueryNames;
18
18
  /** Constraint information */
19
19
  constraints?: TableConstraints;
20
+ /** Smart tags parsed from PostGraphile @-prefixed comment directives */
21
+ smartTags?: Record<string, string | true>;
20
22
  }
21
23
  /**
22
24
  * PostGraphile-generated names for this table
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-codegen",
3
- "version": "4.38.1",
3
+ "version": "4.39.0",
4
4
  "description": "GraphQL SDK generator for Constructive databases with React Query hooks",
5
5
  "keywords": [
6
6
  "graphql",
@@ -56,7 +56,7 @@
56
56
  "@0no-co/graphql.web": "^1.1.2",
57
57
  "@babel/generator": "^7.29.1",
58
58
  "@babel/types": "^7.29.0",
59
- "@constructive-io/graphql-query": "^3.21.1",
59
+ "@constructive-io/graphql-query": "^3.22.0",
60
60
  "@constructive-io/graphql-types": "^3.8.0",
61
61
  "@inquirerer/utils": "^3.3.5",
62
62
  "@pgpmjs/core": "^6.16.1",
@@ -64,7 +64,7 @@
64
64
  "deepmerge": "^4.3.1",
65
65
  "find-and-require-package-json": "^0.9.1",
66
66
  "gql-ast": "^3.8.0",
67
- "graphile-schema": "^1.18.4",
67
+ "graphile-schema": "^1.18.6",
68
68
  "graphql": "16.13.0",
69
69
  "inflekt": "^0.7.1",
70
70
  "inquirerer": "^4.7.0",
@@ -100,5 +100,5 @@
100
100
  "tsx": "^4.21.0",
101
101
  "typescript": "^5.9.3"
102
102
  },
103
- "gitHead": "0fcb26c8f3379de5c2bf486e7ad78bb61a76e497"
103
+ "gitHead": "90016935b53d6fb84e0b83879377f0c2eb9abce6"
104
104
  }
package/types/schema.d.ts CHANGED
@@ -17,6 +17,8 @@ export interface Table {
17
17
  query?: TableQueryNames;
18
18
  /** Constraint information */
19
19
  constraints?: TableConstraints;
20
+ /** Smart tags parsed from PostGraphile @-prefixed comment directives */
21
+ smartTags?: Record<string, string | true>;
20
22
  }
21
23
  /**
22
24
  * PostGraphile-generated names for this table