@constructive-io/graphql-codegen 4.6.0 → 4.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -230,6 +230,10 @@ function extractEntityFields(entityType, typeMap, entityToConnection, commentsEn
230
230
  const fields = [];
231
231
  if (!entityType.fields)
232
232
  return fields;
233
+ // Build a lookup of CreateXxxInput fields to infer hasDefault.
234
+ // If a field is NOT NULL on the entity but NOT required in CreateXxxInput,
235
+ // then it likely has a server-side default (serial, uuid_generate_v4, now(), etc.).
236
+ const createInputRequiredFields = buildCreateInputRequiredFieldSet(entityType.name, typeMap);
233
237
  for (const field of entityType.fields) {
234
238
  const baseTypeName = (0, introspection_1.getBaseTypeName)(field.type);
235
239
  if (!baseTypeName)
@@ -243,16 +247,55 @@ function extractEntityFields(entityType, typeMap, entityToConnection, commentsEn
243
247
  continue; // Skip relation fields
244
248
  }
245
249
  }
250
+ // Infer isNotNull from the NON_NULL wrapper on the entity type field
251
+ const fieldIsNotNull = (0, introspection_1.isNonNull)(field.type);
252
+ // Infer hasDefault: if a field is NOT NULL on the entity but NOT required
253
+ // in CreateXxxInput, it likely has a default value.
254
+ // Also: if it's absent from CreateInput entirely, it's likely computed/generated.
255
+ let fieldHasDefault = null;
256
+ if (createInputRequiredFields !== null) {
257
+ if (fieldIsNotNull && !createInputRequiredFields.has(field.name)) {
258
+ fieldHasDefault = true;
259
+ }
260
+ else {
261
+ fieldHasDefault = false;
262
+ }
263
+ }
246
264
  // Include scalar, enum, and other non-relation fields
247
265
  const fieldDescription = commentsEnabled ? (0, utils_1.stripSmartComments)(field.description) : undefined;
248
266
  fields.push({
249
267
  name: field.name,
250
268
  ...(fieldDescription ? { description: fieldDescription } : {}),
251
269
  type: convertToCleanFieldType(field.type),
270
+ isNotNull: fieldIsNotNull,
271
+ hasDefault: fieldHasDefault,
252
272
  });
253
273
  }
254
274
  return fields;
255
275
  }
276
+ /**
277
+ * Build a set of field names that are required (NON_NULL) in the CreateXxxInput type.
278
+ * Returns null if the CreateXxxInput type doesn't exist (no create mutation).
279
+ */
280
+ function buildCreateInputRequiredFieldSet(entityName, typeMap) {
281
+ const createInputName = `Create${entityName}Input`;
282
+ const createInput = typeMap.get(createInputName);
283
+ if (!createInput?.inputFields)
284
+ return null;
285
+ // The CreateXxxInput typically has a single field like { user: UserInput! }
286
+ // We need to look inside the actual entity input type (e.g., UserInput)
287
+ const entityInputName = `${entityName}Input`;
288
+ const entityInput = typeMap.get(entityInputName);
289
+ if (!entityInput?.inputFields)
290
+ return null;
291
+ const requiredFields = new Set();
292
+ for (const inputField of entityInput.inputFields) {
293
+ if ((0, introspection_1.isNonNull)(inputField.type)) {
294
+ requiredFields.add(inputField.name);
295
+ }
296
+ }
297
+ return requiredFields;
298
+ }
256
299
  /**
257
300
  * Check if a type name is an entity type (has a corresponding Connection)
258
301
  */
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { lcFirst, pluralize, singularize, ucFirst } from 'inflekt';
16
16
  import { stripSmartComments } from '../codegen/utils';
17
- import { getBaseTypeName, isList, unwrapType } from '../../types/introspection';
17
+ import { getBaseTypeName, isList, isNonNull, unwrapType } from '../../types/introspection';
18
18
  // ============================================================================
19
19
  // Pattern Matching Constants
20
20
  // ============================================================================
