@constructive-io/graphql-query 3.23.3 → 3.24.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.
@@ -200,6 +200,10 @@ function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationF
200
200
  update: mutationOps.update,
201
201
  delete: mutationOps.delete,
202
202
  patchFieldName,
203
+ bulkInsert: mutationOps.bulkInsert,
204
+ bulkUpsert: mutationOps.bulkUpsert,
205
+ bulkUpdate: mutationOps.bulkUpdate,
206
+ bulkDelete: mutationOps.bulkDelete,
203
207
  };
204
208
  // Extract description from entity type (PostgreSQL COMMENT), strip smart comments
205
209
  const description = commentsEnabled ? stripSmartComments(entityType.description) : undefined;
@@ -488,14 +492,28 @@ function matchQueryOperations(entityName, queryFields, entityToConnection) {
488
492
  * - create{EntityName}
489
493
  * - update{EntityName} or update{EntityName}ById
490
494
  * - delete{EntityName} or delete{EntityName}ById
495
+ * - bulkCreate{PluralName} (bulk insert)
496
+ * - bulkUpsert{PluralName} (bulk upsert)
497
+ * - bulkUpdate{PluralName} (bulk update)
498
+ * - bulkDelete{PluralName} (bulk delete)
491
499
  */
492
500
  function matchMutationOperations(entityName, mutationFields) {
493
501
  let create = null;
494
502
  let update = null;
495
503
  let del = null;
504
+ let bulkInsert = null;
505
+ let bulkUpsert = null;
506
+ let bulkUpdate = null;
507
+ let bulkDelete = null;
496
508
  const expectedCreate = `create${entityName}`;
497
509
  const expectedUpdate = `update${entityName}`;
498
510
  const expectedDelete = `delete${entityName}`;
511
+ // Bulk mutation patterns use plural form: bulkCreate{Plural}
512
+ const pluralName = pluralize(entityName);
513
+ const expectedBulkInsert = `bulkCreate${pluralName}`;
514
+ const expectedBulkUpsert = `bulkUpsert${pluralName}`;
515
+ const expectedBulkUpdate = `bulkUpdate${pluralName}`;
516
+ const expectedBulkDelete = `bulkDelete${pluralName}`;
499
517
  for (const field of mutationFields) {
500
518
  // Exact match for create
501
519
  if (field.name === expectedCreate) {
@@ -519,8 +537,21 @@ function matchMutationOperations(entityName, mutationFields) {
519
537
  field.name.startsWith(`${expectedDelete}By`))) {
520
538
  del = field.name;
521
539
  }
540
+ // Bulk mutations
541
+ if (field.name === expectedBulkInsert) {
542
+ bulkInsert = field.name;
543
+ }
544
+ if (field.name === expectedBulkUpsert) {
545
+ bulkUpsert = field.name;
546
+ }
547
+ if (field.name === expectedBulkUpdate) {
548
+ bulkUpdate = field.name;
549
+ }
550
+ if (field.name === expectedBulkDelete) {
551
+ bulkDelete = field.name;
552
+ }
522
553
  }
523
- return { create, update, delete: del };
554
+ return { create, update, delete: del, bulkInsert, bulkUpsert, bulkUpdate, bulkDelete };
524
555
  }
525
556
  // ============================================================================
526
557
  // Constraint Inference
@@ -241,6 +241,15 @@ export function getTableOperationNames(tables) {
241
241
  mutations.add(table.query.update);
242
242
  if (table.query.delete)
243
243
  mutations.add(table.query.delete);
244
+ // Add bulk mutation names
245
+ if (table.query.bulkInsert)
246
+ mutations.add(table.query.bulkInsert);
247
+ if (table.query.bulkUpsert)
248
+ mutations.add(table.query.bulkUpsert);
249
+ if (table.query.bulkUpdate)
250
+ mutations.add(table.query.bulkUpdate);
251
+ if (table.query.bulkDelete)
252
+ mutations.add(table.query.bulkDelete);
244
253
  }
245
254
  }
246
255
  return { queries, mutations };
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Generated ORM clients need runtime dependencies at execution time.
5
5
  * This module re-exports so generated code can consolidate imports:
