@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.
@@ -15,6 +15,8 @@ export declare function generateMutationsBarrel(tables: Table[]): string;
15
15
  */
16
16
  export interface MainBarrelOptions {
17
17
  hasMutations?: boolean;
18
+ /** Whether subscriptions/ directory was generated */
19
+ hasSubscriptions?: boolean;
18
20
  /** Whether query-keys.ts was generated */
19
21
  hasQueryKeys?: boolean;
20
22
  /** Whether mutation-keys.ts was generated */
@@ -44,6 +46,10 @@ export declare function generateRootBarrel(options?: RootBarrelOptions): string;
44
46
  * export * as public_ from './public';
45
47
  */
46
48
  export declare function generateMultiTargetBarrel(targetNames: string[]): string;
49
+ /**
50
+ * Generate the subscriptions/index.ts barrel file
51
+ */
52
+ export declare function generateSubscriptionsBarrel(tables: Table[]): string;
47
53
  /**
48
54
  * Generate queries barrel including custom query operations
49
55
  */
@@ -38,6 +38,7 @@ exports.generateMutationsBarrel = generateMutationsBarrel;
38
38
  exports.generateMainBarrel = generateMainBarrel;
39
39
  exports.generateRootBarrel = generateRootBarrel;
40
40
  exports.generateMultiTargetBarrel = generateMultiTargetBarrel;
41
+ exports.generateSubscriptionsBarrel = generateSubscriptionsBarrel;
41
42
  exports.generateCustomQueriesBarrel = generateCustomQueriesBarrel;
42
43
  exports.generateCustomMutationsBarrel = generateCustomMutationsBarrel;
43
44
  /**
@@ -111,7 +112,7 @@ function generateMutationsBarrel(tables) {
111
112
  }
112
113
  function generateMainBarrel(tables, options = {}) {
113
114
  const opts = options;
114
- const { hasMutations = true, hasQueryKeys = false, hasMutationKeys = false, hasInvalidation = false, } = opts;
115
+ const { hasMutations = true, hasSubscriptions = false, hasQueryKeys = false, hasMutationKeys = false, hasInvalidation = false, } = opts;
115
116
  const tableNames = tables.map((tbl) => tbl.name).join(', ');
116
117
  const statements = [];
117
118
  // Client configuration (ORM wrapper with configure/getClient)
@@ -134,6 +135,10 @@ function generateMainBarrel(tables, options = {}) {
134
135
  if (hasMutations) {
135
136
  statements.push(exportAllFrom('./mutations'));
136
137
  }
138
+ // Subscription hooks
139
+ if (hasSubscriptions) {
140
+ statements.push(exportAllFrom('./subscriptions'));
141
+ }
137
142
  // Add file header as leading comment on first statement
138
143
  if (statements.length > 0) {
139
144
  (0, babel_ast_1.addJSDocComment)(statements[0], [
@@ -243,6 +248,26 @@ function generateMultiTargetBarrel(targetNames) {
243
248
  }
244
249
  return (0, babel_ast_1.generateCode)(statements);
245
250
  }
251
+ /**
252
+ * Generate the subscriptions/index.ts barrel file
253
+ */
254
+ function generateSubscriptionsBarrel(tables) {
255
+ const statements = [];
256
+ for (const table of tables) {
257
+ const hookName = (0, utils_1.getSubscriptionHookName)(table);
258
+ statements.push(exportAllFrom(`./${hookName}`));
259
+ }
260
+ // Connection state hook
261
+ statements.push(exportAllFrom('./useConnectionState'));
262
+ if (statements.length > 0) {
263
+ (0, babel_ast_1.addJSDocComment)(statements[0], [
264
+ 'Subscription hooks barrel export',
265
+ '@generated by @constructive-io/graphql-codegen',
266
+ 'DO NOT EDIT - changes will be overwritten',
267
+ ]);
268
+ }
269
+ return (0, babel_ast_1.generateCode)(statements);
270
+ }
246
271
  // ============================================================================
247
272
  // Custom operation barrels (includes both table and custom hooks)
248
273
  // ============================================================================
@@ -36,6 +36,7 @@ export interface GenerateResult {
36
36
  tables: number;
37
37
  queryHooks: number;
38
38
  mutationHooks: number;
39
+ subscriptionHooks: number;
39
40
  customQueryHooks: number;
40
41
  customMutationHooks: number;
41
42
  totalFiles: number;
@@ -71,7 +72,7 @@ export declare function generateAllFiles(tables: Table[], config: GraphQLSDKConf
71
72
  * (they're expected to exist in the shared types directory).
72
73
  */
73
74
  export declare function generate(options: GenerateOptions): GenerateResult;
74
- export { generateCustomMutationsBarrel, generateCustomQueriesBarrel, generateMainBarrel, generateMutationsBarrel, generateQueriesBarrel, } from './barrel';
75
+ export { generateCustomMutationsBarrel, generateCustomQueriesBarrel, generateMainBarrel, generateMutationsBarrel, generateQueriesBarrel, generateSubscriptionsBarrel, } from './barrel';
75
76
  export { generateClientFile } from './client';
76
77
  export { generateAllCustomMutationHooks, generateCustomMutationHook, } from './custom-mutations';
77
78
  export { generateAllCustomQueryHooks, generateCustomQueryHook, } from './custom-queries';
@@ -80,3 +81,4 @@ export { generateMutationKeysFile } from './mutation-keys';
80
81
  export { generateAllMutationHooks, generateCreateMutationHook, generateDeleteMutationHook, generateUpdateMutationHook, } from './mutations';
81
82
  export { generateAllQueryHooks, generateListQueryHook, generateSingleQueryHook, } from './queries';
82
83
  export { generateQueryKeysFile } from './query-keys';
84
+ export { generateAllSubscriptionHooks, generateConnectionStateHook, generateSubscriptionHook, } from './subscriptions';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateQueryKeysFile = exports.generateSingleQueryHook = exports.generateListQueryHook = exports.generateAllQueryHooks = exports.generateUpdateMutationHook = exports.generateDeleteMutationHook = exports.generateCreateMutationHook = exports.generateAllMutationHooks = exports.generateMutationKeysFile = exports.generateInvalidationFile = exports.generateCustomQueryHook = exports.generateAllCustomQueryHooks = exports.generateCustomMutationHook = exports.generateAllCustomMutationHooks = exports.generateClientFile = exports.generateQueriesBarrel = exports.generateMutationsBarrel = exports.generateMainBarrel = exports.generateCustomQueriesBarrel = exports.generateCustomMutationsBarrel = void 0;
3
+ exports.generateSubscriptionHook = exports.generateConnectionStateHook = exports.generateAllSubscriptionHooks = exports.generateQueryKeysFile = exports.generateSingleQueryHook = exports.generateListQueryHook = exports.generateAllQueryHooks = exports.generateUpdateMutationHook = exports.generateDeleteMutationHook = exports.generateCreateMutationHook = exports.generateAllMutationHooks = exports.generateMutationKeysFile = exports.generateInvalidationFile = exports.generateCustomQueryHook = exports.generateAllCustomQueryHooks = exports.generateCustomMutationHook = exports.generateAllCustomMutationHooks = exports.generateClientFile = exports.generateSubscriptionsBarrel = exports.generateQueriesBarrel = exports.generateMutationsBarrel = exports.generateMainBarrel = exports.generateCustomQueriesBarrel = exports.generateCustomMutationsBarrel = void 0;
4
4
  exports.generateAllFiles = generateAllFiles;
5
5
  exports.generate = generate;
6
6
  const config_1 = require("../../types/config");
@@ -13,6 +13,7 @@ const mutation_keys_1 = require("./mutation-keys");
13
13
  const mutations_1 = require("./mutations");
14
14
  const queries_1 = require("./queries");
15
15
  const query_keys_1 = require("./query-keys");
16
+ const subscriptions_1 = require("./subscriptions");
16
17
  const selection_1 = require("./selection");
17
18
  const utils_1 = require("./utils");
18
19
  // ============================================================================
@@ -173,12 +174,37 @@ function generate(options) {
173
174
  : (0, barrel_1.generateMutationsBarrel)(tables),
174
175
  });
175
176
  }
177
+ // 8b. Generate subscription hooks (subscriptions/*.ts)
178
+ // Only generate for tables with the @realtime smart tag
179
+ const realtimeTables = tables.filter((t) => t.smartTags?.['@realtime'] !== undefined);
180
+ const subscriptionHooks = (0, subscriptions_1.generateAllSubscriptionHooks)(realtimeTables);
181
+ for (const hook of subscriptionHooks) {
182
+ files.push({
183
+ path: `subscriptions/${hook.fileName}`,
184
+ content: hook.content,
185
+ });
186
+ }
187
+ // 8c. Generate connection state hook + barrel only if any table has @realtime
188
+ const hasSubscriptions = subscriptionHooks.length > 0;
189
+ if (hasSubscriptions) {
190
+ const connectionStateHook = (0, subscriptions_1.generateConnectionStateHook)();
191
+ files.push({
192
+ path: `subscriptions/${connectionStateHook.fileName}`,
193
+ content: connectionStateHook.content,
194
+ });
195
+ // 8d. Generate subscriptions/index.ts barrel
196
+ files.push({
197
+ path: 'subscriptions/index.ts',
198
+ content: (0, barrel_1.generateSubscriptionsBarrel)(realtimeTables),
199
+ });
200
+ }
176
201
  // 9. Generate main index.ts barrel
177
202
  // No longer includes types.ts or schema-types.ts - hooks import from ORM directly
178
203
  files.push({
179
204
  path: 'index.ts',
180
205
  content: (0, barrel_1.generateMainBarrel)(tables, {
181
206
  hasMutations,
207
+ hasSubscriptions,
182
208
  hasQueryKeys,
183
209
  hasMutationKeys,
184
210
  hasInvalidation,
@@ -190,6 +216,7 @@ function generate(options) {
190
216
  tables: tables.length,
191
217
  queryHooks: queryHooks.length,
192
218
  mutationHooks: mutationHooks.length,
219
+ subscriptionHooks: subscriptionHooks.length,
193
220
  customQueryHooks: customQueryHooks.length,
194
221
  customMutationHooks: customMutationHooks.length,
195
222
  totalFiles: files.length,
@@ -205,6 +232,7 @@ Object.defineProperty(exports, "generateCustomQueriesBarrel", { enumerable: true
205
232
  Object.defineProperty(exports, "generateMainBarrel", { enumerable: true, get: function () { return barrel_2.generateMainBarrel; } });
206
233
  Object.defineProperty(exports, "generateMutationsBarrel", { enumerable: true, get: function () { return barrel_2.generateMutationsBarrel; } });
207
234
  Object.defineProperty(exports, "generateQueriesBarrel", { enumerable: true, get: function () { return barrel_2.generateQueriesBarrel; } });
235
+ Object.defineProperty(exports, "generateSubscriptionsBarrel", { enumerable: true, get: function () { return barrel_2.generateSubscriptionsBarrel; } });
208
236
  var client_2 = require("./client");
209
237
  Object.defineProperty(exports, "generateClientFile", { enumerable: true, get: function () { return client_2.generateClientFile; } });
210
238
  var custom_mutations_2 = require("./custom-mutations");
@@ -228,3 +256,7 @@ Object.defineProperty(exports, "generateListQueryHook", { enumerable: true, get:
228
256
  Object.defineProperty(exports, "generateSingleQueryHook", { enumerable: true, get: function () { return queries_2.generateSingleQueryHook; } });
229
257
  var query_keys_2 = require("./query-keys");
230
258
  Object.defineProperty(exports, "generateQueryKeysFile", { enumerable: true, get: function () { return query_keys_2.generateQueryKeysFile; } });
259
+ var subscriptions_2 = require("./subscriptions");
260
+ Object.defineProperty(exports, "generateAllSubscriptionHooks", { enumerable: true, get: function () { return subscriptions_2.generateAllSubscriptionHooks; } });
261
+ Object.defineProperty(exports, "generateConnectionStateHook", { enumerable: true, get: function () { return subscriptions_2.generateConnectionStateHook; } });
262
+ Object.defineProperty(exports, "generateSubscriptionHook", { enumerable: true, get: function () { return subscriptions_2.generateSubscriptionHook; } });
@@ -10,6 +10,12 @@ export interface GeneratedClientFile {
10
10
  * Reads from the templates directory for proper type checking.
11
11
  */
12
12
  export declare function generateOrmClientFile(): GeneratedClientFile;
13
+ /**
14
+ * Generate the realtime.ts file (RealtimeManager + subscription types)
15
+ *
16
+ * Reads from the templates directory for proper type checking.
17
+ */
18
+ export declare function generateRealtimeFile(): GeneratedClientFile;
13
19
  /**
14
20
  * Generate the query-builder.ts file (runtime query builder)
15
21
  *
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.generateOrmClientFile = generateOrmClientFile;
37
+ exports.generateRealtimeFile = generateRealtimeFile;
37
38
  exports.generateQueryBuilderFile = generateQueryBuilderFile;
38
39
  exports.generateSelectTypesFile = generateSelectTypesFile;
39
40
  exports.generateCreateClientFile = generateCreateClientFile;
@@ -83,6 +84,17 @@ function generateOrmClientFile() {
83
84
  content: readTemplateFile('orm-client.ts', 'ORM Client - Runtime GraphQL executor'),
84
85
  };
85
86
  }
87
+ /**
88
+ * Generate the realtime.ts file (RealtimeManager + subscription types)
89
+ *
90
+ * Reads from the templates directory for proper type checking.
91
+ */
92
+ function generateRealtimeFile() {
93
+ return {
94
+ fileName: 'realtime.ts',
95
+ content: readTemplateFile('orm-realtime.ts', 'Realtime Manager - WebSocket subscription support'),
96
+ };
97
+ }
86
98
  /**
87
99
  * Generate the query-builder.ts file (runtime query builder)
88
100
  *
@@ -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
  }
@@ -92,6 +92,9 @@ class OrmClient {
92
92
  async execute(document, variables) {
93
93
  return this.adapter.execute(document, variables);
94
94
  }
95
+ subscribe(meta, document, variables, options) {
96
+ throw new Error('Realtime not configured');
97
+ }
95
98
  setHeaders(headers) {
96
99
  if (this.adapter.setHeaders) {
97
100
  this.adapter.setHeaders(headers);
@@ -100,5 +103,18 @@ class OrmClient {
100
103
  getEndpoint() {
101
104
  return this.adapter.getEndpoint?.() ?? '';
102
105
  }
106
+ getConnectionState() {
107
+ return 'disconnected';
108
+ }
109
+ onConnectionStateChange(listener) {
110
+ return () => { };
111
+ }
112
+ getActiveSubscriptionCount() {
113
+ return 0;
114
+ }
115
+ get isRealtimeEnabled() {
116
+ return false;
117
+ }
118
+ dispose() { }
103
119
  }
104
120
  exports.OrmClient = OrmClient;
@@ -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,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateModelFile = exports.generateAllModelFiles = exports.generateCustomQueryOpsFile = exports.generateCustomMutationOpsFile = exports.generateSelectTypesFile = exports.generateQueryBuilderFile = exports.generateOrmClientFile = exports.generateTypesBarrel = exports.generateModelsBarrel = void 0;
3
+ exports.generateModelFile = exports.generateAllModelFiles = exports.generateCustomQueryOpsFile = exports.generateCustomMutationOpsFile = exports.generateSelectTypesFile = exports.generateRealtimeFile = exports.generateQueryBuilderFile = exports.generateOrmClientFile = exports.generateTypesBarrel = exports.generateModelsBarrel = void 0;
4
4
  exports.generateOrm = generateOrm;
5
5
  const barrel_1 = require("./barrel");
6
6
  const client_generator_1 = require("./client-generator");
@@ -19,9 +19,11 @@ function generateOrm(options) {
19
19
  const hasCustomQueries = (customOperations?.queries.length ?? 0) > 0;
20
20
  const hasCustomMutations = (customOperations?.mutations.length ?? 0) > 0;
21
21
  const typeRegistry = customOperations?.typeRegistry;
22
- // 1. Generate runtime files (client, query-builder, select-types)
22
+ // 1. Generate runtime files (client, query-builder, select-types, realtime)
23
23
  const clientFile = (0, client_generator_1.generateOrmClientFile)();
24
24
  files.push({ path: clientFile.fileName, content: clientFile.content });
25
+ const realtimeFile = (0, client_generator_1.generateRealtimeFile)();
26
+ files.push({ path: realtimeFile.fileName, content: realtimeFile.content });
25
27
  const queryBuilderFile = (0, client_generator_1.generateQueryBuilderFile)();
26
28
  files.push({
27
29
  path: queryBuilderFile.fileName,
@@ -111,6 +113,7 @@ Object.defineProperty(exports, "generateTypesBarrel", { enumerable: true, get: f
111
113
  var client_generator_2 = require("./client-generator");
112
114
  Object.defineProperty(exports, "generateOrmClientFile", { enumerable: true, get: function () { return client_generator_2.generateOrmClientFile; } });
113
115
  Object.defineProperty(exports, "generateQueryBuilderFile", { enumerable: true, get: function () { return client_generator_2.generateQueryBuilderFile; } });
116
+ Object.defineProperty(exports, "generateRealtimeFile", { enumerable: true, get: function () { return client_generator_2.generateRealtimeFile; } });
114
117
  Object.defineProperty(exports, "generateSelectTypesFile", { enumerable: true, get: function () { return client_generator_2.generateSelectTypesFile; } });
115
118
  var custom_ops_generator_2 = require("./custom-ops-generator");
116
119
  Object.defineProperty(exports, "generateCustomMutationOpsFile", { enumerable: true, get: function () { return custom_ops_generator_2.generateCustomMutationOpsFile; } });
@@ -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,270 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateSubscriptionHook = generateSubscriptionHook;
37
+ exports.generateConnectionStateHook = generateConnectionStateHook;
38
+ exports.generateAllSubscriptionHooks = generateAllSubscriptionHooks;
39
+ /**
40
+ * Subscription hook generators - delegates to ORM client subscribe (Babel AST-based)
41
+ *
42
+ * Output structure:
43
+ * subscriptions/
44
+ * useContactSubscription.ts - Subscription hook -> ORM client.subscribe()
45
+ * useConnectionState.ts - Connection state hook
46
+ */
47
+ const t = __importStar(require("@babel/types"));
48
+ const hooks_ast_1 = require("./hooks-ast");
49
+ const utils_1 = require("./utils");
50
+ /**
51
+ * Generate a subscription hook for a table.
52
+ *
53
+ * Produces a React hook that calls `getClient().subscribe()` with the
54
+ * correct subscription document, field metadata, and typed callbacks.
55
+ *
56
+ * Example generated output:
57
+ * ```ts
58
+ * export function useContactSubscription(options: ContactSubscriptionOptions): Unsubscribe {
59
+ * ...
60
+ * }
61
+ * ```
62
+ */
63
+ function generateSubscriptionHook(table) {
64
+ const { typeName, singularName } = (0, utils_1.getTableNames)(table);
65
+ const hookName = (0, utils_1.getSubscriptionHookName)(table);
66
+ const subscriptionFieldName = (0, utils_1.getSubscriptionFieldName)(table);
67
+ const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
68
+ const statements = [];
69
+ // Imports
70
+ statements.push((0, hooks_ast_1.createImportDeclaration)('react', ['useEffect', 'useRef', 'useCallback']));
71
+ statements.push((0, hooks_ast_1.createImportDeclaration)('@tanstack/react-query', ['useQueryClient']));
72
+ statements.push((0, hooks_ast_1.createImportDeclaration)('@tanstack/react-query', ['QueryClient'], true));
73
+ statements.push((0, hooks_ast_1.createImportDeclaration)('../client', ['getClient']));
74
+ statements.push((0, hooks_ast_1.createImportDeclaration)('../../orm/client', [
75
+ 'SubscriptionEvent',
76
+ 'SubscriptionFieldMeta',
77
+ 'Unsubscribe',
78
+ ], true));
79
+ statements.push((0, hooks_ast_1.createImportDeclaration)('../../orm/input-types', [typeName], true));
80
+ statements.push((0, hooks_ast_1.createImportDeclaration)('../query-keys', [keysName]));
81
+ // Re-export SubscriptionEvent for consumer convenience
82
+ statements.push((0, hooks_ast_1.createTypeReExport)(['SubscriptionEvent', 'Unsubscribe'], '../../orm/client'));
83
+ // Subscription document constant
84
+ const subscriptionDoc = `subscription On${typeName}Changed {
85
+ ${subscriptionFieldName} {
86
+ event
87
+ ${singularName} { __typename }
88
+ timestamp
89
+ }
90
+ }`;
91
+ const docDecl = (0, hooks_ast_1.constDecl)('SUBSCRIPTION_DOCUMENT', t.stringLiteral(subscriptionDoc));
92
+ statements.push(docDecl);
93
+ // Field metadata constant
94
+ const metaDecl = t.variableDeclaration('const', [
95
+ t.variableDeclarator(t.identifier('FIELD_META'), t.objectExpression([
96
+ (0, hooks_ast_1.objectProp)('fieldName', t.stringLiteral(subscriptionFieldName)),
97
+ (0, hooks_ast_1.objectProp)('tableName', t.stringLiteral(singularName)),
98
+ (0, hooks_ast_1.objectProp)('dataFieldName', t.stringLiteral(singularName)),
99
+ ])),
100
+ ]);
101
+ // Add type annotation: SubscriptionFieldMeta
102
+ const metaId = metaDecl.declarations[0].id;
103
+ metaId.typeAnnotation = t.tsTypeAnnotation((0, hooks_ast_1.typeRef)('SubscriptionFieldMeta'));
104
+ statements.push(metaDecl);
105
+ // Options interface
106
+ const optionsTypeName = `${typeName}SubscriptionOptions`;
107
+ const optionsInterface = t.tsInterfaceDeclaration(t.identifier(optionsTypeName), null, null, t.tsInterfaceBody([
108
+ (() => {
109
+ const p = t.tsPropertySignature(t.identifier('onEvent'), t.tsTypeAnnotation(t.tsFunctionType(null, [
110
+ (0, hooks_ast_1.createFunctionParam)('event', (0, hooks_ast_1.typeRef)('SubscriptionEvent', [(0, hooks_ast_1.typeRef)(typeName)])),
111
+ ], t.tsTypeAnnotation(t.tsVoidKeyword()))));
112
+ return p;
113
+ })(),
114
+ (() => {
115
+ const p = t.tsPropertySignature(t.identifier('onError'), t.tsTypeAnnotation(t.tsFunctionType(null, [(0, hooks_ast_1.createFunctionParam)('error', (0, hooks_ast_1.typeRef)('Error'))], t.tsTypeAnnotation(t.tsVoidKeyword()))));
116
+ p.optional = true;
117
+ return p;
118
+ })(),
119
+ (() => {
120
+ const p = t.tsPropertySignature(t.identifier('enabled'), t.tsTypeAnnotation(t.tsBooleanKeyword()));
121
+ p.optional = true;
122
+ return p;
123
+ })(),
124
+ (() => {
125
+ const p = t.tsPropertySignature(t.identifier('invalidateQueries'), t.tsTypeAnnotation(t.tsBooleanKeyword()));
126
+ p.optional = true;
127
+ return p;
128
+ })(),
129
+ ]));
130
+ statements.push(t.exportNamedDeclaration(optionsInterface));
131
+ // Hook implementation
132
+ const hookBody = [];
133
+ // const queryClient = useQueryClient();
134
+ hookBody.push((0, hooks_ast_1.constDecl)('queryClient', (0, hooks_ast_1.callExpr)('useQueryClient', [])));
135
+ // const optionsRef = useRef(options);
136
+ hookBody.push((0, hooks_ast_1.constDecl)('optionsRef', (0, hooks_ast_1.callExpr)('useRef', [t.identifier('options')])));
137
+ // optionsRef.current = options;
138
+ hookBody.push(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('options'))));
139
+ // useEffect with subscribe
140
+ const effectBody = [];
141
+ // if (options.enabled === false) return;
142
+ effectBody.push(t.ifStatement(t.binaryExpression('===', t.memberExpression(t.identifier('options'), t.identifier('enabled')), t.booleanLiteral(false)), t.returnStatement(null)));
143
+ // const client = getClient();
144
+ effectBody.push((0, hooks_ast_1.constDecl)('client', (0, hooks_ast_1.callExpr)('getClient', [])));
145
+ // if (!client.isRealtimeEnabled) return;
146
+ effectBody.push(t.ifStatement(t.unaryExpression('!', t.memberExpression(t.identifier('client'), t.identifier('isRealtimeEnabled'))), t.returnStatement(null)));
147
+ // const unsubscribe = client.subscribe(FIELD_META, SUBSCRIPTION_DOCUMENT, {}, { onEvent, onError, onComplete });
148
+ const subscribeCall = t.callExpression(t.memberExpression(t.identifier('client'), t.identifier('subscribe')), [
149
+ t.identifier('FIELD_META'),
150
+ t.identifier('SUBSCRIPTION_DOCUMENT'),
151
+ t.objectExpression([]),
152
+ t.objectExpression([
153
+ (0, hooks_ast_1.objectProp)('onEvent', t.arrowFunctionExpression([t.identifier('event')], t.blockStatement([
154
+ // optionsRef.current.onEvent(event);
155
+ t.expressionStatement((0, hooks_ast_1.callExpr)(t.memberExpression(t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('onEvent')), [t.identifier('event')])),
156
+ // if (optionsRef.current.invalidateQueries !== false) { queryClient.invalidateQueries({ queryKey: keysName.all }); }
157
+ t.ifStatement(t.binaryExpression('!==', t.memberExpression(t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('invalidateQueries')), t.booleanLiteral(false)), t.expressionStatement((0, hooks_ast_1.callExpr)(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [
158
+ t.objectExpression([
159
+ (0, hooks_ast_1.objectProp)('queryKey', t.memberExpression(t.identifier(keysName), t.identifier('all'))),
160
+ ]),
161
+ ]))),
162
+ ]))),
163
+ (0, hooks_ast_1.objectProp)('onError', t.arrowFunctionExpression([t.identifier('err')], t.blockStatement([
164
+ t.expressionStatement(t.optionalCallExpression(t.optionalMemberExpression(t.memberExpression(t.identifier('optionsRef'), t.identifier('current')), t.identifier('onError'), false, true), [t.identifier('err')], false)),
165
+ ]))),
166
+ ]),
167
+ ]);
168
+ effectBody.push((0, hooks_ast_1.constDecl)('unsubscribe', subscribeCall));
169
+ // return () => unsubscribe();
170
+ effectBody.push(t.returnStatement(t.arrowFunctionExpression([], t.callExpression(t.identifier('unsubscribe'), []))));
171
+ // useEffect(() => { ... }, [options.enabled]);
172
+ const effectFn = t.arrowFunctionExpression([], t.blockStatement(effectBody));
173
+ hookBody.push(t.expressionStatement((0, hooks_ast_1.callExpr)('useEffect', [
174
+ effectFn,
175
+ t.arrayExpression([
176
+ t.memberExpression(t.identifier('options'), t.identifier('enabled')),
177
+ t.identifier('queryClient'),
178
+ ]),
179
+ ])));
180
+ // Hook declaration
181
+ const hookParam = (0, hooks_ast_1.createFunctionParam)('options', (0, hooks_ast_1.typeRef)(optionsTypeName));
182
+ const hookDecl = (0, hooks_ast_1.exportFunction)(hookName, null, [hookParam], hookBody, t.tsVoidKeyword());
183
+ (0, hooks_ast_1.addJSDocComment)(hookDecl, [
184
+ `Subscription hook for ${typeName} realtime events`,
185
+ '',
186
+ 'Subscribes to realtime changes on the server and automatically',
187
+ 'invalidates React Query cache when events are received.',
188
+ '',
189
+ '@example',
190
+ '```tsx',
191
+ `${hookName}({`,
192
+ ' onEvent: (event) => {',
193
+ ` console.log(event.operation, event.data);`,
194
+ ' },',
195
+ '});',
196
+ '```',
197
+ ]);
198
+ statements.push(hookDecl);
199
+ return {
200
+ fileName: (0, utils_1.getSubscriptionFileName)(table),
201
+ content: (0, hooks_ast_1.generateHookFileCode)(`Subscription hook for ${typeName}`, statements),
202
+ };
203
+ }
204
+ /**
205
+ * Generate the useConnectionState hook file.
206
+ *
207
+ * This hook exposes the WebSocket connection state from the ORM client
208
+ * so UI components can show connection indicators.
209
+ */
210
+ function generateConnectionStateHook() {
211
+ const statements = [];
212
+ // Imports
213
+ statements.push((0, hooks_ast_1.createImportDeclaration)('react', ['useState', 'useEffect']));
214
+ statements.push((0, hooks_ast_1.createImportDeclaration)('../client', ['getClient']));
215
+ statements.push((0, hooks_ast_1.createImportDeclaration)('../../orm/client', ['ConnectionState'], true));
216
+ // Re-export ConnectionState
217
+ statements.push((0, hooks_ast_1.createTypeReExport)(['ConnectionState'], '../../orm/client'));
218
+ // Hook body
219
+ const hookBody = [];
220
+ // const [state, setState] = useState<ConnectionState>(() => getClient().getConnectionState());
221
+ const initFn = t.arrowFunctionExpression([], (0, hooks_ast_1.callExpr)(t.memberExpression((0, hooks_ast_1.callExpr)('getClient', []), t.identifier('getConnectionState')), []));
222
+ const useStateCall = (0, hooks_ast_1.callExpr)('useState', [initFn]);
223
+ // @ts-ignore - typeParameters on CallExpression for TS
224
+ useStateCall.typeParameters = t.tsTypeParameterInstantiation([
225
+ (0, hooks_ast_1.typeRef)('ConnectionState'),
226
+ ]);
227
+ hookBody.push(t.variableDeclaration('const', [
228
+ t.variableDeclarator(t.arrayPattern([t.identifier('state'), t.identifier('setState')]), useStateCall),
229
+ ]));
230
+ // useEffect
231
+ const effectBody = [];
232
+ effectBody.push((0, hooks_ast_1.constDecl)('client', (0, hooks_ast_1.callExpr)('getClient', [])));
233
+ // if (!client.isRealtimeEnabled) return;
234
+ effectBody.push(t.ifStatement(t.unaryExpression('!', t.memberExpression(t.identifier('client'), t.identifier('isRealtimeEnabled'))), t.returnStatement(null)));
235
+ // const unsubscribe = client.onConnectionStateChange(setState);
236
+ effectBody.push((0, hooks_ast_1.constDecl)('unsubscribe', (0, hooks_ast_1.callExpr)(t.memberExpression(t.identifier('client'), t.identifier('onConnectionStateChange')), [t.identifier('setState')])));
237
+ // return () => unsubscribe();
238
+ effectBody.push(t.returnStatement(t.arrowFunctionExpression([], t.callExpression(t.identifier('unsubscribe'), []))));
239
+ hookBody.push(t.expressionStatement((0, hooks_ast_1.callExpr)('useEffect', [
240
+ t.arrowFunctionExpression([], t.blockStatement(effectBody)),
241
+ t.arrayExpression([]),
242
+ ])));
243
+ // return state;
244
+ hookBody.push(t.returnStatement(t.identifier('state')));
245
+ // Hook declaration
246
+ const hookDecl = (0, hooks_ast_1.exportFunction)('useConnectionState', null, [], hookBody, (0, hooks_ast_1.typeRef)('ConnectionState'));
247
+ (0, hooks_ast_1.addJSDocComment)(hookDecl, [
248
+ 'Hook to observe the WebSocket connection state.',
249
+ '',
250
+ 'Returns the current connection state of the realtime WebSocket.',
251
+ "Returns 'disconnected' if realtime is not configured.",
252
+ '',
253
+ '@example',
254
+ '```tsx',
255
+ 'const state = useConnectionState();',
256
+ "// state: 'disconnected' | 'connecting' | 'connected' | 'reconnecting'",
257
+ '```',
258
+ ]);
259
+ statements.push(hookDecl);
260
+ return {
261
+ fileName: 'useConnectionState.ts',
262
+ content: (0, hooks_ast_1.generateHookFileCode)('WebSocket connection state hook', statements),
263
+ };
264
+ }
265
+ /**
266
+ * Generate subscription hooks for all tables
267
+ */
268
+ function generateAllSubscriptionHooks(tables) {
269
+ return tables.map((table) => generateSubscriptionHook(table));
270
+ }