@@ -227,6 +227,10 @@ function extractEntityFields(entityType, typeMap, entityToConnection, commentsEn
227
227
  const fields = [];
228
228
  if (!entityType.fields)
229
229
  return fields;
230
+ // Build a lookup of CreateXxxInput fields to infer hasDefault.
231
+ // If a field is NOT NULL on the entity but NOT required in CreateXxxInput,
232
+ // then it likely has a server-side default (serial, uuid_generate_v4, now(), etc.).
233
+ const createInputRequiredFields = buildCreateInputRequiredFieldSet(entityType.name, typeMap);
230
234
  for (const field of entityType.fields) {
231
235
  const baseTypeName = getBaseTypeName(field.type);
232
236
  if (!baseTypeName)
@@ -240,16 +244,55 @@ function extractEntityFields(entityType, typeMap, entityToConnection, commentsEn
240
244
  continue; // Skip relation fields
241
245
  }
242
246
  }
247
+ // Infer isNotNull from the NON_NULL wrapper on the entity type field
248
+ const fieldIsNotNull = isNonNull(field.type);
249
+ // Infer hasDefault: if a field is NOT NULL on the entity but NOT required
250
+ // in CreateXxxInput, it likely has a default value.
251
+ // Also: if it's absent from CreateInput entirely, it's likely computed/generated.
252
+ let fieldHasDefault = null;
253
+ if (createInputRequiredFields !== null) {
254
+ if (fieldIsNotNull && !createInputRequiredFields.has(field.name)) {
255
+ fieldHasDefault = true;
256
+ }
257
+ else {
258
+ fieldHasDefault = false;
259
+ }
260
+ }
243
261
  // Include scalar, enum, and other non-relation fields
244
262
  const fieldDescription = commentsEnabled ? stripSmartComments(field.description) : undefined;
245
263
  fields.push({
246
264
  name: field.name,
247
265
  ...(fieldDescription ? { description: fieldDescription } : {}),
248
266
  type: convertToCleanFieldType(field.type),
267
+ isNotNull: fieldIsNotNull,
268
+ hasDefault: fieldHasDefault,
249
269
  });
250
270
  }
251
271
  return fields;
252
272
  }
273
+ /**
274
+ * Build a set of field names that are required (NON_NULL) in the CreateXxxInput type.
275
+ * Returns null if the CreateXxxInput type doesn't exist (no create mutation).
276
+ */
277
+ function buildCreateInputRequiredFieldSet(entityName, typeMap) {
278
+ const createInputName = `Create${entityName}Input`;
279
+ const createInput = typeMap.get(createInputName);
280
+ if (!createInput?.inputFields)
281
+ return null;
282
+ // The CreateXxxInput typically has a single field like { user: UserInput! }
283
+ // We need to look inside the actual entity input type (e.g., UserInput)
284
+ const entityInputName = `${entityName}Input`;
285
+ const entityInput = typeMap.get(entityInputName);
286
+ if (!entityInput?.inputFields)
287
+ return null;
288
+ const requiredFields = new Set();
289
+ for (const inputField of entityInput.inputFields) {
290
+ if (isNonNull(inputField.type)) {
291
+ requiredFields.add(inputField.name);
292
+ }
293
+ }
294
+ return requiredFields;
295
+ }
253
296
  /**
254
297
  * Check if a type name is an entity type (has a corresponding Connection)
255
298
  */
@@ -1,3 +1,28 @@
1
+ const relationalFieldSetCache = new WeakMap();
2
+ function getRelationalFieldSet(table) {
3
+ const cached = relationalFieldSetCache.get(table);
4
+ if (cached)
5
+ return cached;
6
+ const set = new Set();
7
+ for (const rel of table.relations.belongsTo) {
8
+ if (rel.fieldName)
9
+ set.add(rel.fieldName);
10
+ }
11
+ for (const rel of table.relations.hasOne) {
12
+ if (rel.fieldName)
13
+ set.add(rel.fieldName);
14
+ }
15
+ for (const rel of table.relations.hasMany) {
16
+ if (rel.fieldName)
17
+ set.add(rel.fieldName);
18
+ }
19
+ for (const rel of table.relations.manyToMany) {
20
+ if (rel.fieldName)
21
+ set.add(rel.fieldName);
22
+ }
23
+ relationalFieldSetCache.set(table, set);
24
+ return set;
25
+ }
1
26
  /**
2
27
  * Convert simplified field selection to QueryBuilder SelectionOptions
3
28
  */