6
- * - @0no-co/graphql.web — parseType, print
7
- * - @constructive-io/graphql-types — GraphQLAdapter, GraphQLError, QueryResult
8
- * - ./localhost-fetch — createFetch (isomorphic *.localhost-aware fetch)
6
+ * - @0no-co/graphql.web — parseType, print
7
+ * - @constructive-io/graphql-types — GraphQLAdapter, GraphQLError, QueryResult
8
+ * - @constructive-io/fetch — createFetch (isomorphic *.localhost-aware fetch)
9
9
  *
10
10
  * gql-ast is intentionally NOT re-exported here because the templates
11
11
  * use `import * as t from 'gql-ast'` — mixing it into this namespace
@@ -16,7 +16,5 @@
16
16
  * import * as t from 'gql-ast';
17
17
  * import type { GraphQLAdapter } from '@constructive-io/graphql-query/runtime';
18
18
  */
19
- // From @0no-co/graphql.web — GraphQL parsing/printing
20
19
  export { parseType, print } from '@0no-co/graphql.web';
21
- // Isomorphic fetch with *.localhost DNS + Host header fix for Node.js
22
- export { createFetch } from './localhost-fetch';
20
+ export { createFetch } from '@constructive-io/fetch';
@@ -203,6 +203,10 @@ function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationF
203
203
  update: mutationOps.update,
204
204
  delete: mutationOps.delete,
205
205
  patchFieldName,
206
+ bulkInsert: mutationOps.bulkInsert,
207
+ bulkUpsert: mutationOps.bulkUpsert,
208
+ bulkUpdate: mutationOps.bulkUpdate,
209
+ bulkDelete: mutationOps.bulkDelete,
206
210
  };
207
211
  // Extract description from entity type (PostgreSQL COMMENT), strip smart comments
208
212
  const description = commentsEnabled ? (0, utils_1.stripSmartComments)(entityType.description) : undefined;
@@ -491,14 +495,28 @@ function matchQueryOperations(entityName, queryFields, entityToConnection) {
491
495
  * - create{EntityName}
492
496
  * - update{EntityName} or update{EntityName}ById
493
497
  * - delete{EntityName} or delete{EntityName}ById
498
+ * - bulkCreate{PluralName} (bulk insert)
499
+ * - bulkUpsert{PluralName} (bulk upsert)
500
+ * - bulkUpdate{PluralName} (bulk update)
501
+ * - bulkDelete{PluralName} (bulk delete)
494
502
  */
495
503
  function matchMutationOperations(entityName, mutationFields) {
496
504
  let create = null;
497
505
  let update = null;
498
506
  let del = null;
507
+ let bulkInsert = null;
508
+ let bulkUpsert = null;
509
+ let bulkUpdate = null;
510
+ let bulkDelete = null;
499
511
  const expectedCreate = `create${entityName}`;
500
512
  const expectedUpdate = `update${entityName}`;
501
513
  const expectedDelete = `delete${entityName}`;
514
+ // Bulk mutation patterns use plural form: bulkCreate{Plural}
515
+ const pluralName = (0, inflekt_1.pluralize)(entityName);
516
+ const expectedBulkInsert = `bulkCreate${pluralName}`;
517
+ const expectedBulkUpsert = `bulkUpsert${pluralName}`;
518
+ const expectedBulkUpdate = `bulkUpdate${pluralName}`;
519
+ const expectedBulkDelete = `bulkDelete${pluralName}`;
502
520
  for (const field of mutationFields) {
503
521
  // Exact match for create
504
522
  if (field.name === expectedCreate) {
@@ -522,8 +540,21 @@ function matchMutationOperations(entityName, mutationFields) {
522
540
  field.name.startsWith(`${expectedDelete}By`))) {
523
541
  del = field.name;
524
542
  }
543
+ // Bulk mutations
544
+ if (field.name === expectedBulkInsert) {
545
+ bulkInsert = field.name;
546
+ }
547
+ if (field.name === expectedBulkUpsert) {
548
+ bulkUpsert = field.name;
549
+ }
550
+ if (field.name === expectedBulkUpdate) {
551
+ bulkUpdate = field.name;
552
+ }
553
+ if (field.name === expectedBulkDelete) {
554
+ bulkDelete = field.name;
555
+ }
525
556
  }
526
- return { create, update, delete: del };
557
+ return { create, update, delete: del, bulkInsert, bulkUpsert, bulkUpdate, bulkDelete };
527
558
  }
528
559
  // ============================================================================
529
560
  // Constraint Inference
@@ -62,6 +62,10 @@ export declare function getTableOperationNames(tables: Array<{
62
62
  create: string;
63
63
  update: string | null;
64
64
  delete: string | null;
65
+ bulkInsert?: string | null;
66
+ bulkUpsert?: string | null;
67
+ bulkUpdate?: string | null;
68
+ bulkDelete?: string | null;
65
69
  };
66
70
  }>): TableOperationNames;
