@common-stack/server-stack 9.0.2-alpha.1 → 9.0.2-alpha.12

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,90 @@
1
+ import {createRequire}from'node:module';/**
2
+ * Express Engine Adapter
3
+ *
4
+ * Provides a unified interface for both Express 4 and ultimate-express (uWebSockets.js).
5
+ * The engine is selected at startup via the `EXPRESS_ENGINE` environment variable:
6
+ *
7
+ * - `EXPRESS_ENGINE=ultimate` (default) — uses ultimate-express + ultimate-ws for best performance
8
+ * - `EXPRESS_ENGINE=express` — uses classic Express 4 + ws for maximum compatibility
9
+ *
10
+ * All server modules import from this adapter instead of directly from 'express' or 'ultimate-express',
11
+ * so switching engines requires zero code changes — just set the env var.
12
+ *
13
+ * IMPORTANT: ultimate-express does NOT support http.createServer(). The server is started via app.listen().
14
+ * Classic express works with both http.createServer() and app.listen().
15
+ * This adapter normalizes both to use app.listen() for consistency.
16
+ */
17
+ // ─── Engine Detection ────────────────────────────────────────────────────────
18
+ // Read engine choice early (before envalid config is available).
19
+ // This module is loaded at import time, so we read directly from process.env.
20
+ const requestedEngine = (process.env.EXPRESS_ENGINE || 'ultimate').toLowerCase();
21
+ const _require = createRequire(import.meta.url);
22
+ /**
23
+ * Safely attempt to load the ultimate-* native engine.
24
+ * These packages are declared as `optionalDependencies` and require a recent glibc;
25
+ * in stripped-down container images they may be missing or fail to load — in that
26
+ * case we transparently fall back to classic express + ws.
27
+ */
28
+ function tryLoadUltimate() {
29
+ try {
30
+ const ultimateExpress = _require('ultimate-express');
31
+ const ultimateWs = _require('ultimate-ws');
32
+ return { express: ultimateExpress, WebSocketServer: ultimateWs.WebSocketServer };
33
+ }
34
+ catch (err) {
35
+ if (requestedEngine === 'ultimate') {
36
+ // eslint-disable-next-line no-console
37
+ console.warn('[express-adapter] ultimate-express/ultimate-ws unavailable, falling back to express + ws.', err?.message);
38
+ }
39
+ return null;
40
+ }
41
+ }
42
+ const ultimate = requestedEngine === 'ultimate' ? tryLoadUltimate() : null;
43
+ /** Whether the ultimate-express (uWebSockets.js) engine is active */
44
+ const isUltimateEngine = ultimate !== null;
45
+ /**
46
+ * The express factory function — creates an Express application.
47
+ * When engine='ultimate' (and the native module loaded), this is ultimate-express (uWS.js-backed).
48
+ * Otherwise this is classic Express 4.
49
+ */
50
+ const express = ultimate ? ultimate.express : _require('express');
51
+ /**
52
+ * WebSocketServer class.
53
+ * When engine='ultimate' (and the native module loaded), this is ultimate-ws (uWS.js-backed).
54
+ * Otherwise this is the standard `ws` package.
55
+ */
56
+ const WebSocketServer = ultimate ? ultimate.WebSocketServer : _require('ws').WebSocketServer;
57
+ /**
58
+ * Create an HTTP server for the given express app.
59
+ *
60
+ * - ultimate-express: returns null (uWS manages the server internally via app.listen())
61
+ * - classic express: returns http.createServer(app)
62
+ */
63
+ function createHttpServer(app) {
64
+ if (isUltimateEngine) {
65
+ // ultimate-express manages its own uWS server; no http.Server is created
66
+ return null;
67
+ }
68
+ const http = _require('http');
69
+ return http.createServer(app);
70
+ }
71
+ /**
72
+ * Body parser middleware.
73
+ *
74
+ * - ultimate-express: uses built-in express.json() / express.urlencoded() for best perf
75
+ * - classic express: uses body-parser package (more battle-tested options)
76
+ */
77
+ function jsonParser(options) {
78
+ if (isUltimateEngine) {
79
+ return express.json(options);
80
+ }
81
+ const bodyParser = _require('body-parser');
82
+ return bodyParser.json(options);
83
+ }
84
+ function urlencodedParser(options) {
85
+ if (isUltimateEngine) {
86
+ return express.urlencoded(options);
87
+ }
88
+ const bodyParser = _require('body-parser');
89
+ return bodyParser.urlencoded(options);
90
+ }export{WebSocketServer,createHttpServer,express as default,express,isUltimateEngine,jsonParser,urlencodedParser};//# sourceMappingURL=express-adapter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express-adapter.mjs","sources":["../src/express-adapter.ts"],"sourcesContent":[null],"names":[],"mappings":"wCAAA;;;;;;;;;;;;;;;AAeG;AAQH;AACA;AACA;AACA,MAAM,eAAe,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,WAAW,EAAE,CAAC;AAKjF,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEhD;;;;;AAKG;AACH,SAAS,eAAe,GAAA;AACpB,IAAA,IAAI;AACA,QAAA,MAAM,eAAe,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AACrD,QAAA,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC3C,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,CAAC,eAAe,EAAE,CAAC;KACpF;IAAC,OAAO,GAAG,EAAE;AACV,QAAA,IAAI,eAAe,KAAK,UAAU,EAAE;;YAEhC,OAAO,CAAC,IAAI,CACR,2FAA2F,EAC1F,GAAa,EAAE,OAAO,CAC1B,CAAC;SACL;AACD,QAAA,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AAED,MAAM,QAAQ,GAAG,eAAe,KAAK,UAAU,GAAG,eAAe,EAAE,GAAG,IAAI,CAAC;AAE3E;AACa,MAAA,gBAAgB,GAAY,QAAQ,KAAK,KAAK;AAE3D;;;;AAIG;AACU,MAAA,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,SAAS,EAAE;AAIzE;;;;AAIG;MACU,eAAe,GAAG,QAAQ,GAAG,QAAQ,CAAC,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,gBAAgB;AAKpG;;;;;AAKG;AACG,SAAU,gBAAgB,CAAC,GAAY,EAAA;IACzC,IAAI,gBAAgB,EAAE;;AAElB,QAAA,OAAO,IAAI,CAAC;KACf;AACD,IAAA,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,IAAA,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAuBD;;;;;AAKG;AACG,SAAU,UAAU,CAAC,OAAa,EAAA;IACpC,IAAI,gBAAgB,EAAE;AAClB,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;KAChC;AACD,IAAA,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC3C,IAAA,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAEK,SAAU,gBAAgB,CAAC,OAAa,EAAA;IAC1C,IAAI,gBAAgB,EAAE;AAClB,QAAA,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;KACtC;AACD,IAAA,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC3C,IAAA,OAAO,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;AAC1C"}
@@ -1,12 +1,47 @@
1
- 'use strict';require('isomorphic-fetch');const contextServicesMiddleware = (createContext, serviceContext) => (req, res, next) => {
2
- Promise.all([
3
- createContext(req, res),
4
- serviceContext(req, res),
5
- ])
6
- .then(([context, services]) => {
7
- req.context = context;
8
- req.services = services;
1
+ 'use strict';require('isomorphic-fetch');/**
2
+ * Phase-aware context middleware. Runs only the `createContextFunc` factories
3
+ * whose `phase` property matches the current phase (untagged factories default
4
+ * to `pre-beforeware`).
5
+ *
6
+ * - `pre-beforeware` (default): seeds `req.services` with the container-built
7
+ * service map and merges any safe context factories. Lenient: a thrown
8
+ * factory is logged and skipped so the request can still reach auth.
9
+ * - `pre-middleware`: runs after the consumer's beforewares (e.g. auth chain),
10
+ * so factories that depend on `req.user` belong here. Strict: errors are
11
+ * propagated via `next(err)`.
12
+ * - `post-middleware`: runs after the consumer's middlewares. Strict.
13
+ */
14
+ const phasedContextMiddleware = (feature, phase) => async (req, res, next) => {
15
+ const isPreBeforeware = phase === 'pre-beforeware';
16
+ try {
17
+ const { context, matched } = await feature.runContextFactoriesForPhase(req, res, phase);
18
+ if (isPreBeforeware) {
19
+ const services = feature.services || {};
20
+ req.context = { ...(req.context || {}), ...context };
21
+ req.services = { ...(req.services || {}), ...services, ...context };
22
+ }
23
+ else if (matched) {
24
+ req.context = { ...(req.context || {}), ...context };
25
+ req.services = { ...(req.services || {}), ...context };
26
+ }
9
27
  next();
10
- })
11
- .catch((err) => next());
12
- };exports.contextServicesMiddleware=contextServicesMiddleware;//# sourceMappingURL=services.cjs.map
28
+ }
29
+ catch (err) {
30
+ if (isPreBeforeware) {
31
+ // Lenient: some factories require auth (which has not run yet).
32
+ // Seed req.services with the static service map so downstream
33
+ // beforewares still have a usable services object.
34
+ // eslint-disable-next-line no-console
35
+ console.warn(`[phased-context] pre-beforeware factory failed (continuing):`, err?.message);
36
+ const services = feature.services || {};
37
+ if (!req.services)
38
+ req.services = { ...services };
39
+ else
40
+ req.services = { ...req.services, ...services };
41
+ next();
42
+ }
43
+ else {
44
+ next(err);
45
+ }
46
+ }
47
+ };exports.phasedContextMiddleware=phasedContextMiddleware;//# sourceMappingURL=services.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"services.cjs","sources":["../../src/middleware/services.ts"],"sourcesContent":[null],"names":[],"mappings":"yCAEa,MAAA,yBAAyB,GAAG,CAAC,aAAa,EAAE,cAAc,KAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;IAC5F,OAAO,CAAC,GAAG,CAAC;AACR,QAAA,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;AACvB,QAAA,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC;KAC3B,CAAC;SACG,IAAI,CAAC,CAAC,CAAE,OAAO,EAAE,QAAQ,CAAE,KAAI;AAC5B,QAAA,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;AACtB,QAAA,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAExB,QAAA,IAAI,EAAE,CAAC;AACX,KAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC;AAChC"}
1
+ {"version":3,"file":"services.cjs","sources":["../../src/middleware/services.ts"],"sourcesContent":[null],"names":[],"mappings":"yCAsBA;;;;;;;;;;;;AAYG;AACU,MAAA,uBAAuB,GAAG,CAAC,OAAgB,EAAE,KAAmB,KAAK,OAAO,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;AACvG,IAAA,MAAM,eAAe,GAAG,KAAK,KAAK,gBAAgB,CAAC;AACnD,IAAA,IAAI;AACA,QAAA,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,2BAA2B,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACxF,IAAI,eAAe,EAAE;AACjB,YAAA,MAAM,QAAQ,GAAI,OAAe,CAAC,QAAQ,IAAI,EAAE,CAAC;AACjD,YAAA,GAAG,CAAC,OAAO,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;AACrD,YAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;SACvE;aAAM,IAAI,OAAO,EAAE;AAChB,YAAA,GAAG,CAAC,OAAO,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;AACrD,YAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;SAC1D;AACD,QAAA,IAAI,EAAE,CAAC;KACV;IAAC,OAAO,GAAG,EAAE;QACV,IAAI,eAAe,EAAE;;;;;YAKjB,OAAO,CAAC,IAAI,CAAC,CAAA,4DAAA,CAA8D,EAAG,GAAa,EAAE,OAAO,CAAC,CAAC;AACtG,YAAA,MAAM,QAAQ,GAAI,OAAe,CAAC,QAAQ,IAAI,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,QAAQ;AAAE,gBAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;;AAC7C,gBAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC;AACrD,YAAA,IAAI,EAAE,CAAC;SACV;aAAM;YACH,IAAI,CAAC,GAAG,CAAC,CAAC;SACb;KACJ;AACL"}
@@ -1,12 +1,47 @@
1
- import'isomorphic-fetch';const contextServicesMiddleware = (createContext, serviceContext) => (req, res, next) => {
2
- Promise.all([
3
- createContext(req, res),
4
- serviceContext(req, res),
5
- ])
6
- .then(([context, services]) => {
7
- req.context = context;
8
- req.services = services;
1
+ import'isomorphic-fetch';/**
2
+ * Phase-aware context middleware. Runs only the `createContextFunc` factories
3
+ * whose `phase` property matches the current phase (untagged factories default
4
+ * to `pre-beforeware`).
5
+ *
6
+ * - `pre-beforeware` (default): seeds `req.services` with the container-built
7
+ * service map and merges any safe context factories. Lenient: a thrown
8
+ * factory is logged and skipped so the request can still reach auth.
9
+ * - `pre-middleware`: runs after the consumer's beforewares (e.g. auth chain),
10
+ * so factories that depend on `req.user` belong here. Strict: errors are
11
+ * propagated via `next(err)`.
12
+ * - `post-middleware`: runs after the consumer's middlewares. Strict.
13
+ */
14
+ const phasedContextMiddleware = (feature, phase) => async (req, res, next) => {
15
+ const isPreBeforeware = phase === 'pre-beforeware';
16
+ try {
17
+ const { context, matched } = await feature.runContextFactoriesForPhase(req, res, phase);
18
+ if (isPreBeforeware) {
19
+ const services = feature.services || {};
20
+ req.context = { ...(req.context || {}), ...context };
21
+ req.services = { ...(req.services || {}), ...services, ...context };
22
+ }
23
+ else if (matched) {
24
+ req.context = { ...(req.context || {}), ...context };
25
+ req.services = { ...(req.services || {}), ...context };
26
+ }
9
27
  next();
10
- })
11
- .catch((err) => next());
12
- };export{contextServicesMiddleware};//# sourceMappingURL=services.mjs.map
28
+ }
29
+ catch (err) {
30
+ if (isPreBeforeware) {
31
+ // Lenient: some factories require auth (which has not run yet).
32
+ // Seed req.services with the static service map so downstream
33
+ // beforewares still have a usable services object.
34
+ // eslint-disable-next-line no-console
35
+ console.warn(`[phased-context] pre-beforeware factory failed (continuing):`, err?.message);
36
+ const services = feature.services || {};
37
+ if (!req.services)
38
+ req.services = { ...services };
39
+ else
40
+ req.services = { ...req.services, ...services };
41
+ next();
42
+ }
43
+ else {
44
+ next(err);
45
+ }
46
+ }
47
+ };export{phasedContextMiddleware};//# sourceMappingURL=services.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"services.mjs","sources":["../../src/middleware/services.ts"],"sourcesContent":[null],"names":[],"mappings":"yBAEa,MAAA,yBAAyB,GAAG,CAAC,aAAa,EAAE,cAAc,KAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;IAC5F,OAAO,CAAC,GAAG,CAAC;AACR,QAAA,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;AACvB,QAAA,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC;KAC3B,CAAC;SACG,IAAI,CAAC,CAAC,CAAE,OAAO,EAAE,QAAQ,CAAE,KAAI;AAC5B,QAAA,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;AACtB,QAAA,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAExB,QAAA,IAAI,EAAE,CAAC;AACX,KAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC;AAChC"}
1
+ {"version":3,"file":"services.mjs","sources":["../../src/middleware/services.ts"],"sourcesContent":[null],"names":[],"mappings":"yBAsBA;;;;;;;;;;;;AAYG;AACU,MAAA,uBAAuB,GAAG,CAAC,OAAgB,EAAE,KAAmB,KAAK,OAAO,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;AACvG,IAAA,MAAM,eAAe,GAAG,KAAK,KAAK,gBAAgB,CAAC;AACnD,IAAA,IAAI;AACA,QAAA,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,2BAA2B,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACxF,IAAI,eAAe,EAAE;AACjB,YAAA,MAAM,QAAQ,GAAI,OAAe,CAAC,QAAQ,IAAI,EAAE,CAAC;AACjD,YAAA,GAAG,CAAC,OAAO,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;AACrD,YAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;SACvE;aAAM,IAAI,OAAO,EAAE;AAChB,YAAA,GAAG,CAAC,OAAO,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;AACrD,YAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;SAC1D;AACD,QAAA,IAAI,EAAE,CAAC;KACV;IAAC,OAAO,GAAG,EAAE;QACV,IAAI,eAAe,EAAE;;;;;YAKjB,OAAO,CAAC,IAAI,CAAC,CAAA,4DAAA,CAA8D,EAAG,GAAa,EAAE,OAAO,CAAC,CAAC;AACtG,YAAA,MAAM,QAAQ,GAAI,OAAe,CAAC,QAAQ,IAAI,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,QAAQ;AAAE,gBAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;;AAC7C,gBAAA,GAAG,CAAC,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC;AACrD,YAAA,IAAI,EAAE,CAAC;SACV;aAAM;YACH,IAAI,CAAC,GAAG,CAAC,CAAC;SACb;KACJ;AACL"}
@@ -1,38 +1,53 @@
1
- 'use strict';var express=require('express'),bodyParser=require('body-parser'),error=require('../middleware/error.cjs'),services=require('../middleware/services.cjs');require('../middleware/sentry.cjs');var redisClient=require('../middleware/redis-client.cjs'),cors=require('../middleware/cors.cjs'),Sentry=require('@sentry/node');function _interopNamespaceDefault(e){var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var Sentry__namespace=/*#__PURE__*/_interopNamespaceDefault(Sentry);/* eslint-disable @typescript-eslint/no-var-requires */
1
+ 'use strict';var expressAdapter=require('../express-adapter.cjs'),error=require('../middleware/error.cjs'),services=require('../middleware/services.cjs');require('../middleware/sentry.cjs');var redisClient=require('../middleware/redis-client.cjs'),cors=require('../middleware/cors.cjs'),Sentry=require('@sentry/node');function _interopNamespaceDefault(e){var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var Sentry__namespace=/*#__PURE__*/_interopNamespaceDefault(Sentry);/* eslint-disable @typescript-eslint/no-var-requires */
2
2
  function expressApp(modules, options, middlewares, http, redisClient$1) {
3
- const app = express();
3
+ const app = expressAdapter.express();
4
4
  // FIRST PRIORITY: Add Redis client middleware at the very top
5
5
  // This ensures req.redisClient is available to ALL downstream middleware
6
6
  if (redisClient$1) {
7
7
  app.use(redisClient.redisClientMiddleware(redisClient$1));
8
8
  }
9
- app.use(services.contextServicesMiddleware(options.createContext, options.serviceContext));
9
+ // CORS must run BEFORE auth/beforewares so that auth-rejected responses
10
+ // (e.g. 401 from /graphql auth chain) still carry the
11
+ // Access-Control-Allow-Origin header. Otherwise browsers report a CORS
12
+ // error instead of surfacing the 401 to the app.
13
+ app.use(cors.corsMiddleware);
14
+ app.use((req, res, next) => {
15
+ res.header('Access-Control-Allow-Credentials', JSON.stringify(true));
16
+ res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
17
+ res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, X-CSP-Nonce, Content-Type, Accept');
18
+ next();
19
+ });
20
+ // Phase 1: pre-beforeware — runs default-tagged context factories and
21
+ // seeds req.services from the static service map. Lenient: a factory
22
+ // that throws (e.g. one that requires auth) is logged and skipped.
23
+ app.use(services.phasedContextMiddleware(modules, 'pre-beforeware'));
24
+ // Consumer beforewares (e.g. auth chains for /graphql, /api/workflow).
10
25
  for (const applyBeforeware of modules.beforewares) {
11
26
  applyBeforeware(app);
12
27
  }
28
+ // Phase 2: pre-middleware — runs context factories tagged
29
+ // `phase = 'pre-middleware'` (e.g. ones that read req.user). Strict.
30
+ app.use(services.phasedContextMiddleware(modules, 'pre-middleware'));
13
31
  // Don't rate limit heroku
14
32
  app.enable('trust proxy');
15
33
  if (middlewares !== null) {
16
34
  app.use(middlewares);
17
35
  }
18
- app.use(cors.corsMiddleware);
19
- app.use((req, res, next) => {
20
- res.header('Access-Control-Allow-Credentials', JSON.stringify(true));
21
- res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
22
- res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, X-CSP-Nonce, Content-Type, Accept');
23
- next();
24
- });
25
- app.use(bodyParser.json({
36
+ app.use(expressAdapter.jsonParser({
26
37
  limit: '50mb',
27
38
  verify: (req, res, buf) => {
28
39
  // #Todo: Find some proper solution for it
29
40
  req.rawBody = buf;
30
41
  },
31
42
  }));
32
- app.use(bodyParser.urlencoded({ limit: '50mb', extended: true, parameterLimit: 50000 }));
43
+ app.use(expressAdapter.urlencodedParser({ limit: '50mb', extended: true, parameterLimit: 50000 }));
33
44
  for (const applyMiddleware of modules.middlewares) {
34
45
  applyMiddleware(app);
35
46
  }
47
+ // Phase 3: post-middleware — runs context factories tagged
48
+ // `phase = 'post-middleware'`. Strict. Skipped at zero cost when none
49
+ // are tagged.
50
+ app.use(services.phasedContextMiddleware(modules, 'post-middleware'));
36
51
  // Sentry error handler must be after all middleware/routes but before generic error handlers
37
52
  Sentry__namespace.setupExpressErrorHandler(app);
38
53
  if (__DEV__) {
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressApp.cjs","sources":["../../src/servers/ExpressApp.ts"],"sourcesContent":[null],"names":["redisClient","redisClientMiddleware","contextServicesMiddleware","corsMiddleware","Sentry","errorMiddleware"],"mappings":"4qBAAA;AAWM,SAAU,UAAU,CAAC,OAAgB,EAAE,OAAuB,EAAE,WAAW,EAAE,IAAK,EAAEA,aAAY,EAAA;AAClG,IAAA,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;;;IAItB,IAAIA,aAAW,EAAE;QACb,GAAG,CAAC,GAAG,CAACC,iCAAqB,CAACD,aAAW,CAAC,CAAC,CAAC;KAC/C;AAED,IAAA,GAAG,CAAC,GAAG,CAACE,kCAAyB,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;AAClF,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;AAGD,IAAA,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAE1B,IAAA,IAAI,WAAW,KAAK,IAAI,EAAE;AACtB,QAAA,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;KACxB;AAED,IAAA,GAAG,CAAC,GAAG,CAACC,mBAAc,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;AACvB,QAAA,GAAG,CAAC,MAAM,CAAC,kCAAkC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,QAAA,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,qBAAqB,CAAC,CAAC;AAClE,QAAA,GAAG,CAAC,MAAM,CACN,8BAA8B,EAC9B,6EAA6E,CAChF,CAAC;AACF,QAAA,IAAI,EAAE,CAAC;AACX,KAAC,CAAC,CAAC;AAEH,IAAA,GAAG,CAAC,GAAG,CACH,UAAU,CAAC,IAAI,CAAC;AACZ,QAAA,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,KAAI;;AAErB,YAAA,GAAW,CAAC,OAAO,GAAG,GAAG,CAAC;SAC9B;AACJ,KAAA,CAAC,CACL,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAEzF,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;AAGD,IAAAC,iBAAM,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,OAAO,EAAE;AACT,QAAA,GAAG,CAAC,GAAG,CAACC,qBAAe,CAAC,CAAC;KAC5B;AAED,IAAA,OAAO,GAAG,CAAC;AACf"}
1
+ {"version":3,"file":"ExpressApp.cjs","sources":["../../src/servers/ExpressApp.ts"],"sourcesContent":[null],"names":["redisClient","express","redisClientMiddleware","corsMiddleware","phasedContextMiddleware","jsonParser","urlencodedParser","Sentry","errorMiddleware"],"mappings":"gqBAAA;AAUM,SAAU,UAAU,CAAC,OAAgB,EAAE,OAAuB,EAAE,WAAW,EAAE,IAAK,EAAEA,aAAY,EAAA;AAClG,IAAA,MAAM,GAAG,GAAGC,sBAAO,EAAE,CAAC;;;IAItB,IAAID,aAAW,EAAE;QACb,GAAG,CAAC,GAAG,CAACE,iCAAqB,CAACF,aAAW,CAAC,CAAC,CAAC;KAC/C;;;;;AAMD,IAAA,GAAG,CAAC,GAAG,CAACG,mBAAc,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;AACvB,QAAA,GAAG,CAAC,MAAM,CAAC,kCAAkC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,QAAA,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,qBAAqB,CAAC,CAAC;AAClE,QAAA,GAAG,CAAC,MAAM,CACN,8BAA8B,EAC9B,6EAA6E,CAChF,CAAC;AACF,QAAA,IAAI,EAAE,CAAC;AACX,KAAC,CAAC,CAAC;;;;IAKH,GAAG,CAAC,GAAG,CAACC,gCAAuB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;;AAG5D,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;;IAID,GAAG,CAAC,GAAG,CAACA,gCAAuB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;;AAG5D,IAAA,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAE1B,IAAA,IAAI,WAAW,KAAK,IAAI,EAAE;AACtB,QAAA,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;KACxB;AAED,IAAA,GAAG,CAAC,GAAG,CACHC,yBAAU,CAAC;AACP,QAAA,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,KAAI;;AAErB,YAAA,GAAW,CAAC,OAAO,GAAG,GAAG,CAAC;SAC9B;AACJ,KAAA,CAAC,CACL,CAAC;IACF,GAAG,CAAC,GAAG,CAACC,+BAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAEpF,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;;;IAKD,GAAG,CAAC,GAAG,CAACF,gCAAuB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;;AAG7D,IAAAG,iBAAM,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,OAAO,EAAE;AACT,QAAA,GAAG,CAAC,GAAG,CAACC,qBAAe,CAAC,CAAC;KAC5B;AAED,IAAA,OAAO,GAAG,CAAC;AACf"}
@@ -1,4 +1,4 @@
1
- import express from'express';import bodyParser from'body-parser';import {errorMiddleware}from'../middleware/error.mjs';import {contextServicesMiddleware}from'../middleware/services.mjs';import'../middleware/sentry.mjs';import {redisClientMiddleware}from'../middleware/redis-client.mjs';import {corsMiddleware}from'../middleware/cors.mjs';import*as Sentry from'@sentry/node';/* eslint-disable @typescript-eslint/no-var-requires */
1
+ import {express,jsonParser,urlencodedParser}from'../express-adapter.mjs';import {errorMiddleware}from'../middleware/error.mjs';import {phasedContextMiddleware}from'../middleware/services.mjs';import'../middleware/sentry.mjs';import {redisClientMiddleware}from'../middleware/redis-client.mjs';import {corsMiddleware}from'../middleware/cors.mjs';import*as Sentry from'@sentry/node';/* eslint-disable @typescript-eslint/no-var-requires */
2
2
  function expressApp(modules, options, middlewares, http, redisClient) {
3
3
  const app = express();
4
4
  // FIRST PRIORITY: Add Redis client middleware at the very top
@@ -6,33 +6,48 @@ function expressApp(modules, options, middlewares, http, redisClient) {
6
6
  if (redisClient) {
7
7
  app.use(redisClientMiddleware(redisClient));
8
8
  }
9
- app.use(contextServicesMiddleware(options.createContext, options.serviceContext));
9
+ // CORS must run BEFORE auth/beforewares so that auth-rejected responses
10
+ // (e.g. 401 from /graphql auth chain) still carry the
11
+ // Access-Control-Allow-Origin header. Otherwise browsers report a CORS
12
+ // error instead of surfacing the 401 to the app.
13
+ app.use(corsMiddleware);
14
+ app.use((req, res, next) => {
15
+ res.header('Access-Control-Allow-Credentials', JSON.stringify(true));
16
+ res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
17
+ res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, X-CSP-Nonce, Content-Type, Accept');
18
+ next();
19
+ });
20
+ // Phase 1: pre-beforeware — runs default-tagged context factories and
21
+ // seeds req.services from the static service map. Lenient: a factory
22
+ // that throws (e.g. one that requires auth) is logged and skipped.
23
+ app.use(phasedContextMiddleware(modules, 'pre-beforeware'));
24
+ // Consumer beforewares (e.g. auth chains for /graphql, /api/workflow).
10
25
  for (const applyBeforeware of modules.beforewares) {
11
26
  applyBeforeware(app);
12
27
  }
28
+ // Phase 2: pre-middleware — runs context factories tagged
29
+ // `phase = 'pre-middleware'` (e.g. ones that read req.user). Strict.
30
+ app.use(phasedContextMiddleware(modules, 'pre-middleware'));
13
31
  // Don't rate limit heroku
14
32
  app.enable('trust proxy');
15
33
  if (middlewares !== null) {
16
34
  app.use(middlewares);
17
35
  }
18
- app.use(corsMiddleware);
19
- app.use((req, res, next) => {
20
- res.header('Access-Control-Allow-Credentials', JSON.stringify(true));
21
- res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
22
- res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, X-CSP-Nonce, Content-Type, Accept');
23
- next();
24
- });
25
- app.use(bodyParser.json({
36
+ app.use(jsonParser({
26
37
  limit: '50mb',
27
38
  verify: (req, res, buf) => {
28
39
  // #Todo: Find some proper solution for it
29
40
  req.rawBody = buf;
30
41
  },
31
42
  }));
32
- app.use(bodyParser.urlencoded({ limit: '50mb', extended: true, parameterLimit: 50000 }));
43
+ app.use(urlencodedParser({ limit: '50mb', extended: true, parameterLimit: 50000 }));
33
44
  for (const applyMiddleware of modules.middlewares) {
34
45
  applyMiddleware(app);
35
46
  }
47
+ // Phase 3: post-middleware — runs context factories tagged
48
+ // `phase = 'post-middleware'`. Strict. Skipped at zero cost when none
49
+ // are tagged.
50
+ app.use(phasedContextMiddleware(modules, 'post-middleware'));
36
51
  // Sentry error handler must be after all middleware/routes but before generic error handlers
37
52
  Sentry.setupExpressErrorHandler(app);
38
53
  if (__DEV__) {
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressApp.mjs","sources":["../../src/servers/ExpressApp.ts"],"sourcesContent":[null],"names":[],"mappings":"sXAAA;AAWM,SAAU,UAAU,CAAC,OAAgB,EAAE,OAAuB,EAAE,WAAW,EAAE,IAAK,EAAE,WAAY,EAAA;AAClG,IAAA,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;;;IAItB,IAAI,WAAW,EAAE;QACb,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC;KAC/C;AAED,IAAA,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;AAClF,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;AAGD,IAAA,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAE1B,IAAA,IAAI,WAAW,KAAK,IAAI,EAAE;AACtB,QAAA,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;KACxB;AAED,IAAA,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;AACvB,QAAA,GAAG,CAAC,MAAM,CAAC,kCAAkC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,QAAA,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,qBAAqB,CAAC,CAAC;AAClE,QAAA,GAAG,CAAC,MAAM,CACN,8BAA8B,EAC9B,6EAA6E,CAChF,CAAC;AACF,QAAA,IAAI,EAAE,CAAC;AACX,KAAC,CAAC,CAAC;AAEH,IAAA,GAAG,CAAC,GAAG,CACH,UAAU,CAAC,IAAI,CAAC;AACZ,QAAA,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,KAAI;;AAErB,YAAA,GAAW,CAAC,OAAO,GAAG,GAAG,CAAC;SAC9B;AACJ,KAAA,CAAC,CACL,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAEzF,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;AAGD,IAAA,MAAM,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,OAAO,EAAE;AACT,QAAA,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;KAC5B;AAED,IAAA,OAAO,GAAG,CAAC;AACf"}
1
+ {"version":3,"file":"ExpressApp.mjs","sources":["../../src/servers/ExpressApp.ts"],"sourcesContent":[null],"names":[],"mappings":"4XAAA;AAUM,SAAU,UAAU,CAAC,OAAgB,EAAE,OAAuB,EAAE,WAAW,EAAE,IAAK,EAAE,WAAY,EAAA;AAClG,IAAA,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;;;IAItB,IAAI,WAAW,EAAE;QACb,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC;KAC/C;;;;;AAMD,IAAA,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;AACvB,QAAA,GAAG,CAAC,MAAM,CAAC,kCAAkC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,QAAA,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,qBAAqB,CAAC,CAAC;AAClE,QAAA,GAAG,CAAC,MAAM,CACN,8BAA8B,EAC9B,6EAA6E,CAChF,CAAC;AACF,QAAA,IAAI,EAAE,CAAC;AACX,KAAC,CAAC,CAAC;;;;IAKH,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;;AAG5D,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;;IAID,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;;AAG5D,IAAA,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAE1B,IAAA,IAAI,WAAW,KAAK,IAAI,EAAE;AACtB,QAAA,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;KACxB;AAED,IAAA,GAAG,CAAC,GAAG,CACH,UAAU,CAAC;AACP,QAAA,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,KAAI;;AAErB,YAAA,GAAW,CAAC,OAAO,GAAG,GAAG,CAAC;SAC9B;AACJ,KAAA,CAAC,CACL,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAEpF,IAAA,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE;QAC/C,eAAe,CAAC,GAAG,CAAC,CAAC;KACxB;;;;IAKD,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;;AAG7D,IAAA,MAAM,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,OAAO,EAAE;AACT,QAAA,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;KAC5B;AAED,IAAA,OAAO,GAAG,CAAC;AACf"}
@@ -1,4 +1,4 @@
1
- 'use strict';var server=require('@apollo/server'),express4=require('@apollo/server/express4'),cacheControl=require('@apollo/server/plugin/cacheControl'),drainHttpServer=require('@apollo/server/plugin/drainHttpServer');require('isomorphic-fetch');var express=require('express'),cors=require('cors'),Keyv=require('keyv'),KeyvRedis=require('@keyv/redis'),utils_keyvadapter=require('@apollo/utils.keyvadapter'),ws=require('ws'),storeRedis=require('@common-stack/store-redis'),GraphqlWs=require('./GraphqlWs.cjs'),envConfig=require('../config/env-config.cjs');if ((process.env.LOG_LEVEL && process.env.LOG_LEVEL === 'trace') || process.env.LOG_LEVEL === 'debug') ;
1
+ 'use strict';var server=require('@apollo/server'),express4=require('@apollo/server/express4'),cacheControl=require('@apollo/server/plugin/cacheControl');require('isomorphic-fetch');var expressAdapter=require('../express-adapter.cjs'),cors=require('../middleware/cors.cjs'),Keyv=require('keyv'),KeyvRedis=require('@keyv/redis'),utils_keyvadapter=require('@apollo/utils.keyvadapter'),storeRedis=require('@common-stack/store-redis'),GraphqlWs=require('./GraphqlWs.cjs'),envConfig=require('../config/env-config.cjs');if ((process.env.LOG_LEVEL && process.env.LOG_LEVEL === 'trace') || process.env.LOG_LEVEL === 'debug') ;
2
2
  // @workaround as the `dataSources` not available in Subscription (websocket) Context.
3
3
  // https://github.com/apollographql/apollo-server/issues/1526 need to revisit in Apollo-Server v3.
4
4
  const constructDataSourcesForSubscriptions = (context, cache, dataSources) => {
@@ -12,7 +12,7 @@ const constructDataSourcesForSubscriptions = (context, cache, dataSources) => {
12
12
  };
13
13
  class GraphqlServer {
14
14
  app;
15
- httpServer;
15
+ wsServerHost;
16
16
  redisClient;
17
17
  moduleService;
18
18
  enableSubscription;
@@ -21,9 +21,10 @@ class GraphqlServer {
21
21
  plugins;
22
22
  logger;
23
23
  graphqlWsServer;
24
- constructor(app, httpServer, redisClient, moduleService, enableSubscription = true, cacheKeyGenerator, invalidateCacheKeyGenerator, plugins) {
24
+ constructor(app, wsServerHost, // ultimate-express app or http.Server
25
+ redisClient, moduleService, enableSubscription = true, cacheKeyGenerator, invalidateCacheKeyGenerator, plugins) {
25
26
  this.app = app;
26
- this.httpServer = httpServer;
27
+ this.wsServerHost = wsServerHost;
27
28
  this.redisClient = redisClient;
28
29
  this.moduleService = moduleService;
29
30
  this.enableSubscription = enableSubscription;
@@ -32,8 +33,11 @@ class GraphqlServer {
32
33
  this.plugins = plugins;
33
34
  this.logger = this.moduleService.logger.child({ className: 'GraphqlServer' });
34
35
  if (enableSubscription) {
35
- const wsServer = new ws.WebSocketServer({
36
- server: this.httpServer,
36
+ // WebSocketServer binds to different hosts depending on engine:
37
+ // - ultimate-express: pass the app as 'server' (ultimate-ws)
38
+ // - classic express: pass the httpServer as 'server' (ws)
39
+ const wsServer = new expressAdapter.WebSocketServer({
40
+ server: this.wsServerHost,
37
41
  path: envConfig.config.GRAPHQL_ENDPOINT,
38
42
  });
39
43
  this.graphqlWsServer = new GraphqlWs.GraphqlWs(wsServer, this.moduleService, this.redisClient);
@@ -46,11 +50,7 @@ class GraphqlServer {
46
50
  }
47
51
  const apolloServer = this.configureApolloServer();
48
52
  await apolloServer.start();
49
- const corsOptions = {
50
- origin: [envConfig.config.CLIENT_URL],
51
- credentials: true,
52
- };
53
- this.app.use(__GRAPHQL_ENDPOINT__, cors(corsOptions), express.json(), express4.expressMiddleware(apolloServer, {
53
+ this.app.use(__GRAPHQL_ENDPOINT__, cors.corsMiddleware, expressAdapter.express.json(), express4.expressMiddleware(apolloServer, {
54
54
  context: async ({ req, res, connection }) => {
55
55
  let context;
56
56
  let addons = {};
@@ -117,17 +117,29 @@ class GraphqlServer {
117
117
  const cacheSet = cacheAdapter.set.bind(cacheAdapter);
118
118
  cacheAdapter.set = (key, value, opts) => cacheSet(key.replaceAll('fqc:', ''), value, opts);
119
119
  const { cacheKeyGenerator, invalidateCacheKeyGenerator, logger } = this;
120
+ const serverPlugins = [
121
+ cacheControl.ApolloServerPluginCacheControl(),
122
+ storeRedis.invalidateCachePlugin({ cache: this.redisClient, invalidateCacheKeyGenerator }),
123
+ storeRedis.responseCachePlugin({ logger, cacheKeyGenerator }),
124
+ ...(this.plugins ?? []),
125
+ ];
126
+ // For classic express, add drain plugin to gracefully close the HTTP server.
127
+ // ultimate-express (uWebSockets.js) handles connection draining natively.
128
+ if (!expressAdapter.isUltimateEngine && this.wsServerHost) {
129
+ try {
130
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
131
+ const { ApolloServerPluginDrainHttpServer } = require('@apollo/server/plugin/drainHttpServer');
132
+ serverPlugins.push(ApolloServerPluginDrainHttpServer({ httpServer: this.wsServerHost }));
133
+ }
134
+ catch {
135
+ // Plugin not available — skip
136
+ }
137
+ }
120
138
  const serverConfig = {
121
139
  schema: this.moduleService.schema,
122
140
  allowBatchedHttpRequests: true,
123
141
  cache: cacheAdapter,
124
- plugins: [
125
- drainHttpServer.ApolloServerPluginDrainHttpServer({ httpServer: this.httpServer }),
126
- cacheControl.ApolloServerPluginCacheControl(),
127
- storeRedis.invalidateCachePlugin({ cache: this.redisClient, invalidateCacheKeyGenerator }),
128
- storeRedis.responseCachePlugin({ logger, cacheKeyGenerator }),
129
- ...(this.plugins ?? []),
130
- ],
142
+ plugins: serverPlugins,
131
143
  };
132
144
  if (this.enableSubscription) {
133
145
  serverConfig.plugins.push({
@@ -1 +1 @@
1
- {"version":3,"file":"GraphqlServer.cjs","sources":["../../src/servers/GraphqlServer.ts"],"sourcesContent":[null],"names":["WebSocketServer","config","GraphqlWs","expressMiddleware","KeyvAdapter","ApolloServerPluginDrainHttpServer","ApolloServerPluginCacheControl","invalidateCachePlugin","responseCachePlugin","ApolloServer"],"mappings":"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"}
1
+ {"version":3,"file":"GraphqlServer.cjs","sources":["../../src/servers/GraphqlServer.ts"],"sourcesContent":[null],"names":["WebSocketServer","config","GraphqlWs","corsMiddleware","express","expressMiddleware","KeyvAdapter","ApolloServerPluginCacheControl","invalidateCachePlugin","responseCachePlugin","isUltimateEngine","ApolloServer"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import {ApolloServer}from'@apollo/server';import {expressMiddleware}from'@apollo/server/express4';import {ApolloServerPluginCacheControl}from'@apollo/server/plugin/cacheControl';import {ApolloServerPluginDrainHttpServer}from'@apollo/server/plugin/drainHttpServer';import'isomorphic-fetch';import express from'express';import cors from'cors';import Keyv from'keyv';import KeyvRedis from'@keyv/redis';import {KeyvAdapter}from'@apollo/utils.keyvadapter';import {WebSocketServer}from'ws';import {invalidateCachePlugin,responseCachePlugin}from'@common-stack/store-redis';import {GraphqlWs}from'./GraphqlWs.mjs';import {config}from'../config/env-config.mjs';if ((process.env.LOG_LEVEL && process.env.LOG_LEVEL === 'trace') || process.env.LOG_LEVEL === 'debug') ;
1
+ import {ApolloServer}from'@apollo/server';import {expressMiddleware}from'@apollo/server/express4';import {ApolloServerPluginCacheControl}from'@apollo/server/plugin/cacheControl';import'isomorphic-fetch';import {WebSocketServer,express,isUltimateEngine}from'../express-adapter.mjs';import {corsMiddleware}from'../middleware/cors.mjs';import Keyv from'keyv';import KeyvRedis from'@keyv/redis';import {KeyvAdapter}from'@apollo/utils.keyvadapter';import {invalidateCachePlugin,responseCachePlugin}from'@common-stack/store-redis';import {GraphqlWs}from'./GraphqlWs.mjs';import {config}from'../config/env-config.mjs';if ((process.env.LOG_LEVEL && process.env.LOG_LEVEL === 'trace') || process.env.LOG_LEVEL === 'debug') ;
2
2
  // @workaround as the `dataSources` not available in Subscription (websocket) Context.
3
3
  // https://github.com/apollographql/apollo-server/issues/1526 need to revisit in Apollo-Server v3.
4
4
  const constructDataSourcesForSubscriptions = (context, cache, dataSources) => {
@@ -12,7 +12,7 @@ const constructDataSourcesForSubscriptions = (context, cache, dataSources) => {
12
12
  };
13
13
  class GraphqlServer {
14
14
  app;
15
- httpServer;
15
+ wsServerHost;
16
16
  redisClient;
17
17
  moduleService;
18
18
  enableSubscription;
@@ -21,9 +21,10 @@ class GraphqlServer {
21
21
  plugins;
22
22
  logger;
23
23
  graphqlWsServer;
24
- constructor(app, httpServer, redisClient, moduleService, enableSubscription = true, cacheKeyGenerator, invalidateCacheKeyGenerator, plugins) {
24
+ constructor(app, wsServerHost, // ultimate-express app or http.Server
25
+ redisClient, moduleService, enableSubscription = true, cacheKeyGenerator, invalidateCacheKeyGenerator, plugins) {
25
26
  this.app = app;
26
- this.httpServer = httpServer;
27
+ this.wsServerHost = wsServerHost;
27
28
  this.redisClient = redisClient;
28
29
  this.moduleService = moduleService;
29
30
  this.enableSubscription = enableSubscription;
@@ -32,8 +33,11 @@ class GraphqlServer {
32
33
  this.plugins = plugins;
33
34
  this.logger = this.moduleService.logger.child({ className: 'GraphqlServer' });
34
35
  if (enableSubscription) {
36
+ // WebSocketServer binds to different hosts depending on engine:
37
+ // - ultimate-express: pass the app as 'server' (ultimate-ws)
38
+ // - classic express: pass the httpServer as 'server' (ws)
35
39
  const wsServer = new WebSocketServer({
36
- server: this.httpServer,
40
+ server: this.wsServerHost,
37
41
  path: config.GRAPHQL_ENDPOINT,
38
42
  });
39
43
  this.graphqlWsServer = new GraphqlWs(wsServer, this.moduleService, this.redisClient);
@@ -46,11 +50,7 @@ class GraphqlServer {
46
50
  }
47
51
  const apolloServer = this.configureApolloServer();
48
52
  await apolloServer.start();
49
- const corsOptions = {
50
- origin: [config.CLIENT_URL],
51
- credentials: true,
52
- };
53
- this.app.use(__GRAPHQL_ENDPOINT__, cors(corsOptions), express.json(), expressMiddleware(apolloServer, {
53
+ this.app.use(__GRAPHQL_ENDPOINT__, corsMiddleware, express.json(), expressMiddleware(apolloServer, {
54
54
  context: async ({ req, res, connection }) => {
55
55
  let context;
56
56
  let addons = {};
@@ -117,17 +117,29 @@ class GraphqlServer {
117
117
  const cacheSet = cacheAdapter.set.bind(cacheAdapter);
118
118
  cacheAdapter.set = (key, value, opts) => cacheSet(key.replaceAll('fqc:', ''), value, opts);
119
119
  const { cacheKeyGenerator, invalidateCacheKeyGenerator, logger } = this;
120
+ const serverPlugins = [
121
+ ApolloServerPluginCacheControl(),
122
+ invalidateCachePlugin({ cache: this.redisClient, invalidateCacheKeyGenerator }),
123
+ responseCachePlugin({ logger, cacheKeyGenerator }),
124
+ ...(this.plugins ?? []),
125
+ ];
126
+ // For classic express, add drain plugin to gracefully close the HTTP server.
127
+ // ultimate-express (uWebSockets.js) handles connection draining natively.
128
+ if (!isUltimateEngine && this.wsServerHost) {
129
+ try {
130
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
131
+ const { ApolloServerPluginDrainHttpServer } = require('@apollo/server/plugin/drainHttpServer');
132
+ serverPlugins.push(ApolloServerPluginDrainHttpServer({ httpServer: this.wsServerHost }));
133
+ }
134
+ catch {
135
+ // Plugin not available — skip
136
+ }
137
+ }
120
138
  const serverConfig = {
121
139
  schema: this.moduleService.schema,
122
140
  allowBatchedHttpRequests: true,
123
141
  cache: cacheAdapter,
124
- plugins: [
125
- ApolloServerPluginDrainHttpServer({ httpServer: this.httpServer }),
126
- ApolloServerPluginCacheControl(),
127
- invalidateCachePlugin({ cache: this.redisClient, invalidateCacheKeyGenerator }),
128
- responseCachePlugin({ logger, cacheKeyGenerator }),
129
- ...(this.plugins ?? []),
130
- ],
142
+ plugins: serverPlugins,
131
143
  };
132
144
  if (this.enableSubscription) {
133
145
  serverConfig.plugins.push({