@constructive-io/graphql-server 5.4.1 → 5.7.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.
@@ -16,7 +16,10 @@ const defaultRegistry = createDefaultRegistry();
16
16
  // SQL Queries (API resolution only — module queries now live in loaders)
17
17
  // =============================================================================
18
18
  // Private-header X-Api-Name lookup against the scoped routing plane.
19
- // `is_published` is the routing-plane analog of the legacy `is_public` column.
19
+ // X-Api-Name is a trusted internal selector (only honored when the server
20
+ // runs with isPublic=false): it addresses a database's API surface by name.
21
+ // `is_published` governs cross-scope route visibility, not internal
22
+ // addressability, so it does not filter this lookup.
20
23
  const scopedApiNameLookupSql = (routingSchema) => `
21
24
  SELECT
22
25
  a.id as api_id,
@@ -31,7 +34,6 @@ const scopedApiNameLookupSql = (routingSchema) => `
31
34
  LEFT JOIN metaschema_public.schema s ON aps.schema_id = s.id
32
35
  WHERE a.database_id = $1
33
36
  AND a.name = $2
34
- AND a.is_published = $3
35
37
  GROUP BY a.id, a.database_id, a.dbname, a.role_name, a.anon_role, a.is_published
36
38
  LIMIT 1
37
39
  `;
@@ -163,13 +165,13 @@ const validateSchemata = async (pool, schemas) => {
163
165
  const result = await pool.query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name = ANY($1::text[])`, [schemas]);
164
166
  return result.rows.map((row) => row.schema_name);
165
167
  };
