@constructive-io/graphql-codegen 4.37.2 → 4.38.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.
@@ -94,9 +94,10 @@ exports.commentLine = commentLine;
94
94
  * Add a leading JSDoc comment to a node
95
95
  */
96
96
  function addJSDocComment(node, lines) {
97
- const commentText = lines.length === 1
98
- ? `* ${lines[0]} `
99
- : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `;
97
+ const sanitized = lines.map((line) => line.replace(/\*\//g, '*\\/'));
98
+ const commentText = sanitized.length === 1
99
+ ? `* ${sanitized[0]} `
100
+ : `*\n${sanitized.map((line) => ` * ${line}`).join('\n')}\n `;
100
101
  if (!node.leadingComments) {
101
102
  node.leadingComments = [];
102
103
  }
@@ -392,9 +392,10 @@ function destructureParamsWithSelectionAndScope(restName) {
392
392
  // JSDoc comment helpers
393
393
  // ============================================================================
394
394
  function addJSDocComment(node, lines) {
395
- const text = lines.length === 1
396
- ? `* ${lines[0]} `
397
- : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `;
395
+ const sanitized = lines.map((line) => line.replace(/\*\//g, '*\\/'));
396
+ const text = sanitized.length === 1
397
+ ? `* ${sanitized[0]} `
398
+ : `*\n${sanitized.map((line) => ` * ${line}`).join('\n')}\n `;
398
399
  if (!node.leadingComments) {
399
400
  node.leadingComments = [];
400
401
  }
@@ -588,12 +588,40 @@ function generateEntityWithRelations(tables) {
588
588
  */
589
589
  function buildSelectTypeLiteral(table, tableByName) {
590
590
  const members = [];
591
- // Add scalar fields
591
+ // Add scalar fields (and fields with arguments)
592
592
  for (const field of table.fields) {
593
593
  if (!(0, utils_1.isRelationField)(field.name, table)) {
594
- const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsBooleanKeyword()));
595
- prop.optional = true;
596
- members.push(prop);
594
+ if (field.args && field.args.length > 0) {
595
+ // Field with arguments (e.g. requestUploadUrl on bucket types)
596
+ const argMembers = field.args.map((arg) => {
597
+ const tsType = typeRefToTs(arg.type);
598
+ const argProp = t.tsPropertySignature(t.identifier(arg.name), t.tsTypeAnnotation(parseTypeString(tsType)));
599
+ argProp.optional = !arg.isRequired;
600
+ return argProp;
601
+ });
602
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeLiteral([
603
+ (() => {
604
+ const argsProp = t.tsPropertySignature(t.identifier('args'), t.tsTypeAnnotation(t.tsTypeLiteral(argMembers)));
605
+ argsProp.optional = false;
606
+ return argsProp;
607
+ })(),
608
+ (() => {
609
+ const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
610
+ t.tsStringKeyword(),
611
+ t.tsBooleanKeyword(),
612
+ ]))));
613
+ selectProp.optional = true;
614
+ return selectProp;
615
+ })(),
616
+ ])));
617
+ prop.optional = true;
618
+ members.push(prop);
619
+ }
620
+ else {
621
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsBooleanKeyword()));
622
+ prop.optional = true;
623
+ members.push(prop);
624
+ }
597
625
  }
598
626
  }
599
627
  // Add belongsTo relations