67
71
  /**
@@ -253,6 +253,15 @@ function getTableOperationNames(tables) {
253
253
  mutations.add(table.query.update);
254
254
  if (table.query.delete)
255
255
  mutations.add(table.query.delete);
256
+ // Add bulk mutation names
257
+ if (table.query.bulkInsert)
258
+ mutations.add(table.query.bulkInsert);
259
+ if (table.query.bulkUpsert)
260
+ mutations.add(table.query.bulkUpsert);
261
+ if (table.query.bulkUpdate)
262
+ mutations.add(table.query.bulkUpdate);
263
+ if (table.query.bulkDelete)
264
+ mutations.add(table.query.bulkDelete);
256
265
  }
257
266
  }
258
267
  return { queries, mutations };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-query",
3
- "version": "3.23.3",
3
+ "version": "3.24.0",
4
4
  "description": "Constructive GraphQL Query",
5
5
  "author": "Constructive <developers@constructive.io>",
6
6
  "main": "index.js",
@@ -30,13 +30,14 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@0no-co/graphql.web": "^1.1.2",
33
+ "@constructive-io/fetch": "^1.0.0",
33
34
  "@constructive-io/graphql-types": "^3.9.1",
34
35
  "ajv": "^8.18.0",
35
36
  "gql-ast": "^3.9.1",
36
37
  "grafast": "1.0.0",
37
38
  "graphile-build-pg": "5.0.0",
38
39
  "graphile-config": "1.0.0",
39
- "graphile-settings": "^4.33.2",
40
+ "graphile-settings": "^5.0.0",
40
41
  "graphql": "16.13.0",
41
42
  "inflection": "^3.0.0",
42
43
  "inflekt": "^0.7.1",
@@ -53,5 +54,5 @@
53
54
  "devDependencies": {
54
55
  "makage": "^0.3.0"
55
56
  },
56
- "gitHead": "ea590e1b9e1ee38c267f8dbbb37aa3f83a5d3fb7"
57
+ "gitHead": "70f9d2052fe9218c65b82e896d1660d4dc6a51c8"
57
58
  }
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Generated ORM clients need runtime dependencies at execution time.
5
5
  * This module re-exports so generated code can consolidate imports:
6
- * - @0no-co/graphql.web — parseType, print
7
- * - @constructive-io/graphql-types — GraphQLAdapter, GraphQLError, QueryResult
8
- * - ./localhost-fetch — createFetch (isomorphic *.localhost-aware fetch)
6
+ * - @0no-co/graphql.web — parseType, print
7
+ * - @constructive-io/graphql-types — GraphQLAdapter, GraphQLError, QueryResult
8
+ * - @constructive-io/fetch — createFetch (isomorphic *.localhost-aware fetch)
9
9
  *
10
10
  * gql-ast is intentionally NOT re-exported here because the templates
11
11
  * use `import * as t from 'gql-ast'` — mixing it into this namespace
@@ -18,5 +18,5 @@
18
18
  */
19
19
  export { parseType, print } from '@0no-co/graphql.web';
20
20
  export type { GraphQLAdapter, GraphQLError, QueryResult } from '@constructive-io/graphql-types';