166
- const queryByApiName = async (pool, opts, databaseId, name, isPublic) => {
168
+ const queryByApiName = async (pool, opts, databaseId, name) => {
167
169
  const routingSchema = getRoutingSchema(opts);
168
170
  if (!isValidSchemaName(routingSchema)) {
169
171
  log.warn(`[api-name-lookup] invalid routing schema name: ${routingSchema}`);
170
172
  return null;
171
173
  }
172
- const result = await pool.query(scopedApiNameLookupSql(routingSchema), [databaseId, name, isPublic]);
174
+ const result = await pool.query(scopedApiNameLookupSql(routingSchema), [databaseId, name]);
173
175
  return result.rows[0] ?? null;
174
176
  };
175
177
  // =============================================================================
@@ -196,8 +198,7 @@ const resolveApiNameHeader = async (ctx) => {
196
198
  const { opts, pool, headers } = ctx;
197
199
  if (!headers.databaseId)
198
200
  return null;
199
- const isPublic = opts.api?.isPublic ?? false;
200
- const row = await queryByApiName(pool, opts, headers.databaseId, headers.apiName, isPublic);
201
+ const row = await queryByApiName(pool, opts, headers.databaseId, headers.apiName);
201
202
  if (!row) {
202
203
  log.debug(`[api-name-lookup] No API found for databaseId=${headers.databaseId} name=${headers.apiName}`);
203
204
  return null;
@@ -48,12 +48,14 @@ export const routeToApiStructure = (route, opts) => {
48
48
  return null;
49
49
  }
50
50
  const config = (route.resolved_config ?? {});
51
- if (!config.dbname || !config.schemas?.length) {
52
- log.debug('[resolve-route] api target missing dbname/schemas in resolved_config; no match');
51
+ if (!config.schemas?.length) {
52
+ log.debug('[resolve-route] api target missing schemas in resolved_config; no match');
53
53
  return null;
54
54
  }
55
55
  return {
56
56
  apiId: config.api_id ?? route.target_source_id ?? undefined,
57
+ // Scoped APIs leave dbname NULL when their schemas live in the serving
58
+ // database; fall back to the server's own database in that case.
57
59
  dbname: config.dbname || opts.pg?.database || '',
58
60
  anonRole: config.anon_role || 'anon',
59
61
  roleName: config.role_name || 'authenticated',
package/middleware/api.js CHANGED
@@ -22,7 +22,10 @@ const defaultRegistry = (0, express_context_1.createDefaultRegistry)();
22
22
  // SQL Queries (API resolution only — module queries now live in loaders)
23
23
  // =============================================================================
24
24
  // Private-header X-Api-Name lookup against the scoped routing plane.
25
- // `is_published` is the routing-plane analog of the legacy `is_public` column.
25
+ // X-Api-Name is a trusted internal selector (only honored when the server
26
+ // runs with isPublic=false): it addresses a database's API surface by name.
27
+ // `is_published` governs cross-scope route visibility, not internal
28
+ // addressability, so it does not filter this lookup.
26
29
  const scopedApiNameLookupSql = (routingSchema) => `
27
30
  SELECT
28
31
  a.id as api_id,
@@ -37,7 +40,6 @@ const scopedApiNameLookupSql = (routingSchema) => `
37
40
  LEFT JOIN metaschema_public.schema s ON aps.schema_id = s.id
38
41
  WHERE a.database_id = $1
39
42
  AND a.name = $2
40
- AND a.is_published = $3
41
43
  GROUP BY a.id, a.database_id, a.dbname, a.role_name, a.anon_role, a.is_published
42
44
  LIMIT 1
43
45
  `;
@@ -171,13 +173,13 @@ const validateSchemata = async (pool, schemas) => {
171
173
  const result = await pool.query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name = ANY($1::text[])`, [schemas]);
172
174
  return result.rows.map((row) => row.schema_name);
173
175
  };
174
- const queryByApiName = async (pool, opts, databaseId, name, isPublic) => {
176
+ const queryByApiName = async (pool, opts, databaseId, name) => {
175
177
  const routingSchema = (0, routing_1.getRoutingSchema)(opts);
176
178
  if (!(0, routing_1.isValidSchemaName)(routingSchema)) {
177
179
  log.warn(`[api-name-lookup] invalid routing schema name: ${routingSchema}`);
178
180
  return null;
179
181
  }
180
- const result = await pool.query(scopedApiNameLookupSql(routingSchema), [databaseId, name, isPublic]);
182
+ const result = await pool.query(scopedApiNameLookupSql(routingSchema), [databaseId, name]);
181
183
  return result.rows[0] ?? null;
182
184
  };
183
185
  // =============================================================================
@@ -204,8 +206,7 @@ const resolveApiNameHeader = async (ctx) => {
204
206
  const { opts, pool, headers } = ctx;
205
207
  if (!headers.databaseId)
206
208
  return null;
207
- const isPublic = opts.api?.isPublic ?? false;
208
- const row = await queryByApiName(pool, opts, headers.databaseId, headers.apiName, isPublic);
209
+ const row = await queryByApiName(pool, opts, headers.databaseId, headers.apiName);
209
210
  if (!row) {
210
211
  log.debug(`[api-name-lookup] No API found for databaseId=${headers.databaseId} name=${headers.apiName}`);
211
212
  return null;
@@ -54,12 +54,14 @@ const routeToApiStructure = (route, opts) => {
54
54
  return null;
55
55
  }
56
56
  const config = (route.resolved_config ?? {});
57
- if (!config.dbname || !config.schemas?.length) {
58
- log.debug('[resolve-route] api target missing dbname/schemas in resolved_config; no match');
57
+ if (!config.schemas?.length) {
58
+ log.debug('[resolve-route] api target missing schemas in resolved_config; no match');
59
59
  return null;
60
60
  }
61
61
  return {
62
62
  apiId: config.api_id ?? route.target_source_id ?? undefined,
63
+ // Scoped APIs leave dbname NULL when their schemas live in the serving
64
+ // database; fall back to the server's own database in that case.
63
65
  dbname: config.dbname || opts.pg?.database || '',
64
66
  anonRole: config.anon_role || 'anon',
65
67
  roleName: config.role_name || 'authenticated',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-server",
3
- "version": "5.4.1",
3
+ "version": "5.7.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Constructive GraphQL Server",
6
6
  "main": "index.js",
@@ -41,39 +41,39 @@
41
41
  "backend"
42
42
  ],
43
43
  "dependencies": {
44
- "@constructive-io/csrf": "^0.18.2",
45
- "@constructive-io/express-context": "^0.15.0",
46
- "@constructive-io/graphql-env": "^3.20.0",
47
- "@constructive-io/graphql-types": "^3.19.0",
48
- "@constructive-io/query-builder": "^3.1.2",
49
- "@constructive-io/s3-utils": "^2.21.2",
50
- "@constructive-io/url-domains": "^2.20.2",
44
+ "@constructive-io/csrf": "^0.21.0",
45
+ "@constructive-io/express-context": "^0.18.0",
46
+ "@constructive-io/graphql-env": "^3.23.0",
47
+ "@constructive-io/graphql-types": "^3.22.0",
48
+ "@constructive-io/query-builder": "^3.4.0",
49
+ "@constructive-io/s3-utils": "^2.24.0",
50
+ "@constructive-io/url-domains": "^2.23.0",
51
51
  "@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
52
- "@pgpmjs/env": "^2.30.2",
53
- "@pgpmjs/logger": "^2.15.2",
54
- "@pgpmjs/server-utils": "^3.16.2",
55
- "@pgpmjs/types": "^2.37.2",
56
- "agentic-server": "0.13.0",
52
+ "@pgpmjs/env": "^2.33.0",
53
+ "@pgpmjs/logger": "^2.18.0",
54
+ "@pgpmjs/server-utils": "^3.19.0",
55
+ "@pgpmjs/types": "^2.40.0",
56
+ "agentic-server": "0.16.0",
57
57
  "cors": "^2.8.6",
58
58
  "deepmerge": "^4.3.1",
59
59
  "express": "^5.2.1",
60
- "gql-ast": "^3.14.2",
60
+ "gql-ast": "^3.17.0",
61
61
  "grafast": "1.0.2",
62
62
  "grafserv": "1.0.0",
63
63
  "graphile-build": "5.0.2",
64
64
  "graphile-build-pg": "5.0.2",
65
- "graphile-cache": "^4.1.2",
65
+ "graphile-cache": "^4.4.0",
66
66
  "graphile-config": "1.0.1",
67
- "graphile-function-bindings": "^1.2.1",
68
- "graphile-settings": "^6.2.1",
67
+ "graphile-function-bindings": "^1.5.0",
68
+ "graphile-settings": "^6.5.0",
69
69
  "graphile-utils": "5.0.1",
70
70
  "graphql": "16.13.0",
71
71
  "graphql-upload": "^13.0.0",
72
72
  "lru-cache": "^11.2.7",
73
73
  "pg": "^8.21.0",
74
- "pg-cache": "^3.16.2",
75
- "pg-env": "^1.19.2",
76
- "pg-query-context": "^2.20.2",
74
+ "pg-cache": "^3.19.0",
75
+ "pg-env": "^1.22.0",
76
+ "pg-query-context": "^2.23.0",
77
77
  "pg-sql2": "5.0.1",
78
78
  "postgraphile": "5.0.3",
79
79
  "request-ip": "^3.3.0"
@@ -87,10 +87,10 @@
87
87
  "@types/pg": "^8.20.0",
88
88
  "@types/request-ip": "^0.0.41",
89
89
  "cookie-parser": "^1.4.7",
90
- "graphile-test": "5.2.1",
90
+ "graphile-test": "5.5.0",
91
91
  "makage": "^0.3.0",
92
92
  "nodemon": "^3.1.14",
93
93
  "ts-node": "^10.9.2"
94
94
  },
95
- "gitHead": "cc1b2af2c73c23b99a221e6bd8a8ba76c7ce79e6"
95
+ "gitHead": "73f55ad24d9a92e7ef1e268526b31bfb7bb12b28"
96
96
  }