graphile-cache 3.8.0 → 3.10.1

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.
@@ -2,6 +2,12 @@ import type { GraphileCacheEntry } from './graphile-cache';
2
2
  interface GraphileInstanceOptions {
3
3
  preset: any;
4
4
  cacheKey: string;
5
+ /**
6
+ * When true, a RealtimeManager is created and started alongside the
7
+ * PostGraphile instance. The pool is extracted from the preset's
8
+ * pgServices (managed by pg-cache) rather than passed separately.
9
+ */
10
+ enableRealtime?: boolean;
5
11
  }
6
12
  /**
7
13
  * Create a PostGraphile v5 instance backed by grafserv/express.
@@ -12,6 +18,12 @@ interface GraphileInstanceOptions {
12
18
  *
13
19
  * Callers are responsible for building the `GraphileConfig.Preset` (including
14
20
  * pgServices, grafserv options, grafast context, etc.) before passing it here.
21
+ *
22
+ * When `enableRealtime` is true, a RealtimeManager is created that bridges
23
+ * cursor-tracked events from `drain_changes()` into the PostGraphile
24
+ * instance's PgSubscriber EventEmitter. Both `pgSubscriber` and the pg
25
+ * pool are extracted from the resolved preset's pgServices — no separate
26
+ * pool parameter is needed.
15
27
  */
16
28
  export declare const createGraphileInstance: (opts: GraphileInstanceOptions) => Promise<GraphileCacheEntry>;
17
29
  export {};
@@ -5,9 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createGraphileInstance = void 0;
7
7
  const node_http_1 = require("node:http");
8
+ const logger_1 = require("@pgpmjs/logger");
8
9
  const express_1 = __importDefault(require("express"));
9
10
  const postgraphile_1 = require("postgraphile");
10
11
  const v4_1 = require("grafserv/express/v4");
12
+ const log = new logger_1.Logger('graphile-cache:create');
11
13
  /**
12
14
  * Create a PostGraphile v5 instance backed by grafserv/express.
13
15
  *
@@ -17,16 +19,22 @@ const v4_1 = require("grafserv/express/v4");
17
19
  *
18
20
  * Callers are responsible for building the `GraphileConfig.Preset` (including
19
21
  * pgServices, grafserv options, grafast context, etc.) before passing it here.
22
+ *
23
+ * When `enableRealtime` is true, a RealtimeManager is created that bridges
24
+ * cursor-tracked events from `drain_changes()` into the PostGraphile
25
+ * instance's PgSubscriber EventEmitter. Both `pgSubscriber` and the pg
26
+ * pool are extracted from the resolved preset's pgServices — no separate
27
+ * pool parameter is needed.
20
28
  */