21
- export { createFetch } from './localhost-fetch';
22
- export type { FetchFunction } from './localhost-fetch';
21
+ export { createFetch } from '@constructive-io/fetch';
22
+ export type { FetchFunction } from '@constructive-io/fetch';
package/runtime/index.js CHANGED
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * Generated ORM clients need runtime dependencies at execution time.
6
6
  * This module re-exports so generated code can consolidate imports:
7
- * - @0no-co/graphql.web — parseType, print
8
- * - @constructive-io/graphql-types — GraphQLAdapter, GraphQLError, QueryResult
9
- * - ./localhost-fetch — createFetch (isomorphic *.localhost-aware fetch)
7
+ * - @0no-co/graphql.web — parseType, print
8
+ * - @constructive-io/graphql-types — GraphQLAdapter, GraphQLError, QueryResult
9
+ * - @constructive-io/fetch — createFetch (isomorphic *.localhost-aware fetch)
10
10
  *
11
11
  * gql-ast is intentionally NOT re-exported here because the templates
12
12
  * use `import * as t from 'gql-ast'` — mixing it into this namespace
@@ -19,10 +19,8 @@
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.createFetch = exports.parseType = void 0;
22
- // From @0no-co/graphql.web — GraphQL parsing/printing
23
22
  var graphql_web_1 = require("@0no-co/graphql.web");
24
23
  Object.defineProperty(exports, "parseType", { enumerable: true, get: function () { return graphql_web_1.parseType; } });
25
24
  Object.defineProperty(exports, "print", { enumerable: true, get: function () { return graphql_web_1.print; } });
26
- // Isomorphic fetch with *.localhost DNS + Host header fix for Node.js
27
- var localhost_fetch_1 = require("./localhost-fetch");
28
- Object.defineProperty(exports, "createFetch", { enumerable: true, get: function () { return localhost_fetch_1.createFetch; } });
25
+ var fetch_1 = require("@constructive-io/fetch");
26
+ Object.defineProperty(exports, "createFetch", { enumerable: true, get: function () { return fetch_1.createFetch; } });
package/types/schema.d.ts CHANGED
@@ -85,6 +85,14 @@ export interface TableQueryNames {
85
85
  delete: string | null;
86
86
  /** Patch field name in update mutation input (e.g., "userPatch" for UpdateUserInput) */
87
87
  patchFieldName?: string;
88
+ /** Bulk insert mutation name (e.g., "bulkCreateUsers") */
89
+ bulkInsert?: string | null;
90
+ /** Bulk upsert mutation name (e.g., "bulkUpsertUsers") */
91
+ bulkUpsert?: string | null;
92
+ /** Bulk update mutation name (e.g., "bulkUpdateUsers") */
93
+ bulkUpdate?: string | null;
94
+ /** Bulk delete mutation name (e.g., "bulkDeleteUsers") */
95
+ bulkDelete?: string | null;
88
96
  }