@@ -37,6 +37,7 @@ export type SelectConfig<TFields extends string> = {
37
37
  */
38
38
  export interface NestedSelectConfig {
39
39
  select?: Record<string, boolean | NestedSelectConfig>;
40
+ args?: Record<string, unknown>;
40
41
  first?: number;
41
42
  last?: number;
42
43
  after?: string;
@@ -101,6 +102,9 @@ export type HookStrictSelect<S, Shape> = S extends DeepExact<S, Shape, 5> ? {} :
101
102
  */
102
103
  export type InferSelectResult<TEntity, TSelect> = TSelect extends undefined ? TEntity : {
103
104
  [K in keyof TSelect as TSelect[K] extends false | undefined ? never : K]: TSelect[K] extends true ? K extends keyof TEntity ? TEntity[K] : never : TSelect[K] extends {
105
+ args: Record<string, unknown>;
106
+ select: infer NestedSelect;
107
+ } ? K extends keyof TEntity ? NonNullable<TEntity[K]> extends ConnectionResult<infer NodeType> ? ConnectionResult<InferSelectResult<NodeType, NestedSelect>> : InferSelectResult<NonNullable<TEntity[K]>, NestedSelect> | (null extends TEntity[K] ? null : never) : never : TSelect[K] extends {
104
108
  select: infer NestedSelect;
105
109
  } ? K extends keyof TEntity ? NonNullable<TEntity[K]> extends ConnectionResult<infer NodeType> ? ConnectionResult<InferSelectResult<NodeType, NestedSelect>> : InferSelectResult<NonNullable<TEntity[K]>, NestedSelect> | (null extends TEntity[K] ? null : never) : never : K extends keyof TEntity ? TEntity[K] : never;
106
110
  };
@@ -138,12 +138,44 @@ export function buildSelections(
138
138
  if (typeof value === 'object' && value !== null) {
139
139
  const nested = value as {
140
140
  select?: Record<string, unknown>;
141
+ args?: Record<string, unknown>;
141
142
  first?: number;
142
143
  filter?: Record<string, unknown>;
143
144
  orderBy?: string[];
144
145
  connection?: boolean;
145
146
  };
146
147
 
148
+ // Field with arguments (e.g. requestUploadUrl on bucket types)
149
+ if (nested.args && typeof nested.args === 'object') {
150
+ const fieldArgs = Object.entries(nested.args).map(
151
+ ([argName, argValue]) =>
152
+ t.argument({ name: argName, value: buildValueAst(argValue) }),
153
+ );
154
+ const nestedSelect = nested.select;
155
+ if (nestedSelect && typeof nestedSelect === 'object') {
156
+ const subSelections = Object.entries(nestedSelect)
157
+ .filter(([, v]) => v)
158
+ .map(([name]) => t.field({ name }));
159
+ fields.push(
160
+ t.field({
161
+ name: key,
162
+ args: fieldArgs.length ? fieldArgs : undefined,
163
+ selectionSet: subSelections.length
164
+ ? t.selectionSet({ selections: subSelections })
165
+ : undefined,
166
+ }),
167
+ );
168
+ } else {
169
+ fields.push(
170
+ t.field({
171
+ name: key,
172
+ args: fieldArgs.length ? fieldArgs : undefined,
173
+ }),
174
+ );
175
+ }
176
+ continue;
177
+ }
178
+
147
179
  if (!nested.select || typeof nested.select !== 'object') {
148
180
  throw new Error(
149
181
  `Invalid selection for field "${key}": nested selections must include a "select" object.`,
@@ -45,9 +45,10 @@ export const commentLine = (value) => {
45
45
  * Add a leading JSDoc comment to a node
46
46
  */
47
47
  export function addJSDocComment(node, lines) {
48
- const commentText = lines.length === 1
49
- ? `* ${lines[0]} `
50
- : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `;
48
+ const sanitized = lines.map((line) => line.replace(/\*\//g, '*\\/'));
49
+ const commentText = sanitized.length === 1
50
+ ? `* ${sanitized[0]} `
51
+ : `*\n${sanitized.map((line) => ` * ${line}`).join('\n')}\n `;
51
52
  if (!node.leadingComments) {
52
53
  node.leadingComments = [];
53
54
  }
@@ -294,9 +294,10 @@ export function destructureParamsWithSelectionAndScope(restName) {
294
294
  // JSDoc comment helpers
295
295
  // ============================================================================
296
296
  export function addJSDocComment(node, lines) {
297
- const text = lines.length === 1
298
- ? `* ${lines[0]} `
299
- : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `;
297
+ const sanitized = lines.map((line) => line.replace(/\*\//g, '*\\/'));
298
+ const text = sanitized.length === 1
299
+ ? `* ${sanitized[0]} `
300
+ : `*\n${sanitized.map((line) => ` * ${line}`).join('\n')}\n `;
300
301
  if (!node.leadingComments) {
301
302
  node.leadingComments = [];
302
303
  }
@@ -550,12 +550,40 @@ function generateEntityWithRelations(tables) {
550
550
  */
551
551
  function buildSelectTypeLiteral(table, tableByName) {
552
552
  const members = [];
553
- // Add scalar fields
553
+ // Add scalar fields (and fields with arguments)
554
554
  for (const field of table.fields) {
555
555
  if (!isRelationField(field.name, table)) {
556
- const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsBooleanKeyword()));
557
- prop.optional = true;
558
- members.push(prop);
556
+ if (field.args && field.args.length > 0) {
557
+ // Field with arguments (e.g. requestUploadUrl on bucket types)
558
+ const argMembers = field.args.map((arg) => {
559
+ const tsType = typeRefToTs(arg.type);
560
+ const argProp = t.tsPropertySignature(t.identifier(arg.name), t.tsTypeAnnotation(parseTypeString(tsType)));
561
+ argProp.optional = !arg.isRequired;
562
+ return argProp;
563
+ });
564
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeLiteral([
565
+ (() => {
566
+ const argsProp = t.tsPropertySignature(t.identifier('args'), t.tsTypeAnnotation(t.tsTypeLiteral(argMembers)));
567
+ argsProp.optional = false;
568
+ return argsProp;
569
+ })(),
570
+ (() => {
571
+ const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
572
+ t.tsStringKeyword(),
573
+ t.tsBooleanKeyword(),
574
+ ]))));
575
+ selectProp.optional = true;
576
+ return selectProp;
577
+ })(),
578
+ ])));
579
+ prop.optional = true;
580
+ members.push(prop);
581
+ }
582
+ else {
583
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsBooleanKeyword()));
584
+ prop.optional = true;
585
+ members.push(prop);
586
+ }
559
587
  }
560
588
  }
561
589
  // Add belongsTo relations
@@ -37,6 +37,7 @@ export type SelectConfig<TFields extends string> = {
37
37
  */
38
38
  export interface NestedSelectConfig {
39
39
  select?: Record<string, boolean | NestedSelectConfig>;
40
+ args?: Record<string, unknown>;
40
41
  first?: number;
41
42
  last?: number;
42
43
  after?: string;
@@ -101,6 +102,9 @@ export type HookStrictSelect<S, Shape> = S extends DeepExact<S, Shape, 5> ? {} :
101
102
  */
102
103
  export type InferSelectResult<TEntity, TSelect> = TSelect extends undefined ? TEntity : {
103
104
  [K in keyof TSelect as TSelect[K] extends false | undefined ? never : K]: TSelect[K] extends true ? K extends keyof TEntity ? TEntity[K] : never : TSelect[K] extends {
105
+ args: Record<string, unknown>;
106
+ select: infer NestedSelect;
107
+ } ? K extends keyof TEntity ? NonNullable<TEntity[K]> extends ConnectionResult<infer NodeType> ? ConnectionResult<InferSelectResult<NodeType, NestedSelect>> : InferSelectResult<NonNullable<TEntity[K]>, NestedSelect> | (null extends TEntity[K] ? null : never) : never : TSelect[K] extends {
104
108
  select: infer NestedSelect;
105
109
  } ? K extends keyof TEntity ? NonNullable<TEntity[K]> extends ConnectionResult<infer NodeType> ? ConnectionResult<InferSelectResult<NodeType, NestedSelect>> : InferSelectResult<NonNullable<TEntity[K]>, NestedSelect> | (null extends TEntity[K] ? null : never) : never : K extends keyof TEntity ? TEntity[K] : never;
106
110
  };
@@ -112,6 +112,22 @@ export interface Field {
112
112
  isNotNull?: boolean | null;
113
113
  /** Whether the column has a DEFAULT value (inferred by comparing entity vs CreateInput field nullability) */
114
114
  hasDefault?: boolean | null;
115
+ /** Arguments for computed fields (e.g. requestUploadUrl on bucket types) */
116
+ args?: FieldArgument[];
117
+ }
118
+ /**
119
+ * Argument on a computed field (not a root operation)
120
+ */
121
+ export interface FieldArgument {
122
+ name: string;
123
+ /** GraphQL type reference */
124
+ type: TypeRef;
125
+ /** Whether this argument is required (NON_NULL) */
126
+ isRequired: boolean;
127
+ /** Description from schema */
128
+ description?: string;
129
+ /** Default value (as string) */
130
+ defaultValue?: string;
115
131
  }
116
132
  /**
117
133
  * Field type information from PostGraphile introspection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-codegen",
3
- "version": "4.37.2",
3
+ "version": "4.38.0",
4
4
  "description": "GraphQL SDK generator for Constructive databases with React Query hooks",
5
5
  "keywords": [
6
6
  "graphql",
@@ -56,7 +56,7 @@
56
56
  "@0no-co/graphql.web": "^1.1.2",
57
57
  "@babel/generator": "^7.29.1",
58
58
  "@babel/types": "^7.29.0",
59
- "@constructive-io/graphql-query": "^3.20.2",
59
+ "@constructive-io/graphql-query": "^3.21.0",
60
60
  "@constructive-io/graphql-types": "^3.8.0",
61
61
  "@inquirerer/utils": "^3.3.5",
62
62
  "@pgpmjs/core": "^6.16.0",
@@ -64,7 +64,7 @@
64
64
  "deepmerge": "^4.3.1",
65
65
  "find-and-require-package-json": "^0.9.1",
66
66
  "gql-ast": "^3.8.0",
67
- "graphile-schema": "^1.18.2",
67
+ "graphile-schema": "^1.18.3",
68
68
  "graphql": "16.13.0",
69
69
  "inflekt": "^0.7.1",
70
70
  "inquirerer": "^4.7.0",
@@ -100,5 +100,5 @@
100
100
  "tsx": "^4.21.0",
101
101
  "typescript": "^5.9.3"
102
102
  },
103
- "gitHead": "b1a89c4ab45fd22a63487cc6dc923a17772fac60"
103
+ "gitHead": "97ec8e14f2b0855b0ee0bc732a082e1a91301b64"
104
104
  }
package/types/schema.d.ts CHANGED
@@ -112,6 +112,22 @@ export interface Field {
112
112
  isNotNull?: boolean | null;
113
113
  /** Whether the column has a DEFAULT value (inferred by comparing entity vs CreateInput field nullability) */
114
114
  hasDefault?: boolean | null;
115
+ /** Arguments for computed fields (e.g. requestUploadUrl on bucket types) */
116
+ args?: FieldArgument[];
117
+ }
118
+ /**
119
+ * Argument on a computed field (not a root operation)
120
+ */
121
+ export interface FieldArgument {
122
+ name: string;
123
+ /** GraphQL type reference */
124
+ type: TypeRef;
125
+ /** Whether this argument is required (NON_NULL) */
126
+ isRequired: boolean;
127
+ /** Description from schema */
128
+ description?: string;
129
+ /** Default value (as string) */
130
+ defaultValue?: string;
115
131
  }
116
132
  /**
117
133
  * Field type information from PostGraphile introspection