@@ -154,11 +179,7 @@ function getNonRelationalFields(table) {
154
179
  * Check if a field is relational using table metadata
155
180
  */
156
181
  export function isRelationalField(fieldName, table) {
157
- const { belongsTo, hasOne, hasMany, manyToMany } = table.relations;
158
- return (belongsTo.some((rel) => rel.fieldName === fieldName) ||
159
- hasOne.some((rel) => rel.fieldName === fieldName) ||
160
- hasMany.some((rel) => rel.fieldName === fieldName) ||
161
- manyToMany.some((rel) => rel.fieldName === fieldName));
182
+ return getRelationalFieldSet(table).has(fieldName);
162
183
  }
163
184
  /**
164
185
  * Get scalar fields for a related table to include in relation queries
@@ -227,16 +248,20 @@ function getRelatedTableScalarFields(relationField, table, allTables) {
227
248
  'createdAt',
228
249
  'updatedAt',
229
250
  ];
251
+ const scalarFieldSet = new Set(scalarFields);
252
+ // Use Set for O(1) duplicate checking
253
+ const includedSet = new Set();
230
254
  const included = [];
231
255
  const push = (fieldName) => {
232
256
  if (!fieldName)
233
257
  return;
234
- if (!scalarFields.includes(fieldName))
258
+ if (!scalarFieldSet.has(fieldName))
235
259
  return;
236
- if (included.includes(fieldName))
260
+ if (includedSet.has(fieldName))
237
261
  return;
238
262
  if (included.length >= MAX_RELATED_FIELDS)
239
263
  return;
264
+ includedSet.add(fieldName);
240
265
  included.push(fieldName);
241
266
  };
242
267
  // Always try to include stable identifiers first.
@@ -17,8 +17,7 @@ export declare function buildPostGraphileCreate(table: CleanTable, _allTables: C
17
17
  export declare function buildPostGraphileUpdate(table: CleanTable, _allTables: CleanTable[], _options?: MutationOptions): TypedDocumentString<Record<string, unknown>, {
18
18
  input: {
19
19
  id: string | number;
20
- patch: Record<string, unknown>;
21
- };
20
+ } & Record<string, unknown>;
22
21
  }>;
23
22
  /**
24
23
  * Build PostGraphile-style DELETE mutation
@@ -4,10 +4,10 @@
4
4
  */
5
5
  import * as t from 'gql-ast';
6
6
  import { OperationTypeNode, print } from 'graphql';
7
- import { camelize } from 'inflekt';
8
7
  import { TypedDocumentString } from '../client/typed-document';
9
8
  import { getCustomAstForCleanField, requiresSubfieldSelection, } from '../core/custom-ast';
10
9
  import { isRelationalField } from './field-selector';
10
+ import { toCamelCaseSingular, toCreateInputTypeName, toCreateMutationName, toDeleteInputTypeName, toDeleteMutationName, toUpdateInputTypeName, toUpdateMutationName, } from './naming-helpers';
11
11
  /**
12
12
  * Generate field selections for PostGraphile mutations using custom AST logic
13
13
  * This handles both scalar fields and complex types that require subfield selections
@@ -31,14 +31,15 @@ function generateFieldSelections(table) {
31
31
  * PostGraphile expects: mutation { createTableName(input: { tableName: TableNameInput! }) { tableName { ... } } }
32
32
  */
33
33
  export function buildPostGraphileCreate(table, _allTables, _options = {}) {
34
- const mutationName = `create${table.name}`;
35
- const singularName = camelize(table.name, true);
34
+ const mutationName = toCreateMutationName(table.name, table);
35
+ const singularName = toCamelCaseSingular(table.name, table);
36
+ const inputTypeName = toCreateInputTypeName(table.name, table);
36
37
  // Create the variable definition for $input
37
38
  const variableDefinitions = [
38
39
  t.variableDefinition({
39
40
  variable: t.variable({ name: 'input' }),
40
41
  type: t.nonNullType({
41
- type: t.namedType({ type: `Create${table.name}Input` }),
42
+ type: t.namedType({ type: inputTypeName }),
42
43
  }),
43
44
  }),
44
45
  ];
@@ -90,14 +91,15 @@ export function buildPostGraphileCreate(table, _allTables, _options = {}) {
90
91
  * PostGraphile expects: mutation { updateTableName(input: { id: UUID!, patch: TableNamePatch! }) { tableName { ... } } }
91
92
  */
92
93
  export function buildPostGraphileUpdate(table, _allTables, _options = {}) {
93
- const mutationName = `update${table.name}`;
94
- const singularName = camelize(table.name, true);
94
+ const mutationName = toUpdateMutationName(table.name, table);
95
+ const singularName = toCamelCaseSingular(table.name, table);
96
+ const inputTypeName = toUpdateInputTypeName(table.name);
95
97
  // Create the variable definition for $input
96
98
  const variableDefinitions = [
97
99
  t.variableDefinition({
98
100
  variable: t.variable({ name: 'input' }),
99
101
  type: t.nonNullType({
100
- type: t.namedType({ type: `Update${table.name}Input` }),
102
+ type: t.namedType({ type: inputTypeName }),
101
103
  }),
102
104
  }),
103
105
  ];
@@ -149,13 +151,14 @@ export function buildPostGraphileUpdate(table, _allTables, _options = {}) {
149
151
  * PostGraphile expects: mutation { deleteTableName(input: { id: UUID! }) { clientMutationId } }
150
152
  */
151
153
  export function buildPostGraphileDelete(table, _allTables, _options = {}) {
152
- const mutationName = `delete${table.name}`;
154
+ const mutationName = toDeleteMutationName(table.name, table);
155
+ const inputTypeName = toDeleteInputTypeName(table.name);
153
156
  // Create the variable definition for $input
154
157
  const variableDefinitions = [
155
158
  t.variableDefinition({
156
159
  variable: t.variable({ name: 'input' }),
157
160
  type: t.nonNullType({
158
- type: t.namedType({ type: `Delete${table.name}Input` }),
161
+ type: t.namedType({ type: inputTypeName }),
159
162
  }),
160
163
  }),
161
164
  ];
@@ -0,0 +1,48 @@
1
+ import type { CleanTable } from '../types/schema';
2
+ /**
3
+ * Safely normalise a server-provided inflection value.
4
+ * Returns `null` for null, undefined, or whitespace-only strings.
5
+ */
6
+ export declare function normalizeInflectionValue(value: string | null | undefined): string | null;
7
+ /**
8
+ * Convert PascalCase table name to camelCase plural for GraphQL queries.
9
+ * Prefers server-provided `table.query.all` / `table.inflection.allRows`
10
+ * when available, with guards against naive pluralisation drift and
11
+ * missing camelCase boundaries.
12
+ *
13
+ * Example: "ActionGoal" -> "actionGoals", "User" -> "users", "Person" -> "people"
14
+ */
15
+ export declare function toCamelCasePlural(tableName: string, table?: CleanTable | null): string;
16
+ /**
17
+ * Convert PascalCase table name to camelCase singular field name.
18
+ * Prefers server-provided names when available.
19
+ */
20
+ export declare function toCamelCaseSingular(tableName: string, table?: CleanTable | null): string;
21
+ export declare function toCreateMutationName(tableName: string, table?: CleanTable | null): string;
22
+ export declare function toUpdateMutationName(tableName: string, table?: CleanTable | null): string;
23
+ export declare function toDeleteMutationName(tableName: string, table?: CleanTable | null): string;
24
+ export declare function toCreateInputTypeName(tableName: string, table?: CleanTable | null): string;
25
+ export declare function toUpdateInputTypeName(tableName: string): string;
26
+ export declare function toDeleteInputTypeName(tableName: string): string;
27
+ export declare function toFilterTypeName(tableName: string, table?: CleanTable | null): string;
28
+ /**
29
+ * Resolve PostGraphile patch field name.
30
+ * In v5 this is typically entity-specific: e.g. "userPatch", "contactPatch".
31
+ * Prefers the value discovered from the schema (`table.query.patchFieldName`
32
+ * or `table.inflection.patchField`), falls back to `${singularName}Patch`.
33
+ */
34
+ export declare function toPatchFieldName(tableName: string, table?: CleanTable | null): string;
35
+ /**
36
+ * Convert camelCase field name to SCREAMING_SNAKE_CASE for PostGraphile
37
+ * orderBy enums.
38
+ *
39
+ * "displayName" -> "DISPLAY_NAME_ASC"
40
+ * "createdAt" -> "CREATED_AT_DESC"
41
+ * "id" -> "ID_ASC"
42
+ */
43
+ export declare function toOrderByEnumValue(fieldName: string, direction: 'asc' | 'desc'): string;
44
+ /**
45
+ * Generate the PostGraphile OrderBy enum type name for a table.
46
+ * Prefers server-provided `table.inflection.orderByType` when available.
47
+ */
48
+ export declare function toOrderByTypeName(tableName: string, table?: CleanTable | null): string;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Server-aware naming helpers for GraphQL query/mutation generation.
3
+ *
4
+ * These functions prefer names already discovered from the GraphQL schema
5
+ * (stored on `table.query` and `table.inflection` by `infer-tables.ts`)
6
+ * and fall back to local inflection when introspection data is unavailable.
7
+ *
8
+ * Back-ported from Dashboard's `packages/data/src/query-generator.ts`.
9
+ */
10
+ import { camelize, pluralize } from 'inflekt';
11
+ // ---------------------------------------------------------------------------
12
+ // Internal helpers
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Safely normalise a server-provided inflection value.
16
+ * Returns `null` for null, undefined, or whitespace-only strings.
17
+ */
18
+ export function normalizeInflectionValue(value) {
19
+ if (typeof value !== 'string')
20
+ return null;
21
+ const trimmed = value.trim();
22
+ return trimmed.length > 0 ? trimmed : null;
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Plural / Singular
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Convert PascalCase table name to camelCase plural for GraphQL queries.
29
+ * Prefers server-provided `table.query.all` / `table.inflection.allRows`
30
+ * when available, with guards against naive pluralisation drift and
31
+ * missing camelCase boundaries.
32
+ *
33
+ * Example: "ActionGoal" -> "actionGoals", "User" -> "users", "Person" -> "people"
34
+ */
35
+ export function toCamelCasePlural(tableName, table) {
36
+ const singular = camelize(tableName, true);
37
+ const inflectedPlural = pluralize(singular);
38
+ const serverPluralCandidates = [
39
+ table?.query?.all,
40
+ table?.inflection?.allRows,
41
+ ];
42
+ for (const candidateRaw of serverPluralCandidates) {
43
+ const candidate = normalizeInflectionValue(candidateRaw);
44
+ if (!candidate)
45
+ continue;
46
+ // Guard against known fallback drift:
47
+ // 1. Naive pluralisation: "activitys" instead of "activities"
48
+ const isNaivePlural = candidate === `${singular}s` && candidate !== inflectedPlural;
49
+ // 2. Missing camelCase boundaries: "deliveryzones" instead of "deliveryZones"
50
+ const isMiscased = candidate !== inflectedPlural &&
51
+ candidate.toLowerCase() === inflectedPlural.toLowerCase();
52
+ if (isNaivePlural || isMiscased)
53
+ continue;
54
+ return candidate;
55
+ }
56
+ return inflectedPlural;
57
+ }
58
+ /**
59
+ * Convert PascalCase table name to camelCase singular field name.
60
+ * Prefers server-provided names when available.
61
+ */
62
+ export function toCamelCaseSingular(tableName, table) {
63
+ const localSingular = camelize(tableName, true);
64
+ for (const candidateRaw of [
65
+ table?.query?.one,
66
+ table?.inflection?.tableFieldName,
67
+ ]) {
68
+ const candidate = normalizeInflectionValue(candidateRaw);
69
+ if (!candidate)
70
+ continue;
71
+ // Reject miscased versions: "deliveryzone" vs "deliveryZone"
72
+ if (candidate !== localSingular &&
73
+ candidate.toLowerCase() === localSingular.toLowerCase())
74
+ continue;
75
+ return candidate;
76
+ }
77
+ return localSingular;
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Mutation names
81
+ // ---------------------------------------------------------------------------
82
+ export function toCreateMutationName(tableName, table) {
83
+ return (normalizeInflectionValue(table?.query?.create) ?? `create${tableName}`);
84
+ }
85
+ export function toUpdateMutationName(tableName, table) {
86
+ return (normalizeInflectionValue(table?.query?.update) ?? `update${tableName}`);
87
+ }
88
+ export function toDeleteMutationName(tableName, table) {
89
+ return (normalizeInflectionValue(table?.query?.delete) ?? `delete${tableName}`);
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Input / type names
93
+ // ---------------------------------------------------------------------------
94
+ export function toCreateInputTypeName(tableName, table) {
95
+ return (normalizeInflectionValue(table?.inflection?.createInputType) ??
96
+ `Create${tableName}Input`);
97
+ }
98
+ export function toUpdateInputTypeName(tableName) {
99
+ return `Update${tableName}Input`;
100
+ }
101
+ export function toDeleteInputTypeName(tableName) {
102
+ return `Delete${tableName}Input`;
103
+ }
104
+ export function toFilterTypeName(tableName, table) {
105
+ return (normalizeInflectionValue(table?.inflection?.filterType) ??
106
+ `${tableName}Filter`);
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Patch field name
110
+ // ---------------------------------------------------------------------------
111
+ /**
112
+ * Resolve PostGraphile patch field name.
113
+ * In v5 this is typically entity-specific: e.g. "userPatch", "contactPatch".
114
+ * Prefers the value discovered from the schema (`table.query.patchFieldName`
115
+ * or `table.inflection.patchField`), falls back to `${singularName}Patch`.
116
+ */
117
+ export function toPatchFieldName(tableName, table) {
118
+ // First check the patch field name discovered from UpdateXxxInput
119
+ const introspectedPatch = normalizeInflectionValue(table?.query?.patchFieldName);
120
+ if (introspectedPatch)
121
+ return introspectedPatch;
122
+ // Then check the inflection table
123
+ const explicitPatchField = normalizeInflectionValue(table?.inflection?.patchField);
124
+ if (explicitPatchField)
125
+ return explicitPatchField;
126
+ return `${toCamelCaseSingular(tableName, table)}Patch`;
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // OrderBy helpers
130
+ // ---------------------------------------------------------------------------
131
+ /**
132
+ * Convert camelCase field name to SCREAMING_SNAKE_CASE for PostGraphile
133
+ * orderBy enums.
134
+ *
135
+ * "displayName" -> "DISPLAY_NAME_ASC"
136
+ * "createdAt" -> "CREATED_AT_DESC"
137
+ * "id" -> "ID_ASC"
138
+ */
139
+ export function toOrderByEnumValue(fieldName, direction) {
140
+ const screaming = fieldName
141
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
142
+ .toUpperCase();
143
+ return `${screaming}_${direction.toUpperCase()}`;
144
+ }
145
+ /**
146
+ * Generate the PostGraphile OrderBy enum type name for a table.
147
+ * Prefers server-provided `table.inflection.orderByType` when available.
148
+ */
149
+ export function toOrderByTypeName(tableName, table) {
150
+ if (table?.inflection?.orderByType)
151
+ return table.inflection.orderByType;
152
+ const plural = toCamelCasePlural(tableName, table);
153
+ return `${plural.charAt(0).toUpperCase() + plural.slice(1)}OrderBy`;
154
+ }
@@ -3,18 +3,7 @@ import { QueryBuilder } from '../core/query-builder';
3
3
  import type { IntrospectionSchema, MetaObject } from '../core/types';
4
4
  import type { QueryOptions } from '../types/query';
5
5
  import type { CleanTable } from '../types/schema';
6
- /**
7
- * Convert PascalCase table name to camelCase plural for GraphQL queries
8
- * Uses the inflection library for proper pluralization
9
- * Example: "ActionGoal" -> "actionGoals", "User" -> "users", "Person" -> "people"
10
- */
11
- export declare function toCamelCasePlural(tableName: string): string;
12
- /**
13
- * Generate the PostGraphile OrderBy enum type name for a table
14
- * PostGraphile uses pluralized PascalCase: "Product" -> "ProductsOrderBy"
15
- * Example: "Product" -> "ProductsOrderBy", "Person" -> "PeopleOrderBy"
16
- */
17
- export declare function toOrderByTypeName(tableName: string): string;
6
+ export { toCamelCasePlural, toOrderByTypeName } from './naming-helpers';
18
7
  /**
19
8
  * Convert CleanTable to MetaObject format for QueryBuilder
20
9
  */