21
29
  const createGraphileInstance = async (opts) => {
22
- const { preset, cacheKey } = opts;
30
+ const { preset, cacheKey, enableRealtime = false } = opts;
23
31
  const pgl = (0, postgraphile_1.postgraphile)(preset);
24
32
  const serv = pgl.createServ(v4_1.grafserv);
25
33
  const handler = (0, express_1.default)();
26
34
  const httpServer = (0, node_http_1.createServer)(handler);
27
35
  await serv.addTo(handler, httpServer);
28
36
  await serv.ready();
29
- return {
37
+ const entry = {
30
38
  pgl,
31
39
  serv,
32
40
  handler,
@@ -34,5 +42,38 @@ const createGraphileInstance = async (opts) => {
34
42
  cacheKey,
35
43
  createdAt: Date.now(),
36
44
  };
45
+ if (enableRealtime) {
46
+ try {
47
+ const { RealtimeManager } = await import('graphile-realtime-subscriptions');
48
+ // Extract PgSubscriber and pool from the resolved preset's pgServices.
49
+ // The pool is the same instance managed by pg-cache (via getPgPool)
50
+ // and threaded into the preset by makePgService({ pool, schemas }).
51
+ const resolvedPreset = pgl.getResolvedPreset();
52
+ const pgService = resolvedPreset.pgServices?.[0];
53
+ const pgSubscriber = pgService?.pgSubscriber ?? null;
54
+ const pool = pgService?.adaptorSettings?.pool ?? null;
55
+ if (!pgSubscriber) {
56
+ log.warn(`PostGraphile[${cacheKey}] has no pgSubscriber — RealtimeManager will not be started`);
57
+ }
58
+ else if (!pool) {
59
+ log.warn(`PostGraphile[${cacheKey}] has no pool in pgService — RealtimeManager will not be started`);
60
+ }
61
+ else {
62
+ const manager = new RealtimeManager({
63
+ pgSubscriber,
64
+ pool,
65
+ nodeId: `graphile-cache:${cacheKey}`,
66
+ schema: 'realtime_public',
67
+ });
68
+ await manager.start();
69
+ entry.realtimeManager = manager;
70
+ log.info(`RealtimeManager started for PostGraphile[${cacheKey}]`);
71
+ }
72
+ }
73
+ catch (err) {
74
+ log.error(`Failed to start RealtimeManager for PostGraphile[${cacheKey}]:`, err);
75
+ }
76
+ }
77
+ return entry;
37
78
  };
38
79
  exports.createGraphileInstance = createGraphileInstance;
@@ -1,7 +1,9 @@
1
1
  import { createServer } from 'node:http';
2
+ import { Logger } from '@pgpmjs/logger';
2
3
  import express from 'express';
3
4
  import { postgraphile } from 'postgraphile';
4
5
  import { grafserv } from 'grafserv/express/v4';
6
+ const log = new Logger('graphile-cache:create');
5
7
  /**
6
8
  * Create a PostGraphile v5 instance backed by grafserv/express.
7
9
  *
@@ -11,16 +13,22 @@ import { grafserv } from 'grafserv/express/v4';
11
13
  *
12
14
  * Callers are responsible for building the `GraphileConfig.Preset` (including
13
15
  * pgServices, grafserv options, grafast context, etc.) before passing it here.
16
+ *
17
+ * When `enableRealtime` is true, a RealtimeManager is created that bridges
18
+ * cursor-tracked events from `drain_changes()` into the PostGraphile
19
+ * instance's PgSubscriber EventEmitter. Both `pgSubscriber` and the pg
20
+ * pool are extracted from the resolved preset's pgServices — no separate
21
+ * pool parameter is needed.
14
22
  */
15
23
  export const createGraphileInstance = async (opts) => {
16
- const { preset, cacheKey } = opts;
24
+ const { preset, cacheKey, enableRealtime = false } = opts;
17
25
  const pgl = postgraphile(preset);
18
26
  const serv = pgl.createServ(grafserv);
19
27
  const handler = express();
20
28
  const httpServer = createServer(handler);
21
29
  await serv.addTo(handler, httpServer);
22
30
  await serv.ready();
23
- return {
31
+ const entry = {
24
32
  pgl,
25
33
  serv,
26
34
  handler,
@@ -28,4 +36,37 @@ export const createGraphileInstance = async (opts) => {
28
36
  cacheKey,
29
37
  createdAt: Date.now(),
30
38
  };
39
+ if (enableRealtime) {
40
+ try {
41
+ const { RealtimeManager } = await import('graphile-realtime-subscriptions');
42
+ // Extract PgSubscriber and pool from the resolved preset's pgServices.
43
+ // The pool is the same instance managed by pg-cache (via getPgPool)
44
+ // and threaded into the preset by makePgService({ pool, schemas }).
45
+ const resolvedPreset = pgl.getResolvedPreset();
46
+ const pgService = resolvedPreset.pgServices?.[0];
47
+ const pgSubscriber = pgService?.pgSubscriber ?? null;
48
+ const pool = pgService?.adaptorSettings?.pool ?? null;
49
+ if (!pgSubscriber) {
50
+ log.warn(`PostGraphile[${cacheKey}] has no pgSubscriber — RealtimeManager will not be started`);
51
+ }
52
+ else if (!pool) {
53
+ log.warn(`PostGraphile[${cacheKey}] has no pool in pgService — RealtimeManager will not be started`);
54
+ }
55
+ else {
56
+ const manager = new RealtimeManager({
57
+ pgSubscriber,
58
+ pool,
59
+ nodeId: `graphile-cache:${cacheKey}`,
60
+ schema: 'realtime_public',
61
+ });
62
+ await manager.start();
63
+ entry.realtimeManager = manager;
64
+ log.info(`RealtimeManager started for PostGraphile[${cacheKey}]`);
65
+ }
66
+ }
67
+ catch (err) {
68
+ log.error(`Failed to start RealtimeManager for PostGraphile[${cacheKey}]:`, err);
69
+ }
70
+ }
71
+ return entry;
31
72
  };
@@ -66,6 +66,15 @@ const disposeEntry = async (entry, key) => {
66
66
  entry.httpServer.close(() => resolve());
67
67
  });
68
68
  }
69
+ // Stop RealtimeManager if present (before releasing PostGraphile)
70
+ if (entry.realtimeManager) {
71
+ try {
72
+ await entry.realtimeManager.stop();
73
+ }
74
+ catch (err) {
75
+ log.error(`Error stopping RealtimeManager for PostGraphile[${key}]:`, err);
76
+ }
77
+ }
69
78
  // Release PostGraphile instance (this also releases grafserv internally)
70
79
  if (entry.pgl) {
71
80
  await entry.pgl.release();
@@ -49,6 +49,10 @@ export interface GraphileCacheEntry {
49
49
  httpServer: HttpServer;
50
50
  cacheKey: string;
51
51
  createdAt: number;
52
+ /** Optional RealtimeManager for cursor-tracked subscription delivery */
53
+ realtimeManager?: {
54
+ stop(): Promise<void>;
55
+ } | null;
52
56
  }
53
57
  export declare const graphileCache: LRUCache<string, GraphileCacheEntry, unknown>;
54
58
  export interface CacheStats {
package/graphile-cache.js CHANGED
@@ -73,6 +73,15 @@ const disposeEntry = async (entry, key) => {
73
73
  entry.httpServer.close(() => resolve());
74
74
  });
75
75
  }
76
+ // Stop RealtimeManager if present (before releasing PostGraphile)
77
+ if (entry.realtimeManager) {
78
+ try {
79
+ await entry.realtimeManager.stop();
80
+ }
81
+ catch (err) {
82
+ log.error(`Error stopping RealtimeManager for PostGraphile[${key}]:`, err);
83
+ }
84
+ }
76
85
  // Release PostGraphile instance (this also releases grafserv internally)
77
86
  if (entry.pgl) {
78
87
  await entry.pgl.release();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-cache",
3
- "version": "3.8.0",
3
+ "version": "3.10.1",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PostGraphile v5 LRU cache with automatic pool cleanup integration",
6
6
  "main": "index.js",
@@ -29,11 +29,12 @@
29
29
  "test:watch": "jest --watch"
30
30
  },
31
31
  "dependencies": {
32
- "@pgpmjs/logger": "^2.9.0",
32
+ "@pgpmjs/logger": "^2.10.1",
33
33
  "express": "^5.2.1",
34
34
  "grafserv": "1.0.0",
35
+ "graphile-realtime-subscriptions": "^0.5.2",
35
36
  "lru-cache": "^11.2.7",
36
- "pg-cache": "^3.8.0",
37
+ "pg-cache": "^3.9.1",
37
38
  "postgraphile": "5.0.0"
38
39
  },
39
40
  "devDependencies": {
@@ -51,5 +52,5 @@
51
52
  "constructive",
52
53
  "v5"
53
54
  ],
54
- "gitHead": "0538fe39630e6bafd228cb3f2b114fff4f9a61aa"
55
+ "gitHead": "d43f1d7e38cc39a71b4f8d2cafb39f46df12e31a"
55
56
  }