@constructive-io/graphql-server 5.13.2 → 5.13.5

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.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * GraphQL-over-HTTP error responses.
3
+ *
4
+ * Middleware that short-circuits a GraphQL request (auth, captcha, ...) must
5
+ * still answer with a valid GraphQL response body. Building that body by hand
6
+ * is how a code ends up shipped without a top-level `message`: clients render
7
+ * `errors[].message`, so an error carrying only `extensions.code` surfaces as
8
+ * an empty string. Route every such response through here so the message and
9
+ * extensions both come from the canonical error registry.
10
+ *
11
+ * @module errors/graphql-response
12
+ */
13
+ import type { ConstructiveError } from '@constructive-io/errors';
14
+ import type { Response } from 'express';
15
+ /**
16
+ * Send a {@link ConstructiveError} as a GraphQL error response.
17
+ *
18
+ * Uses HTTP 200 per the GraphQL-over-HTTP convention: transport succeeded, the
19
+ * operation did not. The error's own `http` hint travels in `extensions`.
20
+ */
21
+ export declare function respondWithGraphQLError(res: Response, error: ConstructiveError): void;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /**
3
+ * GraphQL-over-HTTP error responses.
4
+ *
5
+ * Middleware that short-circuits a GraphQL request (auth, captcha, ...) must
6
+ * still answer with a valid GraphQL response body. Building that body by hand
7
+ * is how a code ends up shipped without a top-level `message`: clients render
8
+ * `errors[].message`, so an error carrying only `extensions.code` surfaces as
9
+ * an empty string. Route every such response through here so the message and
10
+ * extensions both come from the canonical error registry.
11
+ *
12
+ * @module errors/graphql-response
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.respondWithGraphQLError = respondWithGraphQLError;
16
+ /**
17
+ * Send a {@link ConstructiveError} as a GraphQL error response.
18
+ *
19
+ * Uses HTTP 200 per the GraphQL-over-HTTP convention: transport succeeded, the
20
+ * operation did not. The error's own `http` hint travels in `extensions`.
21
+ */
22
+ function respondWithGraphQLError(res, error) {
23
+ res.status(200).json({
24
+ errors: [{ message: error.message, extensions: error.toExtensions() }],
25
+ });
26
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * GraphQL-over-HTTP error responses.
3
+ *
4
+ * Middleware that short-circuits a GraphQL request (auth, captcha, ...) must
5
+ * still answer with a valid GraphQL response body. Building that body by hand
6
+ * is how a code ends up shipped without a top-level `message`: clients render
7
+ * `errors[].message`, so an error carrying only `extensions.code` surfaces as
8
+ * an empty string. Route every such response through here so the message and
9
+ * extensions both come from the canonical error registry.
10
+ *
11
+ * @module errors/graphql-response
12
+ */
13
+ /**
14
+ * Send a {@link ConstructiveError} as a GraphQL error response.
15
+ *
16
+ * Uses HTTP 200 per the GraphQL-over-HTTP convention: transport succeeded, the
17
+ * operation did not. The error's own `http` hint travels in `extensions`.
18
+ */
19
+ export function respondWithGraphQLError(res, error) {
20
+ res.status(200).json({
21
+ errors: [{ message: error.message, extensions: error.toExtensions() }],
22
+ });
23
+ }
@@ -1,7 +1,9 @@
1
+ import { errors } from '@constructive-io/errors';
1
2
  import { getNodeEnv } from '@pgpmjs/env';
2
3
  import { Logger } from '@pgpmjs/logger';
3
4
  import { getPgPool } from 'pg-cache';
4
5
  import pgQueryContext from 'pg-query-context';
6
+ import { respondWithGraphQLError } from '../errors/graphql-response';
5
7
  import './types'; // for Request type
6
8
  const log = new Logger('auth');
7
9
  const isDev = () => getNodeEnv() === 'development';
@@ -80,9 +82,7 @@ export const createAuthenticateMiddleware = (opts) => {
80
82
  log.info(`[auth] Query result: rowCount=${result?.rowCount}`);
81
83
  if (result?.rowCount === 0) {
82
84
  log.info('[auth] No rows returned, returning UNAUTHENTICATED');
83
- res.status(200).json({
84
- errors: [{ extensions: { code: 'UNAUTHENTICATED' } }],
85
- });
85
+ respondWithGraphQLError(res, errors.UNAUTHENTICATED());
86
86
  return;
87
87
  }
88
88
  token = result.rows[0];
@@ -90,16 +90,9 @@ export const createAuthenticateMiddleware = (opts) => {
90
90
  }
91
91
  catch (e) {
92
92
  log.error('[auth] Auth error:', e.message);
93
- res.status(200).json({
94
- errors: [
95
- {
96
- extensions: {
97
- code: 'BAD_TOKEN_DEFINITION',
98
- message: e.message,
99
- },
100
- },
101
- ],
102
- });
93
+ respondWithGraphQLError(res, errors.INTERNAL_FAILURE({
94
+ details: isDev() ? e.message : 'authentication failed',
95
+ }));
103
96
  return;
104
97
  }
105
98
  }
@@ -1,4 +1,6 @@
1
+ import { errors } from '@constructive-io/errors';
1
2
  import { Logger } from '@pgpmjs/logger';
3
+ import { respondWithGraphQLError } from '../errors/graphql-response';
2
4
  import './types'; // for Request type
3
5
  const log = new Logger('captcha');
4
6
  /** Google reCAPTCHA verification endpoint */
@@ -87,22 +89,12 @@ export const createCaptchaMiddleware = () => {
87
89
  }
88
90
  const captchaToken = req.get(CAPTCHA_HEADER);
89
91
  if (!captchaToken) {
90
- res.status(200).json({
91
- errors: [{
92
- message: 'CAPTCHA verification required',
93
- extensions: { code: 'CAPTCHA_REQUIRED' },
94
- }],
95
- });
92
+ respondWithGraphQLError(res, errors.CAPTCHA_REQUIRED());
96
93
  return;
97
94
  }
98
95
  const valid = await verifyToken(captchaToken, secretKey);
99
96
  if (!valid) {
100
- res.status(200).json({
101
- errors: [{
102
- message: 'CAPTCHA verification failed',
103
- extensions: { code: 'CAPTCHA_FAILED' },
104
- }],
105
- });
97
+ respondWithGraphQLError(res, errors.CAPTCHA_FAILED());
106
98
  return;
107
99
  }
108
100
  log.info(`[captcha] Verified for operation=${opName}`);
@@ -4,10 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createAuthenticateMiddleware = void 0;
7
+ const errors_1 = require("@constructive-io/errors");
7
8
  const env_1 = require("@pgpmjs/env");
8
9
  const logger_1 = require("@pgpmjs/logger");
9
10
  const pg_cache_1 = require("pg-cache");
10
11
  const pg_query_context_1 = __importDefault(require("pg-query-context"));
12
+ const graphql_response_1 = require("../errors/graphql-response");
11
13
  require("./types"); // for Request type
12
14
  const log = new logger_1.Logger('auth');
13
15
  const isDev = () => (0, env_1.getNodeEnv)() === 'development';
@@ -86,9 +88,7 @@ const createAuthenticateMiddleware = (opts) => {
86
88
  log.info(`[auth] Query result: rowCount=${result?.rowCount}`);
87
89
  if (result?.rowCount === 0) {
88
90
  log.info('[auth] No rows returned, returning UNAUTHENTICATED');
89
- res.status(200).json({
90
- errors: [{ extensions: { code: 'UNAUTHENTICATED' } }],
91
- });
91
+ (0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.UNAUTHENTICATED());
92
92
  return;
93
93
  }
94
94
  token = result.rows[0];
@@ -96,16 +96,9 @@ const createAuthenticateMiddleware = (opts) => {
96
96
  }
97
97
  catch (e) {
98
98
  log.error('[auth] Auth error:', e.message);
99
- res.status(200).json({
100
- errors: [
101
- {
102
- extensions: {
103
- code: 'BAD_TOKEN_DEFINITION',
104
- message: e.message,
105
- },
106
- },
107
- ],
108
- });
99
+ (0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.INTERNAL_FAILURE({
100
+ details: isDev() ? e.message : 'authentication failed',
101
+ }));
109
102
  return;
110
103
  }
111
104
  }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createCaptchaMiddleware = void 0;
4
+ const errors_1 = require("@constructive-io/errors");
4
5
  const logger_1 = require("@pgpmjs/logger");
6
+ const graphql_response_1 = require("../errors/graphql-response");
5
7
  require("./types"); // for Request type
6
8
  const log = new logger_1.Logger('captcha');
7
9
  /** Google reCAPTCHA verification endpoint */
@@ -90,22 +92,12 @@ const createCaptchaMiddleware = () => {
90
92
  }
91
93
  const captchaToken = req.get(CAPTCHA_HEADER);
92
94
  if (!captchaToken) {
93
- res.status(200).json({
94
- errors: [{
95
- message: 'CAPTCHA verification required',
96
- extensions: { code: 'CAPTCHA_REQUIRED' },
97
- }],
98
- });
95
+ (0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.CAPTCHA_REQUIRED());
99
96
  return;
100
97
  }
101
98
  const valid = await verifyToken(captchaToken, secretKey);
102
99
  if (!valid) {
103
- res.status(200).json({
104
- errors: [{
105
- message: 'CAPTCHA verification failed',
106
- extensions: { code: 'CAPTCHA_FAILED' },
107
- }],
108
- });
100
+ (0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.CAPTCHA_FAILED());
109
101
  return;
110
102
  }
111
103
  log.info(`[captcha] Verified for operation=${opName}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-server",
3
- "version": "5.13.2",
3
+ "version": "5.13.5",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Constructive GraphQL Server",
6
6
  "main": "index.js",
@@ -42,19 +42,19 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "@constructive-io/csrf": "^0.25.1",
45
- "@constructive-io/errors": "^0.7.2",
46
- "@constructive-io/express-context": "^0.22.4",
47
- "@constructive-io/graphql-env": "^3.27.4",
48
- "@constructive-io/graphql-types": "^3.26.4",
45
+ "@constructive-io/errors": "^0.7.3",
46
+ "@constructive-io/express-context": "^0.22.5",
47
+ "@constructive-io/graphql-env": "^3.27.5",
48
+ "@constructive-io/graphql-types": "^3.26.5",
49
49
  "@constructive-io/query-builder": "^3.8.1",
50
50
  "@constructive-io/s3-utils": "^2.28.1",
51
51
  "@constructive-io/url-domains": "^2.27.1",
52
52
  "@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
53
- "@pgpmjs/env": "^2.39.2",
53
+ "@pgpmjs/env": "^2.39.3",
54
54
  "@pgpmjs/logger": "^2.22.1",
55
- "@pgpmjs/server-utils": "^3.23.4",
56
- "@pgpmjs/types": "^2.48.1",
57
- "agentic-server": "0.20.4",
55
+ "@pgpmjs/server-utils": "^3.23.5",
56
+ "@pgpmjs/types": "^2.48.2",
57
+ "agentic-server": "0.20.5",
58
58
  "cors": "^2.8.6",
59
59
  "deepmerge": "^4.3.1",
60
60
  "express": "^5.2.1",
@@ -63,16 +63,16 @@
63
63
  "grafserv": "1.0.0",
64
64
  "graphile-build": "5.0.2",
65
65
  "graphile-build-pg": "5.0.2",
66
- "graphile-cache": "^4.8.4",
66
+ "graphile-cache": "^4.8.5",
67
67
  "graphile-config": "1.0.1",
68
- "graphile-function-bindings": "^1.9.6",
69
- "graphile-settings": "^6.10.1",
68
+ "graphile-function-bindings": "^1.9.9",
69
+ "graphile-settings": "^6.10.4",
70
70
  "graphile-utils": "5.0.1",
71
71
  "graphql": "16.13.0",
72
72
  "graphql-upload": "^13.0.0",
73
73
  "lru-cache": "^11.2.7",
74
74
  "pg": "^8.21.0",
75
- "pg-cache": "^3.23.4",
75
+ "pg-cache": "^3.23.5",
76
76
  "pg-env": "^1.26.1",
77
77
  "pg-query-context": "^2.27.1",
78
78
  "pg-sql2": "5.0.1",
@@ -88,10 +88,10 @@
88
88
  "@types/pg": "^8.20.0",
89
89
  "@types/request-ip": "^0.0.41",
90
90
  "cookie-parser": "^1.4.7",
91
- "graphile-test": "5.9.6",
91
+ "graphile-test": "5.9.9",
92
92
  "makage": "^0.3.0",
93
93
  "nodemon": "^3.1.14",
94
94
  "ts-node": "^10.9.2"
95
95
  },
96
- "gitHead": "a430897efa1488ec24fc636f0d5023207bba7ba7"
96
+ "gitHead": "947334d361e9f4c48d451f1c7312b2d705ea154e"
97
97
  }