89
97
  /**
90
98
  * Table constraints
@@ -1,108 +0,0 @@
1
- /**
2
- * Isomorphic fetch that resolves *.localhost subdomains and preserves
3
- * Host headers across Node.js and browsers.
4
- *
5
- * Node.js has two issues with *.localhost subdomains:
6
- * 1. DNS — fetch('http://auth.localhost:3000/') throws ENOTFOUND
7
- * because undici doesn't resolve *.localhost to loopback.
8
- * 2. Host header — Node's fetch treats Host as forbidden and silently
9
- * drops it, breaking server-side subdomain routing.
10
- *
11
- * In browsers *.localhost resolves natively, so createFetch() returns
12
- * globalThis.fetch as-is.
13
- */
14
- export function isLocalhostSubdomain(hostname) {
15
- return hostname.endsWith('.localhost') && hostname !== 'localhost';
16
- }
17
- function buildNodeFetch(http, https) {
18
- return (input, init) => {
19
- const url = new URL(typeof input === 'string'
20
- ? input
21
- : input instanceof URL
22
- ? input.href
23
- : input.url);
24
- if (!isLocalhostSubdomain(url.hostname)) {
25
- return globalThis.fetch(input, init);
26
- }
27
- const originalHost = url.host;
28
- url.hostname = 'localhost';
29
- return new Promise((resolve, reject) => {
30
- const headers = {
31
- Host: originalHost,
32
- };
33
- if (init?.headers) {
34
- const entries = init.headers instanceof Headers
35
- ? Array.from(init.headers.entries())
36
- : Array.isArray(init.headers)
37
- ? init.headers
38
- : Object.entries(init.headers);
39
- for (const [key, value] of entries) {
40
- headers[key] = value;
41
- }
42
- }
43
- const protocol = url.protocol === 'https:' ? https : http;
44
- const req = protocol.request(url, {
45
- method: init?.method ?? 'GET',
46
- headers,
47
- }, (res) => {
48
- const chunks = [];
49
- res.on('data', (chunk) => chunks.push(chunk));
50
- res.on('end', () => {
51
- const body = Buffer.concat(chunks);
52
- resolve(new Response(body, {
53
- status: res.statusCode ?? 0,
54
- statusText: res.statusMessage ?? '',
55
- headers: res.headers,
56
- }));
57
- });
58
- });
59
- req.on('error', reject);
60
- if (init?.signal) {
61
- const onAbort = () => {
62
- req.destroy(new Error('The operation was aborted'));
63
- };
64
- init.signal.addEventListener('abort', onAbort, { once: true });
65
- req.on('close', () => {
66
- init.signal.removeEventListener('abort', onAbort);
67
- });
68
- }
69
- if (init?.body != null) {
70
- req.write(typeof init.body === 'string' || init.body instanceof Uint8Array
71
- ? init.body
72
- : String(init.body));
73
- }
74
- req.end();
75
- });
76
- };
77
- }
78
- let _fetch;
79
- /**
80
- * Create an isomorphic fetch function.
81
- *
82
- * - In browsers (and Deno/Bun/edge): returns globalThis.fetch as-is.
83
- * - In Node.js: returns a wrapper that uses node:http/node:https for
84
- * *.localhost URLs (fixing DNS + Host header) and delegates everything
85
- * else to globalThis.fetch.
86
- *
87
- * The result is cached — calling createFetch() multiple times returns
88
- * the same function instance.
89
- */
90
- export function createFetch() {
91
- if (_fetch)
92
- return _fetch;
93
- if (typeof process !== 'undefined' && process.versions?.node) {
94
- try {
95
- // eslint-disable-next-line @typescript-eslint/no-require-imports
96
- const http = require('node:http');
97
- // eslint-disable-next-line @typescript-eslint/no-require-imports
98
- const https = require('node:https');
99
- _fetch = buildNodeFetch(http, https);
100
- return _fetch;
101
- }
102
- catch {
103
- // node:http unavailable — fall through
104
- }
105
- }
106
- _fetch = globalThis.fetch;
107
- return _fetch;
108
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Isomorphic fetch that resolves *.localhost subdomains and preserves
3
- * Host headers across Node.js and browsers.
4
- *
5
- * Node.js has two issues with *.localhost subdomains:
6
- * 1. DNS — fetch('http://auth.localhost:3000/') throws ENOTFOUND
7
- * because undici doesn't resolve *.localhost to loopback.
8
- * 2. Host header — Node's fetch treats Host as forbidden and silently
9
- * drops it, breaking server-side subdomain routing.
10
- *
11
- * In browsers *.localhost resolves natively, so createFetch() returns
12
- * globalThis.fetch as-is.
13
- */
14
- export type FetchFunction = typeof globalThis.fetch;
15
- export declare function isLocalhostSubdomain(hostname: string): boolean;
16
- /**
17
- * Create an isomorphic fetch function.
18
- *
19
- * - In browsers (and Deno/Bun/edge): returns globalThis.fetch as-is.
20
- * - In Node.js: returns a wrapper that uses node:http/node:https for
21
- * *.localhost URLs (fixing DNS + Host header) and delegates everything
22
- * else to globalThis.fetch.
23
- *
24
- * The result is cached — calling createFetch() multiple times returns
25
- * the same function instance.
26
- */
27
- export declare function createFetch(): FetchFunction;
@@ -1,112 +0,0 @@
1
- "use strict";
2
- /**
3
- * Isomorphic fetch that resolves *.localhost subdomains and preserves
4
- * Host headers across Node.js and browsers.
5
- *
6
- * Node.js has two issues with *.localhost subdomains:
7
- * 1. DNS — fetch('http://auth.localhost:3000/') throws ENOTFOUND
8
- * because undici doesn't resolve *.localhost to loopback.
9
- * 2. Host header — Node's fetch treats Host as forbidden and silently
10
- * drops it, breaking server-side subdomain routing.
11
- *
12
- * In browsers *.localhost resolves natively, so createFetch() returns
13
- * globalThis.fetch as-is.
14
- */
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.isLocalhostSubdomain = isLocalhostSubdomain;
17
- exports.createFetch = createFetch;
18
- function isLocalhostSubdomain(hostname) {
19
- return hostname.endsWith('.localhost') && hostname !== 'localhost';
20
- }
21
- function buildNodeFetch(http, https) {
22
- return (input, init) => {
23
- const url = new URL(typeof input === 'string'
24
- ? input
25
- : input instanceof URL
26
- ? input.href
27
- : input.url);
28
- if (!isLocalhostSubdomain(url.hostname)) {
29
- return globalThis.fetch(input, init);
30
- }
31
- const originalHost = url.host;
32
- url.hostname = 'localhost';
33
- return new Promise((resolve, reject) => {
34
- const headers = {
35
- Host: originalHost,
36
- };
37
- if (init?.headers) {
38
- const entries = init.headers instanceof Headers
39
- ? Array.from(init.headers.entries())
40
- : Array.isArray(init.headers)
41
- ? init.headers
42
- : Object.entries(init.headers);
43
- for (const [key, value] of entries) {
44
- headers[key] = value;
45
- }
46
- }
47
- const protocol = url.protocol === 'https:' ? https : http;
48
- const req = protocol.request(url, {
49
- method: init?.method ?? 'GET',
50
- headers,
51
- }, (res) => {
52
- const chunks = [];
53
- res.on('data', (chunk) => chunks.push(chunk));
54
- res.on('end', () => {
55
- const body = Buffer.concat(chunks);
56
- resolve(new Response(body, {
57
- status: res.statusCode ?? 0,
58
- statusText: res.statusMessage ?? '',
59
- headers: res.headers,
60
- }));
61
- });
62
- });
63
- req.on('error', reject);
64
- if (init?.signal) {
65
- const onAbort = () => {
66
- req.destroy(new Error('The operation was aborted'));
67
- };
68
- init.signal.addEventListener('abort', onAbort, { once: true });
69
- req.on('close', () => {
70
- init.signal.removeEventListener('abort', onAbort);
71
- });
72
- }
73
- if (init?.body != null) {
74
- req.write(typeof init.body === 'string' || init.body instanceof Uint8Array
75
- ? init.body
76
- : String(init.body));
77
- }
78
- req.end();
79
- });
80
- };
81
- }
82
- let _fetch;
83
- /**
84
- * Create an isomorphic fetch function.
85
- *
86
- * - In browsers (and Deno/Bun/edge): returns globalThis.fetch as-is.
87
- * - In Node.js: returns a wrapper that uses node:http/node:https for
88
- * *.localhost URLs (fixing DNS + Host header) and delegates everything
89
- * else to globalThis.fetch.
90
- *
91
- * The result is cached — calling createFetch() multiple times returns
92
- * the same function instance.
93
- */
94
- function createFetch() {
95
- if (_fetch)
96
- return _fetch;
97
- if (typeof process !== 'undefined' && process.versions?.node) {
98
- try {
99
- // eslint-disable-next-line @typescript-eslint/no-require-imports
100
- const http = require('node:http');
101
- // eslint-disable-next-line @typescript-eslint/no-require-imports
102
- const https = require('node:https');
103
- _fetch = buildNodeFetch(http, https);
104
- return _fetch;
105
- }
106
- catch {
107
- // node:http unavailable — fall through
108
- }
109
- }
110
- _fetch = globalThis.fetch;
111
- return _fetch;
112
- }