nextjs-hasura-auth 0.1.1 → 0.1.2

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,28 +15,67 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.Generator = Generator;
18
+ // @ts-ignore
19
+ const hasura_schema_json_1 = __importDefault(require("../public/hasura-schema.json")); // Импортируем результат интроспекции
18
20
  const debug_1 = __importDefault(require("./debug"));
19
- // @ts-ignore // Assuming debug.js is moved to lib/debug.ts or similar and returns a function
21
+ const core_1 = require("@apollo/client/core");
20
22
  const debug = (0, debug_1.default)('apollo:generator');
21
- const core_1 = require("@apollo/client/core"); // Use core for gql
22
- /**
23
- * Creates a GraphQL query generator based on the provided schema.
24
- *
25
- * @param schema - The GraphQL schema in schema.json format.
26
- * @returns A function to generate queries.
27
- */
23
+ // Получаем объект __schema из импортированного результата
24
+ // Добавляем базовую проверку
25
+ if (!hasura_schema_json_1.default.data || !hasura_schema_json_1.default.data.__schema) {
26
+ throw new Error('❌ Invalid introspection result format. Expected { data: { __schema: { ... } } }');
27
+ }
28
+ const schema = hasura_schema_json_1.default.data.__schema;
29
+ // --- Вспомогательная функция для разбора типа GraphQL ---
30
+ // Рекурсивно разбирает тип (обрабатывая NON_NULL и LIST) и возвращает базовое имя и флаги
31
+ function getTypeInfo(type) {
32
+ let isList = false;
33
+ let isNonNull = false;
34
+ let isNonNullItem = false; // Для проверки [Type!]
35
+ let currentType = type;
36
+ if (currentType.kind === 'NON_NULL') {
37
+ isNonNull = true;
38
+ currentType = currentType.ofType;
39
+ }
40
+ if (currentType.kind === 'LIST') {
41
+ isList = true;
42
+ currentType = currentType.ofType;
43
+ if (currentType.kind === 'NON_NULL') {
44
+ isNonNullItem = true;
45
+ currentType = currentType.ofType;
46
+ }
47
+ }
48
+ // Второй NON_NULL возможен для [Type!]!
49
+ if (currentType.kind === 'NON_NULL') {
50
+ isNonNullItem = true; // Если внешний LIST был NON_NULL, то и внутренний тоже
51
+ currentType = currentType.ofType;
52
+ }
53
+ return {
54
+ name: currentType.name || null, // Return null if name is missing
55
+ kind: currentType.kind,
56
+ isList,
57
+ isNonNull,
58
+ isNonNullItem,
59
+ };
60
+ }
61
+ // --- ---
28
62
  function Generator(schema) {
29
- // Check schema structure
30
- if (!schema || !schema.first_level_queries) {
31
- throw new Error('❌ Invalid schema format. Schema must contain first_level_queries field');
63
+ var _a, _b;
64
+ if (!schema || !schema.queryType || !schema.types) {
65
+ throw new Error('❌ Invalid schema format. Expected standard introspection __schema object.');
66
+ }
67
+ const queryRootName = schema.queryType.name;
68
+ const mutationRootName = (_a = schema.mutationType) === null || _a === void 0 ? void 0 : _a.name; // Может отсутствовать
69
+ const subscriptionRootName = (_b = schema.subscriptionType) === null || _b === void 0 ? void 0 : _b.name; // Может отсутствовать
70
+ // Находим детальные описания корневых типов
71
+ const queryRoot = schema.types.find((t) => t.kind === 'OBJECT' && t.name === queryRootName);
72
+ const mutationRoot = mutationRootName ? schema.types.find((t) => t.kind === 'OBJECT' && t.name === mutationRootName) : null;
73
+ const subscriptionRoot = subscriptionRootName ? schema.types.find((t) => t.kind === 'OBJECT' && t.name === subscriptionRootName) : null;
74
+ if (!queryRoot) {
75
+ throw new Error('❌ Query root type description not found in schema types.');
32
76
  }
33
- /**
34
- * Generates a GraphQL query based on the provided options.
35
- *
36
- * @param opts - Options object for query generation.
37
- * @returns The GraphQL query, variables, and current variable counter.
38
- */
39
77
  return function generate(opts) {
78
+ var _a, _b, _c;
40
79
  let varCounter = opts.varCounter || 1;
41
80
  if (!opts || !opts.operation || !opts.table) {
42
81
  throw new Error('❌ operation and table must be specified in options');
@@ -50,213 +89,126 @@ function Generator(schema) {
50
89
  if (!validOperations.includes(operation)) {
51
90
  throw new Error(`❌ Invalid operation type: ${operation}. Allowed types: ${validOperations.join(', ')}`);
52
91
  }
53
- let tableName = table;
54
- let schemaSection = 'first_level_queries';
55
- if (aggregate && operation === 'query') {
56
- tableName = `${table}_aggregate`;
57
- }
92
+ // --- Определение корневого типа и полей для поиска ---
93
+ let rootType = null; // Тип будет 'OBJECT' из __schema.types
94
+ let rootFields = []; // Массив полей из корневого типа
58
95
  if (operation === 'query') {
59
- schemaSection = 'first_level_queries';
96
+ rootType = queryRoot;
97
+ rootFields = queryRoot.fields || [];
60
98
  }
61
99
  else if (operation === 'subscription') {
62
- schemaSection = 'subscriptions';
100
+ if (!subscriptionRoot)
101
+ throw new Error('❌ Subscription operations not supported by the schema.');
102
+ rootType = subscriptionRoot;
103
+ rootFields = subscriptionRoot.fields || [];
63
104
  }
64
- else if (['insert', 'update', 'delete'].includes(operation)) {
65
- schemaSection = 'mutations';
66
- if (operation === 'insert') {
67
- tableName = `insert_${table}`;
68
- }
69
- else if (operation === 'update') {
70
- // Handle _by_pk update separately later
71
- // tableName = `update_${table}`; // Keep original table for now
72
- }
73
- else if (operation === 'delete') {
74
- // Handle _by_pk delete separately later
75
- // tableName = `delete_${table}`; // Keep original table for now
76
- }
77
- }
78
- if (!schema[schemaSection]) {
79
- throw new Error(`❌ Schema section ${schemaSection} not found. Schema might be outdated or incorrect.`);
105
+ else { // insert, update, delete
106
+ if (!mutationRoot)
107
+ throw new Error('❌ Mutation operations not supported by the schema.');
108
+ rootType = mutationRoot;
109
+ rootFields = mutationRoot.fields || [];
80
110
  }
81
- // Special handling for update/delete by pk
111
+ // --- ---
112
+ // --- Логика определения имени операции (queryName) ---
113
+ let targetFieldName = table; // Имя поля, которое ищем в корневом типе
82
114
  let isByPkOperation = false;
83
- if (['update', 'delete'].includes(operation) && opts.pk_columns) {
84
- isByPkOperation = true;
85
- tableName = `${operation}_${table}_by_pk`; // e.g., update_users_by_pk
115
+ let isAggregate = operation === 'query' && !!aggregate;
116
+ // Определяем префиксы и суффиксы для мутаций и by_pk запросов
117
+ const mutationPrefix = operation === 'insert' ? 'insert_' : operation === 'update' ? 'update_' : operation === 'delete' ? 'delete_' : '';
118
+ const pkSuffix = '_by_pk';
119
+ const aggregateSuffix = '_aggregate';
120
+ const oneSuffix = '_one';
121
+ // Формируем ожидаемые имена полей (улучшенная логика)
122
+ if (isAggregate) {
123
+ targetFieldName = `${table}${aggregateSuffix}`;
86
124
  }
87
- else if (operation === 'query' && opts.pk_columns) {
88
- isByPkOperation = true;
89
- tableName = `${table}_by_pk`; // e.g., users_by_pk
125
+ else if (opts.pk_columns) {
126
+ if (operation === 'query') {
127
+ targetFieldName = `${table}${pkSuffix}`;
128
+ isByPkOperation = true;
129
+ }
130
+ else if (['update', 'delete'].includes(operation)) {
131
+ targetFieldName = `${mutationPrefix}${table}${pkSuffix}`;
132
+ isByPkOperation = true;
133
+ }
134
+ // pk_columns не влияет на insert
90
135
  }
91
136
  else if (operation === 'insert' && opts.object && !opts.objects) {
92
- // Try to find insert_table_one mutation
93
- const oneMutationName = `insert_${table}_one`;
94
- if (schema.mutations && schema.mutations[oneMutationName]) {
95
- tableName = oneMutationName;
96
- isByPkOperation = true; // Treat insert_one like a by_pk operation for simplicity
137
+ const oneFieldName = `${mutationPrefix}${table}${oneSuffix}`;
138
+ if (rootFields.find(f => f.name === oneFieldName)) {
139
+ targetFieldName = oneFieldName;
140
+ // Не ставим isByPkOperation в true для insert_one, т.к. аргументы другие
97
141
  }
98
142
  else {
99
- tableName = `insert_${table}`; // Fallback to regular insert
143
+ targetFieldName = `${mutationPrefix}${table}`;
100
144
  }
101
145
  }
102
146
  else if (operation === 'insert') {
103
- tableName = `insert_${table}`;
147
+ targetFieldName = `${mutationPrefix}${table}`;
104
148
  }
105
149
  else if (operation === 'update') {
106
- tableName = `update_${table}`;
150
+ targetFieldName = `${mutationPrefix}${table}`; // Bulk update
107
151
  }
108
152
  else if (operation === 'delete') {
109
- tableName = `delete_${table}`;
110
- }
111
- // If not by_pk, and it's a query/sub, keep the original table name for non-aggregate
112
- else if (['query', 'subscription'].includes(operation) && !aggregate) {
113
- tableName = table;
114
- }
115
- // If it's an aggregate query
116
- else if (operation === 'query' && aggregate) {
117
- tableName = `${table}_aggregate`;
153
+ targetFieldName = `${mutationPrefix}${table}`; // Bulk delete
118
154
  }
119
- const possibleQueries = Object.keys(schema[schemaSection]);
120
- let queryName = possibleQueries.find(q => q === tableName);
121
- // Fallback logic if specific query (like insert_table_one) wasn't found directly
122
- if (!queryName) {
123
- // General fallback: find first matching query
124
- queryName = possibleQueries.find(q => q.includes(table)) || possibleQueries[0];
125
- if (!queryName) {
126
- throw new Error(`❌ Query/Mutation/Subscription for table "${table}" not found in schema section "${schemaSection}"`);
127
- }
128
- console.log(`[generator] ⚠️ Could not find exact query name "${tableName}", using fallback "${queryName}"`);
129
- }
130
- const queryInfo = schema[schemaSection][queryName];
155
+ // Для обычных query/subscription без pk_columns и aggregate, имя таблицы (targetFieldName) остается исходным 'table'
156
+ const queryInfo = rootFields.find(f => f.name === targetFieldName);
131
157
  if (!queryInfo) {
132
- throw new Error(`❌ Query info for "${queryName}" not found in schema section "${schemaSection}"`);
158
+ // Fallback для случая, когда _by_pk/aggregate/etc не найдены, но базовый запрос есть
159
+ const fallbackQueryInfo = rootFields.find(f => f.name === table);
160
+ if (fallbackQueryInfo && ['query', 'subscription'].includes(operation) && !isAggregate && !isByPkOperation) {
161
+ console.warn(`[generator] ⚠️ Exact field "${targetFieldName}" not found, using fallback "${table}" in ${rootType.name}`);
162
+ targetFieldName = table; // Используем базовое имя
163
+ // queryInfo = fallbackQueryInfo; // Переприсваиваем для дальнейшего использования
164
+ throw new Error(`❌ Field "${targetFieldName}" not found in root type "${rootType.name}"`); // Упадем здесь, если все равно не нашли
165
+ }
166
+ else {
167
+ throw new Error(`❌ Field "${targetFieldName}" not found in root type "${rootType.name}"`);
168
+ }
133
169
  }
170
+ const queryName = queryInfo.name; // Имя поля GraphQL, которое будем использовать
171
+ // --- ---
134
172
  const queryArgs = [];
135
173
  const variables = {};
136
174
  const varParts = [];
137
- // Helper function with improved required logic
138
- const getGqlType = (argName, argSchema, value, defaultType = 'String', forceRequired = false) => {
139
- let typeName = (argSchema === null || argSchema === void 0 ? void 0 : argSchema.type) || defaultType;
140
- let isList = (argSchema === null || argSchema === void 0 ? void 0 : argSchema.isList) || false; // Rely more on schema or explicit checks
141
- let isRequired = forceRequired || (argSchema === null || argSchema === void 0 ? void 0 : argSchema.isRequired) || false;
142
- // Check type name conventions first (e.g., from introspection)
143
- if (typeof typeName === 'string') {
144
- let baseTypeName = typeName;
145
- if (baseTypeName.endsWith('!')) {
146
- isRequired = true;
147
- baseTypeName = baseTypeName.slice(0, -1);
148
- }
149
- if (baseTypeName.startsWith('[') && baseTypeName.endsWith(']')) {
150
- isList = true;
151
- baseTypeName = baseTypeName.slice(1, -1);
152
- // Check for inner required type e.g., [String!]
153
- if (baseTypeName.endsWith('!')) {
154
- // Mark inner as required if needed? (Handled later for known types)
155
- baseTypeName = baseTypeName.slice(0, -1);
156
- }
157
- }
158
- typeName = baseTypeName;
159
- }
160
- // Determine list/required based on arg name conventions
161
- const baseTable = table; // Use original table name for type conventions
162
- let finalType = typeName;
163
- let finalIsRequired = isRequired;
164
- let finalIsList = isList;
165
- let innerRequired = false;
166
- // Apply conventions/overrides
167
- if (argName === 'objects') {
168
- finalType = `${baseTable}_insert_input`;
169
- finalIsList = true;
170
- finalIsRequired = true;
171
- innerRequired = true;
172
- }
173
- if (argName === 'object') {
174
- finalType = `${baseTable}_insert_input`;
175
- finalIsList = false;
176
- finalIsRequired = true;
177
- }
178
- if (argName === 'order_by') {
179
- finalType = `${baseTable}_order_by`;
180
- finalIsList = true;
181
- finalIsRequired = false;
182
- innerRequired = true;
183
- } // List itself not required, but inner usually is
184
- if (argName === 'pk_columns') {
185
- finalType = `${baseTable}_pk_columns_input`;
186
- finalIsList = false;
187
- finalIsRequired = true;
175
+ // --- РЕФАКТОРИНГ getGqlType ---
176
+ const getGqlTypeFromSchema = (argType) => {
177
+ const info = getTypeInfo(argType);
178
+ if (!info.name) {
179
+ throw new Error(`Cannot determine base type name for argType: ${JSON.stringify(argType)}`);
188
180
  }
189
- if (argName === '_set') {
190
- finalType = `${baseTable}_set_input`;
191
- finalIsList = false;
192
- finalIsRequired = true;
181
+ let typeStr = info.name; // Now we know info.name is a string
182
+ if (info.isList) {
183
+ typeStr = `[${info.name}${info.isNonNullItem ? '!' : ''}]`;
193
184
  }
194
- if (argName === 'where') {
195
- finalType = `${baseTable}_bool_exp`;
196
- finalIsList = false;
197
- finalIsRequired = false;
185
+ if (info.isNonNull) {
186
+ typeStr += '!';
198
187
  }
199
- // If it's a direct PK arg (like `id` in `users_by_pk(id: uuid!)`)
200
- if (forceRequired) {
201
- finalIsRequired = true;
202
- finalIsList = false; // Direct PK args are typically not lists
203
- }
204
- // Construct the final type string
205
- let typeString = finalType;
206
- if (finalIsList) {
207
- typeString = `[${finalType}${innerRequired ? '!' : ''}]`;
208
- }
209
- if (finalIsRequired) {
210
- typeString += '!';
211
- }
212
- return typeString;
188
+ return typeStr;
213
189
  };
214
- // Argument processing function (now relies on getGqlType for correctness)
215
- const processArg = (argName, value, argSchema, isDirectPk = false) => {
216
- if (value === undefined || value === null)
190
+ // --- ---
191
+ // --- РЕФАКТОРИНГ Цикла обработки аргументов (Top Level) ---
192
+ const processedArgs = new Set();
193
+ const addArgument = (argName, value, argDefinition) => {
194
+ if (processedArgs.has(argName))
217
195
  return;
218
196
  const varName = `v${varCounter++}`;
219
197
  queryArgs.push(`${argName}: $${varName}`);
220
198
  variables[varName] = value;
221
- const gqlType = getGqlType(argName, argSchema, value, 'String', isDirectPk);
222
- varParts.push(`$${varName}: ${gqlType}`);
199
+ const gqlType = getGqlTypeFromSchema(argDefinition.type);
200
+ // Check if var already exists before pushing
201
+ if (!varParts.some(p => p.startsWith(`$${varName}:`))) {
202
+ varParts.push(`$${varName}: ${gqlType}`);
203
+ }
204
+ processedArgs.add(argName);
223
205
  };
224
- // --- Argument Processing with deterministic order ---
225
- const argProcessingOrder = [
226
- // Common args first
227
- 'where',
228
- // PK args (direct or pk_columns object)
229
- ...(isByPkOperation && opts.pk_columns ? Object.keys(opts.pk_columns) : []), // Direct PK args like 'id'
230
- 'pk_columns', // The pk_columns input object itself
231
- // Mutation specific
232
- '_set',
233
- 'objects',
234
- 'object',
235
- // Pagination/Sorting
236
- 'limit',
237
- 'offset',
238
- 'order_by'
239
- ];
240
- const processedArgs = new Set();
241
- // Process args in defined order
242
- for (const argName of argProcessingOrder) {
243
- if (!queryInfo.args || !queryInfo.args[argName])
244
- continue; // Skip if arg not in schema
245
- if (processedArgs.has(argName))
246
- continue;
247
- const argSchema = queryInfo.args[argName];
206
+ // 1. Обработка прямых аргументов поля (из queryInfo.args)
207
+ (_a = queryInfo.args) === null || _a === void 0 ? void 0 : _a.forEach((argDef) => {
208
+ const argName = argDef.name;
248
209
  let value = undefined;
249
- let isDirectPk = false;
250
- // Map opts to schema args
251
210
  if (argName === 'pk_columns' && opts.pk_columns) {
252
- // Process this ONLY if the operation expects pk_columns (update_by_pk)
253
- if (operation === 'update') {
254
- value = opts.pk_columns;
255
- }
256
- else {
257
- // For query_by_pk and delete_by_pk, pk_columns is used to find direct args
258
- continue; // Skip processing pk_columns itself here
259
- }
211
+ value = opts.pk_columns;
260
212
  }
261
213
  else if (argName === '_set' && opts._set) {
262
214
  value = opts._set;
@@ -265,269 +217,319 @@ function Generator(schema) {
265
217
  value = opts.objects || [opts.object];
266
218
  }
267
219
  else if (argName === 'object' && opts.object && !opts.objects) {
268
- value = opts.object;
269
- }
270
- else if (isByPkOperation && opts.pk_columns && opts.pk_columns[argName] !== undefined) {
271
- // Handle direct PK arg like `id` for `_by_pk` operations
272
- // Check if the schema actually expects this direct arg
273
- if (queryInfo.args[argName]) {
274
- value = opts.pk_columns[argName];
275
- isDirectPk = true;
220
+ if (queryName.endsWith('_one')) {
221
+ value = opts.object;
276
222
  }
277
223
  else {
278
- // This direct PK arg is not in schema, maybe it expects pk_columns object?
279
- // Let's skip processing it directly if pk_columns object is also in the processing order
280
- if (argProcessingOrder.includes('pk_columns'))
281
- continue;
282
- else
283
- value = opts.pk_columns[argName]; // Process if pk_columns object isn't expected
224
+ value = [opts.object];
225
+ if (!queryInfo.args.find((a) => a.name === 'objects')) {
226
+ debug(`Passing single 'object' to bulk operation "%s" which might expect 'objects' array.`, queryName);
227
+ const objectsArgDef = queryInfo.args.find((a) => a.name === 'objects');
228
+ if (objectsArgDef) {
229
+ addArgument('objects', value, objectsArgDef);
230
+ }
231
+ else {
232
+ console.error(`[generator] Cannot determine correct argument ('object' or 'objects') for "${queryName}".`);
233
+ }
234
+ return;
235
+ }
236
+ else if (queryInfo.args.find((a) => a.name === 'object')) {
237
+ return;
238
+ }
239
+ else {
240
+ const objectsArgDef = queryInfo.args.find((a) => a.name === 'objects');
241
+ if (objectsArgDef) {
242
+ addArgument('objects', value, objectsArgDef);
243
+ }
244
+ return;
245
+ }
284
246
  }
285
247
  }
248
+ else if (isByPkOperation && opts.pk_columns && opts.pk_columns[argName] !== undefined) {
249
+ value = opts.pk_columns[argName];
250
+ }
286
251
  else if (opts[argName] !== undefined) {
287
252
  value = opts[argName];
288
253
  }
289
254
  if (value !== undefined) {
290
- processArg(argName, value, argSchema, isDirectPk);
291
- processedArgs.add(argName);
292
- }
293
- }
294
- // Process any remaining args not in the defined order (should be rare)
295
- if (queryInfo.args) {
296
- for (const argName in queryInfo.args) {
297
- if (!processedArgs.has(argName)) {
298
- const value = opts[argName];
299
- if (value !== undefined) {
300
- processArg(argName, value, queryInfo.args[argName], false);
301
- processedArgs.add(argName);
302
- }
303
- }
255
+ addArgument(argName, value, argDef);
304
256
  }
305
- }
306
- // --- End Argument Processing ---
257
+ });
258
+ // --- End Top Level Argument Processing ---
259
+ // --- Returning Field Processing (REWORKED) ---
307
260
  const returningFields = [];
308
- function processReturningField(field, currentVarCounterRef) {
309
- if (typeof field === 'string') {
261
+ const varCounterRef = { count: varCounter }; // Use ref for nested calls
262
+ // Helper to find type details from schema by name
263
+ const findTypeDetails = (typeName) => {
264
+ if (!typeName)
265
+ return null;
266
+ return schema.types.find((t) => t.name === typeName && (t.kind === 'OBJECT' || t.kind === 'INTERFACE'));
267
+ };
268
+ // Recursive function to process fields
269
+ function processReturningField(field, parentTypeName, currentVarCounterRef) {
270
+ var _a, _b;
271
+ if (typeof field === 'string')
310
272
  return field.trim();
311
- }
312
273
  if (typeof field === 'object' && field !== null) {
313
274
  const fieldName = Object.keys(field)[0];
314
275
  const subFieldsOrParams = field[fieldName];
315
- if (typeof subFieldsOrParams === 'boolean' && subFieldsOrParams) {
316
- return fieldName;
276
+ // Find the field definition in the parent type
277
+ const parentTypeDetails = findTypeDetails(parentTypeName);
278
+ const fieldInfo = (_a = parentTypeDetails === null || parentTypeDetails === void 0 ? void 0 : parentTypeDetails.fields) === null || _a === void 0 ? void 0 : _a.find((f) => f.name === fieldName);
279
+ if (!fieldInfo) {
280
+ // Use debug instead of console.warn
281
+ debug(`Field "%s" not found in type "%s". Skipping.`, fieldName, parentTypeName);
282
+ return ''; // Skip if field not found in parent
317
283
  }
318
- if (typeof subFieldsOrParams === 'boolean' && !subFieldsOrParams) {
319
- return ''; // Explicitly skip false fields
320
- }
321
- if (Array.isArray(subFieldsOrParams)) {
322
- const subFieldsStr = subFieldsOrParams
323
- .map(sf => processReturningField(sf, currentVarCounterRef))
324
- .filter(Boolean) // Remove empty strings from skipped fields
284
+ // Determine the return type of this field
285
+ let fieldReturnTypeName = null;
286
+ let currentFieldType = fieldInfo.type;
287
+ while (currentFieldType.ofType)
288
+ currentFieldType = currentFieldType.ofType; // Unwrap LIST/NON_NULL
289
+ fieldReturnTypeName = currentFieldType.name;
290
+ if (typeof subFieldsOrParams === 'boolean' && subFieldsOrParams)
291
+ return fieldName;
292
+ if (typeof subFieldsOrParams === 'boolean' && !subFieldsOrParams)
293
+ return ''; // Skip false
294
+ if (Array.isArray(subFieldsOrParams) || typeof subFieldsOrParams === 'string') {
295
+ // Simple nested fields (array of strings/objects or single string)
296
+ const nestedFields = Array.isArray(subFieldsOrParams) ? subFieldsOrParams : subFieldsOrParams.split(/\s+/).filter(Boolean);
297
+ const nestedProcessed = nestedFields
298
+ .map(sf => processReturningField(sf, fieldReturnTypeName, currentVarCounterRef)) // <<< Pass return type as new parent
299
+ .filter(Boolean)
325
300
  .join('\n ');
326
- return subFieldsStr ? `${fieldName} {\n ${subFieldsStr}\n }` : '';
327
- }
328
- if (typeof subFieldsOrParams === 'string') {
329
- return `${fieldName} {\n ${subFieldsOrParams}\n }`;
301
+ return nestedProcessed ? `${fieldName} {\n ${nestedProcessed}\n }` : fieldName; // Return just fieldName if no nested fields processed
330
302
  }
331
- // Handling nested query object with potential parameters
332
- if (typeof subFieldsOrParams === 'object' && subFieldsOrParams !== null) {
333
- const { where: nestedWhere, limit: nestedLimit, offset: nestedOffset, order_by: nestedOrderBy, alias, returning: nestedReturningDef } = subFieldsOrParams, otherParams = __rest(subFieldsOrParams, ["where", "limit", "offset", "order_by", "alias", "returning"]);
334
- const relationName = fieldName;
335
- const fieldAliasOrName = alias || relationName;
336
- const relationArgTypeNameBase = alias ? alias : relationName;
303
+ if (typeof subFieldsOrParams === 'object') { // Nested query with potential args
304
+ const { returning: nestedReturning, alias } = subFieldsOrParams, nestedArgsInput = __rest(subFieldsOrParams, ["returning", "alias"]);
305
+ const fieldAliasOrName = alias || fieldName;
306
+ // Field definition might include alias
307
+ const fieldDefinition = alias ? `${alias}: ${fieldName}` : fieldName;
308
+ // --- Process Nested Arguments --- (IMPLEMENTED)
337
309
  const nestedArgs = [];
338
- // Nested argument processing
339
- const processNestedArg = (argName, value, typeSuffix, defaultType = 'String', forceList = false) => {
340
- if (value === undefined || value === null)
341
- return;
342
- const varName = `v${currentVarCounterRef.count++}`;
343
- nestedArgs.push(`${argName}: $${varName}`);
344
- variables[varName] = value;
345
- // Construct type, e.g., users_bool_exp, posts_order_by
346
- let gqlType = typeSuffix ? `${relationArgTypeNameBase}${typeSuffix}` : defaultType;
347
- let isRequired = false; // Assume nested args aren't required unless schema says so
348
- let isList = forceList || (argName === 'order_by'); // Force list for order_by
349
- // Basic check if type name implies list/required (might need schema lookup)
350
- if (gqlType.startsWith('['))
351
- isList = true;
352
- if (gqlType.endsWith('!')) {
353
- isRequired = true;
354
- gqlType = gqlType.slice(0, -1);
310
+ if (fieldInfo.args && Object.keys(nestedArgsInput).length > 0) {
311
+ Object.entries(nestedArgsInput).forEach(([argName, argValue]) => {
312
+ const argDef = fieldInfo.args.find((a) => a.name === argName);
313
+ if (argDef && argValue !== undefined) {
314
+ const varName = `v${currentVarCounterRef.count++}`;
315
+ nestedArgs.push(`${argName}: $${varName}`);
316
+ variables[varName] = argValue;
317
+ const gqlType = getGqlTypeFromSchema(argDef.type); // Use existing helper
318
+ // Check if var already exists before pushing
319
+ if (!varParts.some(p => p.startsWith(`$${varName}:`))) {
320
+ varParts.push(`$${varName}: ${gqlType}`);
321
+ }
322
+ }
323
+ else {
324
+ // Use debug instead of console.warn
325
+ debug(`Argument "%s" not found or value is undefined for field "%s"`, argName, fieldName);
326
+ }
327
+ });
328
+ }
329
+ const nestedArgsStr = nestedArgs.length > 0 ? `(${nestedArgs.join(', ')})` : '';
330
+ // --- End Nested Arguments ---
331
+ // --- Process Nested Returning Fields --- (Improved Default Logic)
332
+ let finalNestedReturning = [];
333
+ if (nestedReturning) {
334
+ if (Array.isArray(nestedReturning)) {
335
+ finalNestedReturning = nestedReturning;
355
336
  }
356
- // Strip list markers if present, handle wrapping below
357
- if (gqlType.startsWith('[') && gqlType.endsWith(']')) {
358
- gqlType = gqlType.slice(1, -1);
337
+ else if (typeof nestedReturning === 'string') {
338
+ finalNestedReturning = nestedReturning.split(/\s+/).filter(Boolean);
359
339
  }
360
- let finalType = gqlType;
361
- if (isList) {
362
- // Assume inner type is required for order_by list
363
- const innerRequired = argName === 'order_by' ? '!' : '';
364
- finalType = `[${gqlType}${innerRequired}]`;
365
- // Is the list itself required? (Usually not for nested args)
366
- isRequired = false;
340
+ else if (typeof nestedReturning === 'object') {
341
+ finalNestedReturning = Object.entries(nestedReturning)
342
+ .filter(([_, v]) => v)
343
+ .map(([k, v]) => (typeof v === 'boolean' ? k : { [k]: v }));
367
344
  }
368
- varParts.push(`$${varName}: ${finalType}${isRequired ? '!' : ''}`);
369
- };
370
- processNestedArg('where', nestedWhere, '_bool_exp');
371
- processNestedArg('limit', nestedLimit, '', 'Int');
372
- processNestedArg('offset', nestedOffset, '', 'Int');
373
- // Pass forceList=true for order_by
374
- processNestedArg('order_by', nestedOrderBy, '_order_by', 'String', true);
375
- // Process other arbitrary parameters
376
- for (const paramName in otherParams) {
377
- processNestedArg(paramName, otherParams[paramName], ''); // Assume String default
378
345
  }
379
- const nestedArgsStr = nestedArgs.length > 0 ? `(${nestedArgs.join(', ')})` : '';
380
- let finalNestedReturning = ['id']; // Default returning 'id'
381
- if (nestedReturningDef) {
382
- if (Array.isArray(nestedReturningDef)) {
383
- finalNestedReturning = nestedReturningDef;
346
+ // If no nested returning specified, try to add default 'id' or '__typename'
347
+ if (finalNestedReturning.length === 0) {
348
+ const nestedTypeDetails = findTypeDetails(fieldReturnTypeName);
349
+ if ((_b = nestedTypeDetails === null || nestedTypeDetails === void 0 ? void 0 : nestedTypeDetails.fields) === null || _b === void 0 ? void 0 : _b.find((f) => f.name === 'id')) {
350
+ finalNestedReturning.push('id');
384
351
  }
385
- else if (typeof nestedReturningDef === 'string') {
386
- finalNestedReturning = nestedReturningDef.split(/\s+/).filter(Boolean);
387
- }
388
- else if (typeof nestedReturningDef === 'object') {
389
- // Convert object { field: true, another: {...} } to array ['field', { another: {...} }]
390
- finalNestedReturning = Object.entries(nestedReturningDef)
391
- .filter(([_, value]) => value) // Filter out false values
392
- .map(([key, value]) => (typeof value === 'boolean' ? key : { [key]: value }));
352
+ else if ((nestedTypeDetails === null || nestedTypeDetails === void 0 ? void 0 : nestedTypeDetails.kind) === 'OBJECT' || (nestedTypeDetails === null || nestedTypeDetails === void 0 ? void 0 : nestedTypeDetails.kind) === 'INTERFACE') {
353
+ finalNestedReturning.push('__typename');
393
354
  }
355
+ // If it's a SCALAR/ENUM, no body needed
394
356
  }
395
- const nestedFieldsStr = finalNestedReturning
396
- .map(f => processReturningField(f, currentVarCounterRef))
397
- .filter(Boolean) // Remove empty strings from skipped fields
357
+ const nestedReturningStr = finalNestedReturning
358
+ .map(f => processReturningField(f, fieldReturnTypeName, currentVarCounterRef)) // <<< Pass return type
359
+ .filter(Boolean)
398
360
  .join('\n ');
399
- // Use alias in the field definition if present
400
- const fieldDefinition = alias ? `${relationName}: ${fieldAliasOrName}` : fieldAliasOrName;
401
- return nestedFieldsStr ? `${fieldDefinition}${nestedArgsStr} {\n ${nestedFieldsStr}\n }` : '';
361
+ // --- End Nested Returning Fields ---
362
+ // Only add body if there are fields to return AND the type is OBJECT/INTERFACE
363
+ const nestedFieldTypeDetails = findTypeDetails(fieldReturnTypeName);
364
+ const needsNestedBody = ((nestedFieldTypeDetails === null || nestedFieldTypeDetails === void 0 ? void 0 : nestedFieldTypeDetails.kind) === 'OBJECT' || (nestedFieldTypeDetails === null || nestedFieldTypeDetails === void 0 ? void 0 : nestedFieldTypeDetails.kind) === 'INTERFACE') && nestedReturningStr;
365
+ const nestedBody = needsNestedBody ? ` {\n ${nestedReturningStr}\n }` : '';
366
+ return `${fieldDefinition}${nestedArgsStr}${nestedBody}`;
402
367
  }
403
- return ''; // Skip invalid field types
404
368
  }
405
- return ''; // Skip invalid field types
369
+ return ''; // Should not happen for valid input
406
370
  }
407
- const varCounterRef = { count: varCounter }; // Use ref for mutable counter in recursion
408
- // ---- START MODIFICATION ----
409
- let defaultFieldsGenerated = false;
410
- // Function to generate default fields based on operation type and schema
411
- const generateDefaultFields = () => {
412
- var _a;
413
- if (defaultFieldsGenerated)
414
- return; // Generate only once
371
+ // --- Main Returning Logic (REWORKED) ---
372
+ let topLevelReturnTypeName = null;
373
+ let currentQueryType = queryInfo.type;
374
+ while (currentQueryType.ofType)
375
+ currentQueryType = currentQueryType.ofType;
376
+ topLevelReturnTypeName = currentQueryType.name;
377
+ let finalReturningFields = []; // Initialize final list
378
+ // Helper to generate base default fields
379
+ const baseGenerateDefaultFields = (parentTypeName) => {
380
+ var _a, _b, _c, _d, _e, _f, _g;
381
+ const defaults = [];
415
382
  if (aggregate) {
416
- // Aggregate logic already adds `aggregate { ... }` and potentially `nodes { ... }`
417
- // No separate default fields needed here unless `returning` for nodes is omitted
418
- // The existing aggregate logic handles the structure.
419
- }
420
- else if (queryInfo.returning_fields && !isByPkOperation && operation !== 'delete') {
421
- // Default fields for query, subscription, insert (non-PK)
422
- returningFields.push('id');
423
- ['name', 'email', 'created_at', 'updated_at'].forEach(f => {
424
- if (queryInfo.returning_fields[f])
425
- returningFields.push(f);
426
- });
427
- }
428
- else if (isByPkOperation && operation === 'delete') {
429
- // Default for delete_by_pk: Try to return PK fields, fallback to id
430
- if (opts.pk_columns && queryInfo.returning_fields) {
431
- Object.keys(opts.pk_columns).forEach(pkField => {
432
- if (queryInfo.returning_fields[pkField]) {
433
- returningFields.push(pkField);
434
- }
435
- });
383
+ // Simplified default for aggregate
384
+ const aggregateFieldInfo = rootFields.find(f => f.name === queryName);
385
+ let aggReturnTypeName = null;
386
+ if (aggregateFieldInfo) {
387
+ let currentAggType = aggregateFieldInfo.type;
388
+ while (currentAggType.ofType)
389
+ currentAggType = currentAggType.ofType;
390
+ aggReturnTypeName = currentAggType.name;
436
391
  }
437
- // If no PK fields were added, push id as fallback
438
- if (returningFields.length === 0 && ((_a = queryInfo.returning_fields) === null || _a === void 0 ? void 0 : _a.id)) {
439
- returningFields.push('id');
392
+ const aggTypeDetails = findTypeDetails(aggReturnTypeName);
393
+ if ((_a = aggTypeDetails === null || aggTypeDetails === void 0 ? void 0 : aggTypeDetails.fields) === null || _a === void 0 ? void 0 : _a.find((f) => f.name === 'aggregate')) {
394
+ const aggregateNestedField = aggTypeDetails.fields.find((f) => f.name === 'aggregate');
395
+ let aggregateNestedTypeName = null;
396
+ if (aggregateNestedField) {
397
+ let currentNestedType = aggregateNestedField.type;
398
+ while (currentNestedType.ofType)
399
+ currentNestedType = currentNestedType.ofType;
400
+ aggregateNestedTypeName = currentNestedType.name;
401
+ }
402
+ const aggregateNestedTypeDetails = findTypeDetails(aggregateNestedTypeName);
403
+ if ((_b = aggregateNestedTypeDetails === null || aggregateNestedTypeDetails === void 0 ? void 0 : aggregateNestedTypeDetails.fields) === null || _b === void 0 ? void 0 : _b.find((f) => f.name === 'count')) {
404
+ defaults.push('aggregate { count }');
405
+ }
406
+ else {
407
+ defaults.push('aggregate { __typename }');
408
+ }
409
+ }
410
+ else {
411
+ defaults.push('__typename');
440
412
  }
441
413
  }
442
- else { // Default for other mutations _by_pk (update) and query_by_pk
443
- if (queryInfo.returning_fields) {
444
- returningFields.push('id'); // Default fallback
414
+ else {
415
+ const returnTypeDetails = findTypeDetails(parentTypeName);
416
+ if ((_c = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _c === void 0 ? void 0 : _c.find((f) => f.name === 'id')) {
417
+ defaults.push('id');
418
+ }
419
+ // Add other simple default fields like name, email if they exist?
420
+ // Example: Check if 'name' exists and add it
421
+ if ((_d = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _d === void 0 ? void 0 : _d.find((f) => f.name === 'name')) {
422
+ defaults.push('name');
423
+ }
424
+ if ((_e = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _e === void 0 ? void 0 : _e.find((f) => f.name === 'email')) {
425
+ defaults.push('email');
426
+ }
427
+ if ((_f = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _f === void 0 ? void 0 : _f.find((f) => f.name === 'created_at')) {
428
+ defaults.push('created_at');
429
+ }
430
+ if ((_g = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _g === void 0 ? void 0 : _g.find((f) => f.name === 'updated_at')) {
431
+ defaults.push('updated_at');
432
+ }
433
+ // Fallback __typename if still empty and it's an object/interface
434
+ if (defaults.length === 0 && ((returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.kind) === 'OBJECT' || (returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.kind) === 'INTERFACE')) {
435
+ defaults.push('__typename');
445
436
  }
446
437
  }
447
- defaultFieldsGenerated = true;
438
+ return defaults;
448
439
  };
449
- // Process returning option
450
440
  if (returning) {
451
- if (Array.isArray(returning)) { // Explicit array provided - overrides defaults
452
- returning
453
- .map(field => processReturningField(field, varCounterRef))
454
- .filter(Boolean)
455
- .forEach(processedField => returningFields.push(processedField));
456
- defaultFieldsGenerated = true; // Mark defaults as handled/overridden
457
- }
458
- else if (typeof returning === 'string') { // Explicit string provided - overrides defaults
459
- returning.split(/\s+/).filter(Boolean)
460
- .map(field => processReturningField(field, varCounterRef)) // Process simple strings
461
- .filter(Boolean)
462
- .forEach(processedField => returningFields.push(processedField));
463
- defaultFieldsGenerated = true; // Mark defaults as handled/overridden
464
- }
465
- else if (typeof returning === 'object' && returning !== null) { // NEW: Object provided - ADD to defaults
466
- // 1. Generate default fields first
467
- generateDefaultFields();
468
- // 2. Process the object as additional relations/fields
441
+ if (Array.isArray(returning)) {
442
+ finalReturningFields = returning
443
+ .map(field => processReturningField(field, topLevelReturnTypeName, varCounterRef))
444
+ .filter(Boolean);
445
+ // Defaults are fully overridden by array
446
+ }
447
+ else if (typeof returning === 'string') {
448
+ finalReturningFields = returning.split(/\s+/).filter(Boolean)
449
+ .map(field => processReturningField(field, topLevelReturnTypeName, varCounterRef))
450
+ .filter(Boolean);
451
+ // Defaults are fully overridden by string
452
+ }
453
+ else if (typeof returning === 'object' && returning !== null) {
454
+ // 1. Get default fields
455
+ let currentDefaults = baseGenerateDefaultFields(topLevelReturnTypeName);
456
+ const customFields = [];
457
+ // 2. Process fields from the returning object
469
458
  Object.entries(returning).forEach(([key, value]) => {
470
- // Construct the object format processReturningField expects: { relationName: subOptions }
471
459
  const fieldObject = { [key]: value };
472
- const processedField = processReturningField(fieldObject, varCounterRef);
460
+ const processedField = processReturningField(fieldObject, topLevelReturnTypeName, varCounterRef);
473
461
  if (processedField) {
474
- // Avoid adding duplicates if default already added it (less likely for relations)
475
- if (!returningFields.includes(processedField)) {
476
- returningFields.push(processedField);
477
- }
462
+ // Determine base name (handle alias: "alias: realName" or just "realName")
463
+ const baseNameMatch = processedField.match(/^([\w\d_]+)(?:\s*:\s*[\w\d_]+)?/);
464
+ // If alias exists use the original field name (key), otherwise use the matched name
465
+ const baseName = ((value === null || value === void 0 ? void 0 : value.alias) && typeof value === 'object') ? key : (baseNameMatch ? baseNameMatch[1] : key);
466
+ // Remove default if it exists with the same base name
467
+ currentDefaults = currentDefaults.filter(defaultField => {
468
+ const defaultBaseNameMatch = defaultField.match(/^([\w\d_]+)/);
469
+ return !(defaultBaseNameMatch && defaultBaseNameMatch[1] === baseName);
470
+ });
471
+ customFields.push(processedField); // Add the processed field
478
472
  }
479
473
  });
474
+ // 3. Combine remaining defaults and custom fields
475
+ finalReturningFields = [...currentDefaults, ...customFields];
480
476
  }
481
477
  else {
482
- // Invalid returning type? Fallback to defaults.
483
- generateDefaultFields();
478
+ // Invalid type or null, use defaults
479
+ finalReturningFields = baseGenerateDefaultFields(topLevelReturnTypeName);
484
480
  }
485
481
  }
486
- else { // returning is null or undefined - Use defaults
487
- generateDefaultFields();
482
+ else {
483
+ // No returning provided, use defaults
484
+ finalReturningFields = baseGenerateDefaultFields(topLevelReturnTypeName);
488
485
  }
489
- // ---- END MODIFICATION ----
490
486
  varCounter = varCounterRef.count; // Update main counter
491
- // Adjust structure for bulk mutations (insert, update, delete) vs single (_one, _by_pk)
492
- let finalReturningFields = [...returningFields];
493
- if (['insert', 'update', 'delete'].includes(operation) && !isByPkOperation) {
494
- // Bulk operations usually return affected_rows and a nested 'returning' array
495
- const fieldsForReturning = finalReturningFields.filter(f => !f.startsWith('affected_rows')); // Keep existing fields
496
- finalReturningFields = ['affected_rows']; // Start with affected_rows
497
- if (fieldsForReturning.length > 0) {
498
- const returningFieldsStr = fieldsForReturning.join('\n ');
499
- finalReturningFields.push(`returning {\n ${returningFieldsStr}\n }`);
487
+ // --- End Returning ---
488
+ // --- Final Query String Assembly --- (Adjusted section)
489
+ // const returningStr = finalReturningFields.length > 0 ? finalReturningFields.join('\n ') : ''; // Use the calculated finalReturningFields
490
+ // Логика для affected_rows
491
+ let assembledReturningFields = [...finalReturningFields]; // Start with the combined list
492
+ if (['insert', 'update', 'delete'].includes(operation) && !queryName.endsWith('_by_pk') && !queryName.endsWith('_one')) {
493
+ let directFieldReturnType = queryInfo.type;
494
+ while (directFieldReturnType.ofType)
495
+ directFieldReturnType = directFieldReturnType.ofType;
496
+ const returnTypeDetails = findTypeDetails(directFieldReturnType.name);
497
+ if (((_b = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _b === void 0 ? void 0 : _b.find((f) => f.name === 'affected_rows')) && ((_c = returnTypeDetails === null || returnTypeDetails === void 0 ? void 0 : returnTypeDetails.fields) === null || _c === void 0 ? void 0 : _c.find((f) => f.name === 'returning'))) {
498
+ const fieldsForNestedReturning = assembledReturningFields.filter(f => !f.trim().startsWith('affected_rows'));
499
+ assembledReturningFields = ['affected_rows']; // Reset and start with affected_rows
500
+ if (fieldsForNestedReturning.length > 0) {
501
+ const returningFieldsStr = fieldsForNestedReturning.join('\n ');
502
+ assembledReturningFields.push(`returning {\n ${returningFieldsStr}\n }`);
503
+ }
504
+ }
505
+ else {
506
+ debug(`Mutation "%s" does not seem to return standard affected_rows/returning fields.`, queryName);
500
507
  }
501
508
  }
502
- // Ensure single operations (_one, _by_pk) don't have affected_rows unless explicitly asked?
503
- // The current logic seems to handle this by default returning fields based on queryInfo
504
- // Determine the GraphQL operation type based on the input operation
505
509
  let gqlOperationType;
506
- if (operation === 'query') {
510
+ if (operation === 'query')
507
511
  gqlOperationType = 'query';
508
- }
509
- else if (operation === 'subscription') {
512
+ else if (operation === 'subscription')
510
513
  gqlOperationType = 'subscription';
511
- }
512
- else { // insert, update, delete
514
+ else
513
515
  gqlOperationType = 'mutation';
514
- }
515
- // Construct operation name (e.g., QueryUsers, MutationInsertUsersOne)
516
516
  const opNamePrefix = gqlOperationType.charAt(0).toUpperCase() + gqlOperationType.slice(1);
517
- // Ensure tablePascal handles potential empty parts from split
518
- const tablePascal = queryName.split('_').map(part => part ? part.charAt(0).toUpperCase() + part.slice(1) : '').join('');
519
- const operationName = `${opNamePrefix}${tablePascal}`;
517
+ const queryNamePascal = queryName.split('_').map((part) => part ? part.charAt(0).toUpperCase() + part.slice(1) : '').join('');
518
+ const operationName = `${opNamePrefix}${queryNamePascal}`;
520
519
  const argsStr = queryArgs.length > 0 ? `(${queryArgs.join(', ')})` : '';
521
- const returningStr = finalReturningFields.length > 0 ? finalReturningFields.join('\n ') : (queryInfo.returning_fields ? 'id' : ''); // Fallback to id if possible
520
+ const needsBody = currentQueryType.kind === 'OBJECT' || currentQueryType.kind === 'INTERFACE';
521
+ const returningStr = assembledReturningFields.length > 0 ? assembledReturningFields.join('\n ') : '';
522
+ const bodyStr = needsBody && returningStr ? ` {\n ${returningStr}\n }` : '';
522
523
  const fragmentsStr = fragments.length > 0 ? `\n${fragments.join('\n')}` : '';
523
524
  const queryStr = `
524
525
  ${gqlOperationType} ${operationName}${varParts.length > 0 ? `(${varParts.join(', ')})` : ''} {
525
- ${queryName}${argsStr}${returningStr ? ` {
526
- ${returningStr}
527
- }` : ''}
526
+ ${queryName}${argsStr}${bodyStr}
528
527
  }${fragmentsStr}
529
528
  `;
529
+ // --- End Assembly ---
530
530
  try {
531
+ // debug("Generated Query:", queryStr);
532
+ // debug("Generated Variables:", variables);
531
533
  const gqlQuery = (0, core_1.gql)(queryStr);
532
534
  return {
533
535
  queryString: queryStr,
@@ -544,5 +546,5 @@ function Generator(schema) {
544
546
  }
545
547
  };
546
548
  }
547
- // Export the factory function
548
- exports.default = Generator;
549
+ // Экспортируем Generator с уже загруженной схемой
550
+ exports.default = Generator(schema);