@refpool/nestjs 0.1.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.
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/index.cjs +230 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +92 -0
- package/dist/index.d.ts +92 -0
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atul Singh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# @refpool/nestjs
|
|
2
|
+
|
|
3
|
+
NestJS integration for [`@refpool/core`](../core): a dynamic module, an injectable
|
|
4
|
+
service with lifecycle hooks, multi-tenant connection middleware, and a
|
|
5
|
+
connection-health controller.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @refpool/nestjs @refpool/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peer dependencies (required): `@nestjs/common`, `@nestjs/core`,
|
|
14
|
+
`reflect-metadata`, `rxjs`.
|
|
15
|
+
|
|
16
|
+
## Module wiring
|
|
17
|
+
|
|
18
|
+
`RefPoolModule.forRoot` takes the same [`PoolOptions`](../core#pooloptionst) as the
|
|
19
|
+
core pool, plus a few module-level flags (`isGlobal`, `warmOnInit`, `middleware`).
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Module } from '@nestjs/common';
|
|
23
|
+
import { RefPoolModule } from '@refpool/nestjs';
|
|
24
|
+
import { Pool } from 'pg';
|
|
25
|
+
|
|
26
|
+
@Module({
|
|
27
|
+
imports: [
|
|
28
|
+
RefPoolModule.forRoot<Pool>({
|
|
29
|
+
isGlobal: true,
|
|
30
|
+
max: 20,
|
|
31
|
+
idleTtlMs: 60_000,
|
|
32
|
+
factory: async (tenantId) =>
|
|
33
|
+
new Pool({ connectionString: `postgres://localhost:5432/tenant_${tenantId}` }),
|
|
34
|
+
dispose: async (pool) => pool.end(),
|
|
35
|
+
middleware: { header: 'x-tenant-id', onMissing: 'error' },
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
})
|
|
39
|
+
export class AppModule {}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Async configuration (inject config/other providers):
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
RefPoolModule.forRootAsync<Pool>({
|
|
46
|
+
inject: [ConfigService],
|
|
47
|
+
useFactory: (config: ConfigService) => ({
|
|
48
|
+
max: config.get('REFPOOL_MAX', 20),
|
|
49
|
+
factory: async (tenantId) => new Pool({ connectionString: config.urlFor(tenantId) }),
|
|
50
|
+
dispose: async (pool) => pool.end(),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`RefPoolService` starts the pool on `OnModuleInit` (and runs `warm()` when
|
|
56
|
+
`prewarm` is set or `warmOnInit: true`) and drains it on `OnApplicationShutdown`.
|
|
57
|
+
Enable shutdown hooks in `main.ts` with `app.enableShutdownHooks()`.
|
|
58
|
+
|
|
59
|
+
## RefPoolService
|
|
60
|
+
|
|
61
|
+
Inject it anywhere to use the pool:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { Injectable } from '@nestjs/common';
|
|
65
|
+
import { RefPoolService } from '@refpool/nestjs';
|
|
66
|
+
import type { Pool } from 'pg';
|
|
67
|
+
|
|
68
|
+
@Injectable()
|
|
69
|
+
export class ReportService {
|
|
70
|
+
constructor(private readonly refpool: RefPoolService<Pool>) {}
|
|
71
|
+
|
|
72
|
+
run(tenantId: string) {
|
|
73
|
+
// acquire → run → release (released even on throw)
|
|
74
|
+
return this.refpool.withResource(tenantId, (pool) => pool.query('select now()'));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
stats() {
|
|
78
|
+
return this.refpool.getStats();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`RefPoolService<T>` exposes `acquire(key)`, `withResource(key, fn)`, `getStats()`,
|
|
84
|
+
and the underlying `.pool` (for event subscription, etc.).
|
|
85
|
+
|
|
86
|
+
## Tenant middleware
|
|
87
|
+
|
|
88
|
+
`TenantConnectionMiddleware` reads the tenant key from a request header
|
|
89
|
+
(default `x-tenant-id`), acquires that tenant's resource for the lifetime of the
|
|
90
|
+
request, and releases it when the response finishes/closes. The resource is
|
|
91
|
+
attached to the request (default property `tenantResource`, plus
|
|
92
|
+
`tenantResourceKey`).
|
|
93
|
+
|
|
94
|
+
### Drop-in (auto-applied)
|
|
95
|
+
|
|
96
|
+
`RefPoolModule` implements `NestModule` and wires the middleware for you when you
|
|
97
|
+
opt in via `applyMiddleware: true` or `middleware.routes`. No `configure()` in
|
|
98
|
+
your `AppModule` required:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
@Module({
|
|
102
|
+
imports: [
|
|
103
|
+
RefPoolModule.forRoot<Pool>({
|
|
104
|
+
max: 20,
|
|
105
|
+
factory: async (tenantId) => new Pool({ /* ... */ }),
|
|
106
|
+
// Opt in to auto-wiring. Either flag works:
|
|
107
|
+
applyMiddleware: true, // applies to '*' (all routes)
|
|
108
|
+
middleware: {
|
|
109
|
+
header: 'x-tenant-id',
|
|
110
|
+
onMissing: 'error',
|
|
111
|
+
routes: ['tenant/*'], // or scope to specific routes
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
],
|
|
115
|
+
})
|
|
116
|
+
export class AppModule {}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`applyMiddleware` defaults to `true` when `middleware.routes` is set, otherwise
|
|
120
|
+
`false`. When enabled without explicit `routes`, the middleware is applied to
|
|
121
|
+
`'*'`.
|
|
122
|
+
|
|
123
|
+
### Manual wiring
|
|
124
|
+
|
|
125
|
+
You can still apply it yourself for full control over the route configuration:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
|
129
|
+
import { TenantConnectionMiddleware } from '@refpool/nestjs';
|
|
130
|
+
|
|
131
|
+
@Module({ /* imports: [RefPoolModule.forRoot(...)] */ })
|
|
132
|
+
export class AppModule implements NestModule {
|
|
133
|
+
configure(consumer: MiddlewareConsumer) {
|
|
134
|
+
consumer.apply(TenantConnectionMiddleware).forRoutes('tenant/*');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Middleware options (set via `forRoot({ middleware })`): `header`,
|
|
140
|
+
`onMissing` (`'next' | 'error'`), `requestProperty`, `routes`. Plus the
|
|
141
|
+
module-level `applyMiddleware` flag.
|
|
142
|
+
|
|
143
|
+
## Health route
|
|
144
|
+
|
|
145
|
+
`RefPoolModule` automatically mounts `ConnectionHealthController`, exposing:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
GET /health/connections → PoolStats (live, idle, inUse, waiters, hits, …)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Exports
|
|
152
|
+
|
|
153
|
+
`RefPoolModule`, `RefPoolService`, `TenantConnectionMiddleware`,
|
|
154
|
+
`ConnectionHealthController`, the DI tokens `REFPOOL_OPTIONS` /
|
|
155
|
+
`REFPOOL_MIDDLEWARE_OPTIONS`, and the option types.
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT © Atul Singh
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result) __defProp(target, key, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
ConnectionHealthController: () => ConnectionHealthController,
|
|
33
|
+
REFPOOL_MIDDLEWARE_OPTIONS: () => REFPOOL_MIDDLEWARE_OPTIONS,
|
|
34
|
+
REFPOOL_OPTIONS: () => REFPOOL_OPTIONS,
|
|
35
|
+
RefPoolModule: () => RefPoolModule,
|
|
36
|
+
RefPoolService: () => RefPoolService,
|
|
37
|
+
TenantConnectionMiddleware: () => TenantConnectionMiddleware
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/ref-pool.module.ts
|
|
42
|
+
var import_common4 = require("@nestjs/common");
|
|
43
|
+
|
|
44
|
+
// src/health.controller.ts
|
|
45
|
+
var import_common2 = require("@nestjs/common");
|
|
46
|
+
|
|
47
|
+
// src/ref-pool.service.ts
|
|
48
|
+
var import_common = require("@nestjs/common");
|
|
49
|
+
var import_core = require("@refpool/core");
|
|
50
|
+
|
|
51
|
+
// src/tokens.ts
|
|
52
|
+
var REFPOOL_OPTIONS = /* @__PURE__ */ Symbol.for("refpool:options");
|
|
53
|
+
var REFPOOL_MIDDLEWARE_OPTIONS = /* @__PURE__ */ Symbol.for("refpool:middleware-options");
|
|
54
|
+
|
|
55
|
+
// src/ref-pool.service.ts
|
|
56
|
+
var RefPoolService = class {
|
|
57
|
+
constructor(options) {
|
|
58
|
+
this.options = options;
|
|
59
|
+
this.pool = new import_core.RefCountedLruPool(options);
|
|
60
|
+
}
|
|
61
|
+
async onModuleInit() {
|
|
62
|
+
this.pool.start();
|
|
63
|
+
const shouldWarm = this.options.warmOnInit ?? Boolean(this.options.prewarm);
|
|
64
|
+
if (shouldWarm) {
|
|
65
|
+
await this.pool.warm();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async onApplicationShutdown() {
|
|
69
|
+
await this.pool.stop();
|
|
70
|
+
}
|
|
71
|
+
acquire(key) {
|
|
72
|
+
return this.pool.acquire(key);
|
|
73
|
+
}
|
|
74
|
+
/** Acquire a resource for `key`, run `fn`, and release the handle regardless of outcome. */
|
|
75
|
+
async withResource(key, fn) {
|
|
76
|
+
const handle = await this.pool.acquire(key);
|
|
77
|
+
try {
|
|
78
|
+
return await fn(handle.resource);
|
|
79
|
+
} finally {
|
|
80
|
+
handle.release();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
getStats() {
|
|
84
|
+
return this.pool.getStats();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
RefPoolService = __decorateClass([
|
|
88
|
+
(0, import_common.Injectable)(),
|
|
89
|
+
__decorateParam(0, (0, import_common.Inject)(REFPOOL_OPTIONS))
|
|
90
|
+
], RefPoolService);
|
|
91
|
+
|
|
92
|
+
// src/health.controller.ts
|
|
93
|
+
var ConnectionHealthController = class {
|
|
94
|
+
constructor(service) {
|
|
95
|
+
this.service = service;
|
|
96
|
+
}
|
|
97
|
+
connections() {
|
|
98
|
+
return this.service.getStats();
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
__decorateClass([
|
|
102
|
+
(0, import_common2.Get)("connections")
|
|
103
|
+
], ConnectionHealthController.prototype, "connections", 1);
|
|
104
|
+
ConnectionHealthController = __decorateClass([
|
|
105
|
+
(0, import_common2.Controller)("health"),
|
|
106
|
+
__decorateParam(0, (0, import_common2.Inject)(RefPoolService))
|
|
107
|
+
], ConnectionHealthController);
|
|
108
|
+
|
|
109
|
+
// src/tenant-connection.middleware.ts
|
|
110
|
+
var import_common3 = require("@nestjs/common");
|
|
111
|
+
var DEFAULT_HEADER = "x-tenant-id";
|
|
112
|
+
var DEFAULT_PROPERTY = "tenantResource";
|
|
113
|
+
var TenantConnectionMiddleware = class {
|
|
114
|
+
constructor(service, options) {
|
|
115
|
+
this.service = service;
|
|
116
|
+
this.options = options;
|
|
117
|
+
}
|
|
118
|
+
async use(req, res, next) {
|
|
119
|
+
const header = (this.options.header ?? DEFAULT_HEADER).toLowerCase();
|
|
120
|
+
const property = this.options.requestProperty ?? DEFAULT_PROPERTY;
|
|
121
|
+
const onMissing = this.options.onMissing ?? "next";
|
|
122
|
+
const raw = req.headers?.[header];
|
|
123
|
+
const tenantId = Array.isArray(raw) ? raw[0] : raw;
|
|
124
|
+
if (!tenantId) {
|
|
125
|
+
if (onMissing === "error") {
|
|
126
|
+
next(new Error(`Missing required tenant header "${header}"`));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
next();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
let handle;
|
|
133
|
+
try {
|
|
134
|
+
handle = await this.service.acquire(tenantId);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
next(error);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
let released = false;
|
|
140
|
+
const release = () => {
|
|
141
|
+
if (released) return;
|
|
142
|
+
released = true;
|
|
143
|
+
handle.release();
|
|
144
|
+
};
|
|
145
|
+
res.on("finish", release);
|
|
146
|
+
res.on("close", release);
|
|
147
|
+
req[property] = handle.resource;
|
|
148
|
+
req[`${property}Key`] = tenantId;
|
|
149
|
+
next();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
TenantConnectionMiddleware = __decorateClass([
|
|
153
|
+
(0, import_common3.Injectable)(),
|
|
154
|
+
__decorateParam(0, (0, import_common3.Inject)(RefPoolService)),
|
|
155
|
+
__decorateParam(1, (0, import_common3.Inject)(REFPOOL_MIDDLEWARE_OPTIONS))
|
|
156
|
+
], TenantConnectionMiddleware);
|
|
157
|
+
|
|
158
|
+
// src/ref-pool.module.ts
|
|
159
|
+
var SHARED = [RefPoolService, TenantConnectionMiddleware];
|
|
160
|
+
var EXPORTS = [
|
|
161
|
+
RefPoolService,
|
|
162
|
+
TenantConnectionMiddleware,
|
|
163
|
+
REFPOOL_OPTIONS,
|
|
164
|
+
REFPOOL_MIDDLEWARE_OPTIONS
|
|
165
|
+
];
|
|
166
|
+
var RefPoolModule = class {
|
|
167
|
+
constructor(options) {
|
|
168
|
+
this.options = options;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Auto-wires {@link TenantConnectionMiddleware} when the drop-in multi-tenant
|
|
172
|
+
* path is opted into via `applyMiddleware` or `middleware.routes`. Defaults to
|
|
173
|
+
* `'*'` (all routes) when enabled without explicit routes.
|
|
174
|
+
*/
|
|
175
|
+
configure(consumer) {
|
|
176
|
+
const routes = this.options.middleware?.routes;
|
|
177
|
+
const shouldApply = this.options.applyMiddleware ?? routes !== void 0;
|
|
178
|
+
if (!shouldApply) return;
|
|
179
|
+
const forRoutes = routes === void 0 ? ["*"] : Array.isArray(routes) ? routes : [routes];
|
|
180
|
+
consumer.apply(TenantConnectionMiddleware).forRoutes(...forRoutes);
|
|
181
|
+
}
|
|
182
|
+
static forRoot(options) {
|
|
183
|
+
const providers = [
|
|
184
|
+
{ provide: REFPOOL_OPTIONS, useValue: options },
|
|
185
|
+
{ provide: REFPOOL_MIDDLEWARE_OPTIONS, useValue: options.middleware ?? {} },
|
|
186
|
+
...SHARED
|
|
187
|
+
];
|
|
188
|
+
return {
|
|
189
|
+
module: RefPoolModule,
|
|
190
|
+
global: options.isGlobal ?? false,
|
|
191
|
+
providers,
|
|
192
|
+
controllers: [ConnectionHealthController],
|
|
193
|
+
exports: EXPORTS
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
static forRootAsync(options) {
|
|
197
|
+
const optionsProvider = {
|
|
198
|
+
provide: REFPOOL_OPTIONS,
|
|
199
|
+
useFactory: options.useFactory,
|
|
200
|
+
inject: options.inject ?? []
|
|
201
|
+
};
|
|
202
|
+
const middlewareOptionsProvider = {
|
|
203
|
+
provide: REFPOOL_MIDDLEWARE_OPTIONS,
|
|
204
|
+
useFactory: (resolved) => resolved.middleware ?? {},
|
|
205
|
+
inject: [REFPOOL_OPTIONS]
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
module: RefPoolModule,
|
|
209
|
+
global: options.isGlobal ?? false,
|
|
210
|
+
imports: options.imports ?? [],
|
|
211
|
+
providers: [optionsProvider, middlewareOptionsProvider, ...SHARED],
|
|
212
|
+
controllers: [ConnectionHealthController],
|
|
213
|
+
exports: EXPORTS
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
RefPoolModule = __decorateClass([
|
|
218
|
+
(0, import_common4.Module)({}),
|
|
219
|
+
__decorateParam(0, (0, import_common4.Inject)(REFPOOL_OPTIONS))
|
|
220
|
+
], RefPoolModule);
|
|
221
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
222
|
+
0 && (module.exports = {
|
|
223
|
+
ConnectionHealthController,
|
|
224
|
+
REFPOOL_MIDDLEWARE_OPTIONS,
|
|
225
|
+
REFPOOL_OPTIONS,
|
|
226
|
+
RefPoolModule,
|
|
227
|
+
RefPoolService,
|
|
228
|
+
TenantConnectionMiddleware
|
|
229
|
+
});
|
|
230
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ref-pool.module.ts","../src/health.controller.ts","../src/ref-pool.service.ts","../src/tokens.ts","../src/tenant-connection.middleware.ts"],"sourcesContent":["export { RefPoolModule } from './ref-pool.module.js';\nexport { RefPoolService } from './ref-pool.service.js';\nexport { TenantConnectionMiddleware } from './tenant-connection.middleware.js';\nexport { ConnectionHealthController } from './health.controller.js';\nexport { REFPOOL_OPTIONS, REFPOOL_MIDDLEWARE_OPTIONS } from './tokens.js';\nexport type {\n RefPoolModuleOptions,\n RefPoolModuleAsyncOptions,\n TenantMiddlewareOptions,\n MissingTenantBehavior,\n} from './interfaces.js';\n","import { Inject, Module } from '@nestjs/common';\nimport type {\n DynamicModule,\n MiddlewareConsumer,\n NestModule,\n Provider,\n} from '@nestjs/common';\nimport { ConnectionHealthController } from './health.controller.js';\nimport { RefPoolService } from './ref-pool.service.js';\nimport { TenantConnectionMiddleware } from './tenant-connection.middleware.js';\nimport { REFPOOL_MIDDLEWARE_OPTIONS, REFPOOL_OPTIONS } from './tokens.js';\nimport type {\n RefPoolModuleAsyncOptions,\n RefPoolModuleOptions,\n TenantMiddlewareOptions,\n} from './interfaces.js';\n\nconst SHARED = [RefPoolService, TenantConnectionMiddleware];\nconst EXPORTS = [\n RefPoolService,\n TenantConnectionMiddleware,\n REFPOOL_OPTIONS,\n REFPOOL_MIDDLEWARE_OPTIONS,\n];\n\n@Module({})\nexport class RefPoolModule implements NestModule {\n constructor(\n @Inject(REFPOOL_OPTIONS) private readonly options: RefPoolModuleOptions,\n ) {}\n\n /**\n * Auto-wires {@link TenantConnectionMiddleware} when the drop-in multi-tenant\n * path is opted into via `applyMiddleware` or `middleware.routes`. Defaults to\n * `'*'` (all routes) when enabled without explicit routes.\n */\n configure(consumer: MiddlewareConsumer): void {\n const routes = this.options.middleware?.routes;\n const shouldApply = this.options.applyMiddleware ?? routes !== undefined;\n if (!shouldApply) return;\n const forRoutes = routes === undefined ? ['*'] : Array.isArray(routes) ? routes : [routes];\n consumer.apply(TenantConnectionMiddleware).forRoutes(...forRoutes);\n }\n\n static forRoot<T = unknown>(options: RefPoolModuleOptions<T>): DynamicModule {\n const providers: Provider[] = [\n { provide: REFPOOL_OPTIONS, useValue: options },\n { provide: REFPOOL_MIDDLEWARE_OPTIONS, useValue: options.middleware ?? {} },\n ...SHARED,\n ];\n return {\n module: RefPoolModule,\n global: options.isGlobal ?? false,\n providers,\n controllers: [ConnectionHealthController],\n exports: EXPORTS,\n };\n }\n\n static forRootAsync<T = unknown>(options: RefPoolModuleAsyncOptions<T>): DynamicModule {\n const optionsProvider: Provider = {\n provide: REFPOOL_OPTIONS,\n useFactory: options.useFactory,\n inject: options.inject ?? [],\n };\n const middlewareOptionsProvider: Provider = {\n provide: REFPOOL_MIDDLEWARE_OPTIONS,\n useFactory: (resolved: RefPoolModuleOptions<T>): TenantMiddlewareOptions =>\n resolved.middleware ?? {},\n inject: [REFPOOL_OPTIONS],\n };\n return {\n module: RefPoolModule,\n global: options.isGlobal ?? false,\n imports: options.imports ?? [],\n providers: [optionsProvider, middlewareOptionsProvider, ...SHARED],\n controllers: [ConnectionHealthController],\n exports: EXPORTS,\n };\n }\n}\n","import { Controller, Get, Inject } from '@nestjs/common';\nimport type { PoolStats } from '@refpool/core';\nimport { RefPoolService } from './ref-pool.service.js';\n\n@Controller('health')\nexport class ConnectionHealthController {\n constructor(@Inject(RefPoolService) private readonly service: RefPoolService) {}\n\n @Get('connections')\n connections(): PoolStats {\n return this.service.getStats();\n }\n}\n","import { Inject, Injectable } from '@nestjs/common';\nimport type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';\nimport { RefCountedLruPool } from '@refpool/core';\nimport type { AcquireHandle, PoolStats } from '@refpool/core';\nimport { REFPOOL_OPTIONS } from './tokens.js';\nimport type { RefPoolModuleOptions } from './interfaces.js';\n\n@Injectable()\nexport class RefPoolService<T = unknown> implements OnModuleInit, OnApplicationShutdown {\n /** The underlying pool. Exposed for advanced use (event subscription, etc.). */\n readonly pool: RefCountedLruPool<T>;\n\n constructor(@Inject(REFPOOL_OPTIONS) private readonly options: RefPoolModuleOptions<T>) {\n this.pool = new RefCountedLruPool<T>(options);\n }\n\n async onModuleInit(): Promise<void> {\n this.pool.start();\n const shouldWarm = this.options.warmOnInit ?? Boolean(this.options.prewarm);\n if (shouldWarm) {\n await this.pool.warm();\n }\n }\n\n async onApplicationShutdown(): Promise<void> {\n await this.pool.stop();\n }\n\n acquire(key: string): Promise<AcquireHandle<T>> {\n return this.pool.acquire(key);\n }\n\n /** Acquire a resource for `key`, run `fn`, and release the handle regardless of outcome. */\n async withResource<R>(key: string, fn: (resource: T) => R | Promise<R>): Promise<R> {\n const handle = await this.pool.acquire(key);\n try {\n return await fn(handle.resource);\n } finally {\n handle.release();\n }\n }\n\n getStats(): PoolStats {\n return this.pool.getStats();\n }\n}\n","/** DI token holding the resolved {@link RefPoolModuleOptions}. */\nexport const REFPOOL_OPTIONS = Symbol.for('refpool:options');\n\n/** DI token holding the resolved {@link TenantMiddlewareOptions}. */\nexport const REFPOOL_MIDDLEWARE_OPTIONS = Symbol.for('refpool:middleware-options');\n","import { Inject, Injectable } from '@nestjs/common';\nimport type { NestMiddleware } from '@nestjs/common';\nimport type { AcquireHandle } from '@refpool/core';\nimport { RefPoolService } from './ref-pool.service.js';\nimport { REFPOOL_MIDDLEWARE_OPTIONS } from './tokens.js';\nimport type { TenantMiddlewareOptions } from './interfaces.js';\n\nconst DEFAULT_HEADER = 'x-tenant-id';\nconst DEFAULT_PROPERTY = 'tenantResource';\n\ninterface MinimalRequest {\n headers?: Record<string, string | string[] | undefined>;\n [key: string]: unknown;\n}\n\ninterface MinimalResponse {\n on(event: string, listener: () => void): unknown;\n}\n\ntype NextFn = (error?: unknown) => void;\n\n@Injectable()\nexport class TenantConnectionMiddleware implements NestMiddleware {\n constructor(\n @Inject(RefPoolService) private readonly service: RefPoolService,\n @Inject(REFPOOL_MIDDLEWARE_OPTIONS) private readonly options: TenantMiddlewareOptions,\n ) {}\n\n async use(req: MinimalRequest, res: MinimalResponse, next: NextFn): Promise<void> {\n const header = (this.options.header ?? DEFAULT_HEADER).toLowerCase();\n const property = this.options.requestProperty ?? DEFAULT_PROPERTY;\n const onMissing = this.options.onMissing ?? 'next';\n\n const raw = req.headers?.[header];\n const tenantId = Array.isArray(raw) ? raw[0] : raw;\n\n if (!tenantId) {\n if (onMissing === 'error') {\n next(new Error(`Missing required tenant header \"${header}\"`));\n return;\n }\n next();\n return;\n }\n\n let handle: AcquireHandle<unknown>;\n try {\n handle = await this.service.acquire(tenantId);\n } catch (error) {\n next(error);\n return;\n }\n\n let released = false;\n const release = (): void => {\n if (released) return;\n released = true;\n handle.release();\n };\n res.on('finish', release);\n res.on('close', release);\n\n req[property] = handle.resource;\n req[`${property}Key`] = tenantId;\n next();\n }\n}\n"],"mappings":"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","names":["import_common","import_common","import_common"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ModuleMetadata, InjectionToken, OptionalFactoryDependency, NestModule, MiddlewareConsumer, DynamicModule, OnModuleInit, OnApplicationShutdown, NestMiddleware } from '@nestjs/common';
|
|
2
|
+
import { PoolOptions, RefCountedLruPool, AcquireHandle, PoolStats } from '@refpool/core';
|
|
3
|
+
|
|
4
|
+
/** Behavior when an incoming request lacks the configured tenant header. */
|
|
5
|
+
type MissingTenantBehavior = 'next' | 'error';
|
|
6
|
+
interface TenantMiddlewareOptions {
|
|
7
|
+
/** Request header carrying the tenant key. Default `x-tenant-id`. */
|
|
8
|
+
header?: string;
|
|
9
|
+
/** What to do when the header is absent. Default `next` (skip acquisition). */
|
|
10
|
+
onMissing?: MissingTenantBehavior;
|
|
11
|
+
/** Property on the request used to attach the acquired resource. Default `tenantResource`. */
|
|
12
|
+
requestProperty?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Routes to auto-apply {@link TenantConnectionMiddleware} to. Setting this
|
|
15
|
+
* opts into wiring; omit it (or pass `applyMiddleware`) to fall back to `'*'`.
|
|
16
|
+
*/
|
|
17
|
+
routes?: string | string[];
|
|
18
|
+
}
|
|
19
|
+
interface RefPoolModuleOptions<T = unknown> extends PoolOptions<T> {
|
|
20
|
+
/** Register the module globally so its exports are available app-wide. */
|
|
21
|
+
isGlobal?: boolean;
|
|
22
|
+
/** Run `pool.warm()` during `OnModuleInit`. Defaults to `true` when `prewarm` is set. */
|
|
23
|
+
warmOnInit?: boolean;
|
|
24
|
+
/** Defaults for {@link TenantConnectionMiddleware}. */
|
|
25
|
+
middleware?: TenantMiddlewareOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Auto-apply {@link TenantConnectionMiddleware} for the whole app. Defaults to
|
|
28
|
+
* `true` when `middleware.routes` is set, otherwise `false`. When enabled
|
|
29
|
+
* without explicit `routes`, the middleware is applied to `'*'`.
|
|
30
|
+
*/
|
|
31
|
+
applyMiddleware?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface RefPoolModuleAsyncOptions<T = unknown> extends Pick<ModuleMetadata, 'imports'> {
|
|
34
|
+
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
35
|
+
useFactory: (...args: any[]) => RefPoolModuleOptions<T> | Promise<RefPoolModuleOptions<T>>;
|
|
36
|
+
/** Register the module globally so its exports are available app-wide. */
|
|
37
|
+
isGlobal?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
declare class RefPoolModule implements NestModule {
|
|
41
|
+
private readonly options;
|
|
42
|
+
constructor(options: RefPoolModuleOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Auto-wires {@link TenantConnectionMiddleware} when the drop-in multi-tenant
|
|
45
|
+
* path is opted into via `applyMiddleware` or `middleware.routes`. Defaults to
|
|
46
|
+
* `'*'` (all routes) when enabled without explicit routes.
|
|
47
|
+
*/
|
|
48
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
49
|
+
static forRoot<T = unknown>(options: RefPoolModuleOptions<T>): DynamicModule;
|
|
50
|
+
static forRootAsync<T = unknown>(options: RefPoolModuleAsyncOptions<T>): DynamicModule;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class RefPoolService<T = unknown> implements OnModuleInit, OnApplicationShutdown {
|
|
54
|
+
private readonly options;
|
|
55
|
+
/** The underlying pool. Exposed for advanced use (event subscription, etc.). */
|
|
56
|
+
readonly pool: RefCountedLruPool<T>;
|
|
57
|
+
constructor(options: RefPoolModuleOptions<T>);
|
|
58
|
+
onModuleInit(): Promise<void>;
|
|
59
|
+
onApplicationShutdown(): Promise<void>;
|
|
60
|
+
acquire(key: string): Promise<AcquireHandle<T>>;
|
|
61
|
+
/** Acquire a resource for `key`, run `fn`, and release the handle regardless of outcome. */
|
|
62
|
+
withResource<R>(key: string, fn: (resource: T) => R | Promise<R>): Promise<R>;
|
|
63
|
+
getStats(): PoolStats;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface MinimalRequest {
|
|
67
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
interface MinimalResponse {
|
|
71
|
+
on(event: string, listener: () => void): unknown;
|
|
72
|
+
}
|
|
73
|
+
type NextFn = (error?: unknown) => void;
|
|
74
|
+
declare class TenantConnectionMiddleware implements NestMiddleware {
|
|
75
|
+
private readonly service;
|
|
76
|
+
private readonly options;
|
|
77
|
+
constructor(service: RefPoolService, options: TenantMiddlewareOptions);
|
|
78
|
+
use(req: MinimalRequest, res: MinimalResponse, next: NextFn): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
declare class ConnectionHealthController {
|
|
82
|
+
private readonly service;
|
|
83
|
+
constructor(service: RefPoolService);
|
|
84
|
+
connections(): PoolStats;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** DI token holding the resolved {@link RefPoolModuleOptions}. */
|
|
88
|
+
declare const REFPOOL_OPTIONS: unique symbol;
|
|
89
|
+
/** DI token holding the resolved {@link TenantMiddlewareOptions}. */
|
|
90
|
+
declare const REFPOOL_MIDDLEWARE_OPTIONS: unique symbol;
|
|
91
|
+
|
|
92
|
+
export { ConnectionHealthController, type MissingTenantBehavior, REFPOOL_MIDDLEWARE_OPTIONS, REFPOOL_OPTIONS, RefPoolModule, type RefPoolModuleAsyncOptions, type RefPoolModuleOptions, RefPoolService, TenantConnectionMiddleware, type TenantMiddlewareOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ModuleMetadata, InjectionToken, OptionalFactoryDependency, NestModule, MiddlewareConsumer, DynamicModule, OnModuleInit, OnApplicationShutdown, NestMiddleware } from '@nestjs/common';
|
|
2
|
+
import { PoolOptions, RefCountedLruPool, AcquireHandle, PoolStats } from '@refpool/core';
|
|
3
|
+
|
|
4
|
+
/** Behavior when an incoming request lacks the configured tenant header. */
|
|
5
|
+
type MissingTenantBehavior = 'next' | 'error';
|
|
6
|
+
interface TenantMiddlewareOptions {
|
|
7
|
+
/** Request header carrying the tenant key. Default `x-tenant-id`. */
|
|
8
|
+
header?: string;
|
|
9
|
+
/** What to do when the header is absent. Default `next` (skip acquisition). */
|
|
10
|
+
onMissing?: MissingTenantBehavior;
|
|
11
|
+
/** Property on the request used to attach the acquired resource. Default `tenantResource`. */
|
|
12
|
+
requestProperty?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Routes to auto-apply {@link TenantConnectionMiddleware} to. Setting this
|
|
15
|
+
* opts into wiring; omit it (or pass `applyMiddleware`) to fall back to `'*'`.
|
|
16
|
+
*/
|
|
17
|
+
routes?: string | string[];
|
|
18
|
+
}
|
|
19
|
+
interface RefPoolModuleOptions<T = unknown> extends PoolOptions<T> {
|
|
20
|
+
/** Register the module globally so its exports are available app-wide. */
|
|
21
|
+
isGlobal?: boolean;
|
|
22
|
+
/** Run `pool.warm()` during `OnModuleInit`. Defaults to `true` when `prewarm` is set. */
|
|
23
|
+
warmOnInit?: boolean;
|
|
24
|
+
/** Defaults for {@link TenantConnectionMiddleware}. */
|
|
25
|
+
middleware?: TenantMiddlewareOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Auto-apply {@link TenantConnectionMiddleware} for the whole app. Defaults to
|
|
28
|
+
* `true` when `middleware.routes` is set, otherwise `false`. When enabled
|
|
29
|
+
* without explicit `routes`, the middleware is applied to `'*'`.
|
|
30
|
+
*/
|
|
31
|
+
applyMiddleware?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface RefPoolModuleAsyncOptions<T = unknown> extends Pick<ModuleMetadata, 'imports'> {
|
|
34
|
+
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
35
|
+
useFactory: (...args: any[]) => RefPoolModuleOptions<T> | Promise<RefPoolModuleOptions<T>>;
|
|
36
|
+
/** Register the module globally so its exports are available app-wide. */
|
|
37
|
+
isGlobal?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
declare class RefPoolModule implements NestModule {
|
|
41
|
+
private readonly options;
|
|
42
|
+
constructor(options: RefPoolModuleOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Auto-wires {@link TenantConnectionMiddleware} when the drop-in multi-tenant
|
|
45
|
+
* path is opted into via `applyMiddleware` or `middleware.routes`. Defaults to
|
|
46
|
+
* `'*'` (all routes) when enabled without explicit routes.
|
|
47
|
+
*/
|
|
48
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
49
|
+
static forRoot<T = unknown>(options: RefPoolModuleOptions<T>): DynamicModule;
|
|
50
|
+
static forRootAsync<T = unknown>(options: RefPoolModuleAsyncOptions<T>): DynamicModule;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class RefPoolService<T = unknown> implements OnModuleInit, OnApplicationShutdown {
|
|
54
|
+
private readonly options;
|
|
55
|
+
/** The underlying pool. Exposed for advanced use (event subscription, etc.). */
|
|
56
|
+
readonly pool: RefCountedLruPool<T>;
|
|
57
|
+
constructor(options: RefPoolModuleOptions<T>);
|
|
58
|
+
onModuleInit(): Promise<void>;
|
|
59
|
+
onApplicationShutdown(): Promise<void>;
|
|
60
|
+
acquire(key: string): Promise<AcquireHandle<T>>;
|
|
61
|
+
/** Acquire a resource for `key`, run `fn`, and release the handle regardless of outcome. */
|
|
62
|
+
withResource<R>(key: string, fn: (resource: T) => R | Promise<R>): Promise<R>;
|
|
63
|
+
getStats(): PoolStats;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface MinimalRequest {
|
|
67
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
interface MinimalResponse {
|
|
71
|
+
on(event: string, listener: () => void): unknown;
|
|
72
|
+
}
|
|
73
|
+
type NextFn = (error?: unknown) => void;
|
|
74
|
+
declare class TenantConnectionMiddleware implements NestMiddleware {
|
|
75
|
+
private readonly service;
|
|
76
|
+
private readonly options;
|
|
77
|
+
constructor(service: RefPoolService, options: TenantMiddlewareOptions);
|
|
78
|
+
use(req: MinimalRequest, res: MinimalResponse, next: NextFn): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
declare class ConnectionHealthController {
|
|
82
|
+
private readonly service;
|
|
83
|
+
constructor(service: RefPoolService);
|
|
84
|
+
connections(): PoolStats;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** DI token holding the resolved {@link RefPoolModuleOptions}. */
|
|
88
|
+
declare const REFPOOL_OPTIONS: unique symbol;
|
|
89
|
+
/** DI token holding the resolved {@link TenantMiddlewareOptions}. */
|
|
90
|
+
declare const REFPOOL_MIDDLEWARE_OPTIONS: unique symbol;
|
|
91
|
+
|
|
92
|
+
export { ConnectionHealthController, type MissingTenantBehavior, REFPOOL_MIDDLEWARE_OPTIONS, REFPOOL_OPTIONS, RefPoolModule, type RefPoolModuleAsyncOptions, type RefPoolModuleOptions, RefPoolService, TenantConnectionMiddleware, type TenantMiddlewareOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// src/ref-pool.module.ts
|
|
14
|
+
import { Inject as Inject4, Module } from "@nestjs/common";
|
|
15
|
+
|
|
16
|
+
// src/health.controller.ts
|
|
17
|
+
import { Controller, Get, Inject as Inject2 } from "@nestjs/common";
|
|
18
|
+
|
|
19
|
+
// src/ref-pool.service.ts
|
|
20
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
21
|
+
import { RefCountedLruPool } from "@refpool/core";
|
|
22
|
+
|
|
23
|
+
// src/tokens.ts
|
|
24
|
+
var REFPOOL_OPTIONS = /* @__PURE__ */ Symbol.for("refpool:options");
|
|
25
|
+
var REFPOOL_MIDDLEWARE_OPTIONS = /* @__PURE__ */ Symbol.for("refpool:middleware-options");
|
|
26
|
+
|
|
27
|
+
// src/ref-pool.service.ts
|
|
28
|
+
var RefPoolService = class {
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
this.pool = new RefCountedLruPool(options);
|
|
32
|
+
}
|
|
33
|
+
async onModuleInit() {
|
|
34
|
+
this.pool.start();
|
|
35
|
+
const shouldWarm = this.options.warmOnInit ?? Boolean(this.options.prewarm);
|
|
36
|
+
if (shouldWarm) {
|
|
37
|
+
await this.pool.warm();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async onApplicationShutdown() {
|
|
41
|
+
await this.pool.stop();
|
|
42
|
+
}
|
|
43
|
+
acquire(key) {
|
|
44
|
+
return this.pool.acquire(key);
|
|
45
|
+
}
|
|
46
|
+
/** Acquire a resource for `key`, run `fn`, and release the handle regardless of outcome. */
|
|
47
|
+
async withResource(key, fn) {
|
|
48
|
+
const handle = await this.pool.acquire(key);
|
|
49
|
+
try {
|
|
50
|
+
return await fn(handle.resource);
|
|
51
|
+
} finally {
|
|
52
|
+
handle.release();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
getStats() {
|
|
56
|
+
return this.pool.getStats();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
RefPoolService = __decorateClass([
|
|
60
|
+
Injectable(),
|
|
61
|
+
__decorateParam(0, Inject(REFPOOL_OPTIONS))
|
|
62
|
+
], RefPoolService);
|
|
63
|
+
|
|
64
|
+
// src/health.controller.ts
|
|
65
|
+
var ConnectionHealthController = class {
|
|
66
|
+
constructor(service) {
|
|
67
|
+
this.service = service;
|
|
68
|
+
}
|
|
69
|
+
connections() {
|
|
70
|
+
return this.service.getStats();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
__decorateClass([
|
|
74
|
+
Get("connections")
|
|
75
|
+
], ConnectionHealthController.prototype, "connections", 1);
|
|
76
|
+
ConnectionHealthController = __decorateClass([
|
|
77
|
+
Controller("health"),
|
|
78
|
+
__decorateParam(0, Inject2(RefPoolService))
|
|
79
|
+
], ConnectionHealthController);
|
|
80
|
+
|
|
81
|
+
// src/tenant-connection.middleware.ts
|
|
82
|
+
import { Inject as Inject3, Injectable as Injectable2 } from "@nestjs/common";
|
|
83
|
+
var DEFAULT_HEADER = "x-tenant-id";
|
|
84
|
+
var DEFAULT_PROPERTY = "tenantResource";
|
|
85
|
+
var TenantConnectionMiddleware = class {
|
|
86
|
+
constructor(service, options) {
|
|
87
|
+
this.service = service;
|
|
88
|
+
this.options = options;
|
|
89
|
+
}
|
|
90
|
+
async use(req, res, next) {
|
|
91
|
+
const header = (this.options.header ?? DEFAULT_HEADER).toLowerCase();
|
|
92
|
+
const property = this.options.requestProperty ?? DEFAULT_PROPERTY;
|
|
93
|
+
const onMissing = this.options.onMissing ?? "next";
|
|
94
|
+
const raw = req.headers?.[header];
|
|
95
|
+
const tenantId = Array.isArray(raw) ? raw[0] : raw;
|
|
96
|
+
if (!tenantId) {
|
|
97
|
+
if (onMissing === "error") {
|
|
98
|
+
next(new Error(`Missing required tenant header "${header}"`));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
next();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
let handle;
|
|
105
|
+
try {
|
|
106
|
+
handle = await this.service.acquire(tenantId);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
next(error);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let released = false;
|
|
112
|
+
const release = () => {
|
|
113
|
+
if (released) return;
|
|
114
|
+
released = true;
|
|
115
|
+
handle.release();
|
|
116
|
+
};
|
|
117
|
+
res.on("finish", release);
|
|
118
|
+
res.on("close", release);
|
|
119
|
+
req[property] = handle.resource;
|
|
120
|
+
req[`${property}Key`] = tenantId;
|
|
121
|
+
next();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
TenantConnectionMiddleware = __decorateClass([
|
|
125
|
+
Injectable2(),
|
|
126
|
+
__decorateParam(0, Inject3(RefPoolService)),
|
|
127
|
+
__decorateParam(1, Inject3(REFPOOL_MIDDLEWARE_OPTIONS))
|
|
128
|
+
], TenantConnectionMiddleware);
|
|
129
|
+
|
|
130
|
+
// src/ref-pool.module.ts
|
|
131
|
+
var SHARED = [RefPoolService, TenantConnectionMiddleware];
|
|
132
|
+
var EXPORTS = [
|
|
133
|
+
RefPoolService,
|
|
134
|
+
TenantConnectionMiddleware,
|
|
135
|
+
REFPOOL_OPTIONS,
|
|
136
|
+
REFPOOL_MIDDLEWARE_OPTIONS
|
|
137
|
+
];
|
|
138
|
+
var RefPoolModule = class {
|
|
139
|
+
constructor(options) {
|
|
140
|
+
this.options = options;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Auto-wires {@link TenantConnectionMiddleware} when the drop-in multi-tenant
|
|
144
|
+
* path is opted into via `applyMiddleware` or `middleware.routes`. Defaults to
|
|
145
|
+
* `'*'` (all routes) when enabled without explicit routes.
|
|
146
|
+
*/
|
|
147
|
+
configure(consumer) {
|
|
148
|
+
const routes = this.options.middleware?.routes;
|
|
149
|
+
const shouldApply = this.options.applyMiddleware ?? routes !== void 0;
|
|
150
|
+
if (!shouldApply) return;
|
|
151
|
+
const forRoutes = routes === void 0 ? ["*"] : Array.isArray(routes) ? routes : [routes];
|
|
152
|
+
consumer.apply(TenantConnectionMiddleware).forRoutes(...forRoutes);
|
|
153
|
+
}
|
|
154
|
+
static forRoot(options) {
|
|
155
|
+
const providers = [
|
|
156
|
+
{ provide: REFPOOL_OPTIONS, useValue: options },
|
|
157
|
+
{ provide: REFPOOL_MIDDLEWARE_OPTIONS, useValue: options.middleware ?? {} },
|
|
158
|
+
...SHARED
|
|
159
|
+
];
|
|
160
|
+
return {
|
|
161
|
+
module: RefPoolModule,
|
|
162
|
+
global: options.isGlobal ?? false,
|
|
163
|
+
providers,
|
|
164
|
+
controllers: [ConnectionHealthController],
|
|
165
|
+
exports: EXPORTS
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
static forRootAsync(options) {
|
|
169
|
+
const optionsProvider = {
|
|
170
|
+
provide: REFPOOL_OPTIONS,
|
|
171
|
+
useFactory: options.useFactory,
|
|
172
|
+
inject: options.inject ?? []
|
|
173
|
+
};
|
|
174
|
+
const middlewareOptionsProvider = {
|
|
175
|
+
provide: REFPOOL_MIDDLEWARE_OPTIONS,
|
|
176
|
+
useFactory: (resolved) => resolved.middleware ?? {},
|
|
177
|
+
inject: [REFPOOL_OPTIONS]
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
module: RefPoolModule,
|
|
181
|
+
global: options.isGlobal ?? false,
|
|
182
|
+
imports: options.imports ?? [],
|
|
183
|
+
providers: [optionsProvider, middlewareOptionsProvider, ...SHARED],
|
|
184
|
+
controllers: [ConnectionHealthController],
|
|
185
|
+
exports: EXPORTS
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
RefPoolModule = __decorateClass([
|
|
190
|
+
Module({}),
|
|
191
|
+
__decorateParam(0, Inject4(REFPOOL_OPTIONS))
|
|
192
|
+
], RefPoolModule);
|
|
193
|
+
export {
|
|
194
|
+
ConnectionHealthController,
|
|
195
|
+
REFPOOL_MIDDLEWARE_OPTIONS,
|
|
196
|
+
REFPOOL_OPTIONS,
|
|
197
|
+
RefPoolModule,
|
|
198
|
+
RefPoolService,
|
|
199
|
+
TenantConnectionMiddleware
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ref-pool.module.ts","../src/health.controller.ts","../src/ref-pool.service.ts","../src/tokens.ts","../src/tenant-connection.middleware.ts"],"sourcesContent":["import { Inject, Module } from '@nestjs/common';\nimport type {\n DynamicModule,\n MiddlewareConsumer,\n NestModule,\n Provider,\n} from '@nestjs/common';\nimport { ConnectionHealthController } from './health.controller.js';\nimport { RefPoolService } from './ref-pool.service.js';\nimport { TenantConnectionMiddleware } from './tenant-connection.middleware.js';\nimport { REFPOOL_MIDDLEWARE_OPTIONS, REFPOOL_OPTIONS } from './tokens.js';\nimport type {\n RefPoolModuleAsyncOptions,\n RefPoolModuleOptions,\n TenantMiddlewareOptions,\n} from './interfaces.js';\n\nconst SHARED = [RefPoolService, TenantConnectionMiddleware];\nconst EXPORTS = [\n RefPoolService,\n TenantConnectionMiddleware,\n REFPOOL_OPTIONS,\n REFPOOL_MIDDLEWARE_OPTIONS,\n];\n\n@Module({})\nexport class RefPoolModule implements NestModule {\n constructor(\n @Inject(REFPOOL_OPTIONS) private readonly options: RefPoolModuleOptions,\n ) {}\n\n /**\n * Auto-wires {@link TenantConnectionMiddleware} when the drop-in multi-tenant\n * path is opted into via `applyMiddleware` or `middleware.routes`. Defaults to\n * `'*'` (all routes) when enabled without explicit routes.\n */\n configure(consumer: MiddlewareConsumer): void {\n const routes = this.options.middleware?.routes;\n const shouldApply = this.options.applyMiddleware ?? routes !== undefined;\n if (!shouldApply) return;\n const forRoutes = routes === undefined ? ['*'] : Array.isArray(routes) ? routes : [routes];\n consumer.apply(TenantConnectionMiddleware).forRoutes(...forRoutes);\n }\n\n static forRoot<T = unknown>(options: RefPoolModuleOptions<T>): DynamicModule {\n const providers: Provider[] = [\n { provide: REFPOOL_OPTIONS, useValue: options },\n { provide: REFPOOL_MIDDLEWARE_OPTIONS, useValue: options.middleware ?? {} },\n ...SHARED,\n ];\n return {\n module: RefPoolModule,\n global: options.isGlobal ?? false,\n providers,\n controllers: [ConnectionHealthController],\n exports: EXPORTS,\n };\n }\n\n static forRootAsync<T = unknown>(options: RefPoolModuleAsyncOptions<T>): DynamicModule {\n const optionsProvider: Provider = {\n provide: REFPOOL_OPTIONS,\n useFactory: options.useFactory,\n inject: options.inject ?? [],\n };\n const middlewareOptionsProvider: Provider = {\n provide: REFPOOL_MIDDLEWARE_OPTIONS,\n useFactory: (resolved: RefPoolModuleOptions<T>): TenantMiddlewareOptions =>\n resolved.middleware ?? {},\n inject: [REFPOOL_OPTIONS],\n };\n return {\n module: RefPoolModule,\n global: options.isGlobal ?? false,\n imports: options.imports ?? [],\n providers: [optionsProvider, middlewareOptionsProvider, ...SHARED],\n controllers: [ConnectionHealthController],\n exports: EXPORTS,\n };\n }\n}\n","import { Controller, Get, Inject } from '@nestjs/common';\nimport type { PoolStats } from '@refpool/core';\nimport { RefPoolService } from './ref-pool.service.js';\n\n@Controller('health')\nexport class ConnectionHealthController {\n constructor(@Inject(RefPoolService) private readonly service: RefPoolService) {}\n\n @Get('connections')\n connections(): PoolStats {\n return this.service.getStats();\n }\n}\n","import { Inject, Injectable } from '@nestjs/common';\nimport type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';\nimport { RefCountedLruPool } from '@refpool/core';\nimport type { AcquireHandle, PoolStats } from '@refpool/core';\nimport { REFPOOL_OPTIONS } from './tokens.js';\nimport type { RefPoolModuleOptions } from './interfaces.js';\n\n@Injectable()\nexport class RefPoolService<T = unknown> implements OnModuleInit, OnApplicationShutdown {\n /** The underlying pool. Exposed for advanced use (event subscription, etc.). */\n readonly pool: RefCountedLruPool<T>;\n\n constructor(@Inject(REFPOOL_OPTIONS) private readonly options: RefPoolModuleOptions<T>) {\n this.pool = new RefCountedLruPool<T>(options);\n }\n\n async onModuleInit(): Promise<void> {\n this.pool.start();\n const shouldWarm = this.options.warmOnInit ?? Boolean(this.options.prewarm);\n if (shouldWarm) {\n await this.pool.warm();\n }\n }\n\n async onApplicationShutdown(): Promise<void> {\n await this.pool.stop();\n }\n\n acquire(key: string): Promise<AcquireHandle<T>> {\n return this.pool.acquire(key);\n }\n\n /** Acquire a resource for `key`, run `fn`, and release the handle regardless of outcome. */\n async withResource<R>(key: string, fn: (resource: T) => R | Promise<R>): Promise<R> {\n const handle = await this.pool.acquire(key);\n try {\n return await fn(handle.resource);\n } finally {\n handle.release();\n }\n }\n\n getStats(): PoolStats {\n return this.pool.getStats();\n }\n}\n","/** DI token holding the resolved {@link RefPoolModuleOptions}. */\nexport const REFPOOL_OPTIONS = Symbol.for('refpool:options');\n\n/** DI token holding the resolved {@link TenantMiddlewareOptions}. */\nexport const REFPOOL_MIDDLEWARE_OPTIONS = Symbol.for('refpool:middleware-options');\n","import { Inject, Injectable } from '@nestjs/common';\nimport type { NestMiddleware } from '@nestjs/common';\nimport type { AcquireHandle } from '@refpool/core';\nimport { RefPoolService } from './ref-pool.service.js';\nimport { REFPOOL_MIDDLEWARE_OPTIONS } from './tokens.js';\nimport type { TenantMiddlewareOptions } from './interfaces.js';\n\nconst DEFAULT_HEADER = 'x-tenant-id';\nconst DEFAULT_PROPERTY = 'tenantResource';\n\ninterface MinimalRequest {\n headers?: Record<string, string | string[] | undefined>;\n [key: string]: unknown;\n}\n\ninterface MinimalResponse {\n on(event: string, listener: () => void): unknown;\n}\n\ntype NextFn = (error?: unknown) => void;\n\n@Injectable()\nexport class TenantConnectionMiddleware implements NestMiddleware {\n constructor(\n @Inject(RefPoolService) private readonly service: RefPoolService,\n @Inject(REFPOOL_MIDDLEWARE_OPTIONS) private readonly options: TenantMiddlewareOptions,\n ) {}\n\n async use(req: MinimalRequest, res: MinimalResponse, next: NextFn): Promise<void> {\n const header = (this.options.header ?? DEFAULT_HEADER).toLowerCase();\n const property = this.options.requestProperty ?? DEFAULT_PROPERTY;\n const onMissing = this.options.onMissing ?? 'next';\n\n const raw = req.headers?.[header];\n const tenantId = Array.isArray(raw) ? raw[0] : raw;\n\n if (!tenantId) {\n if (onMissing === 'error') {\n next(new Error(`Missing required tenant header \"${header}\"`));\n return;\n }\n next();\n return;\n }\n\n let handle: AcquireHandle<unknown>;\n try {\n handle = await this.service.acquire(tenantId);\n } catch (error) {\n next(error);\n return;\n }\n\n let released = false;\n const release = (): void => {\n if (released) return;\n released = true;\n handle.release();\n };\n res.on('finish', release);\n res.on('close', release);\n\n req[property] = handle.resource;\n req[`${property}Key`] = tenantId;\n next();\n }\n}\n"],"mappings":"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","names":["Inject","Inject","Inject","Inject","Injectable","Injectable","Inject","Inject"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@refpool/nestjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "NestJS integration for refpool: dynamic module, injectable service, multi-tenant connection middleware, and a connection health controller.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Atul Singh <atulsingh.harsh@gmail.com> (https://atulsingh.io)",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"keywords": [
|
|
10
|
+
"nestjs",
|
|
11
|
+
"pool",
|
|
12
|
+
"resource-pool",
|
|
13
|
+
"reference-counting",
|
|
14
|
+
"multi-tenant",
|
|
15
|
+
"connection-pool",
|
|
16
|
+
"middleware"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/expedite-atul/refpool.git",
|
|
21
|
+
"directory": "packages/nestjs"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/expedite-atul/refpool/tree/main/packages/nestjs#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/expedite-atul/refpool/issues"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js",
|
|
37
|
+
"require": "./dist/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@refpool/core": "0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
50
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
51
|
+
"reflect-metadata": "^0.1.13 || ^0.2.0",
|
|
52
|
+
"rxjs": "^7.8.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@nestjs/common": "^11.0.0",
|
|
56
|
+
"@nestjs/core": "^11.0.0",
|
|
57
|
+
"@nestjs/testing": "^11.0.0",
|
|
58
|
+
"@types/node": "^22.10.2",
|
|
59
|
+
"reflect-metadata": "^0.2.2",
|
|
60
|
+
"rxjs": "^7.8.1",
|
|
61
|
+
"tsup": "^8.3.5",
|
|
62
|
+
"typescript": "^5.6.3",
|
|
63
|
+
"vitest": "^2.1.8"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsup",
|
|
70
|
+
"typecheck": "tsc --noEmit",
|
|
71
|
+
"test": "vitest run"
|
|
72
|
+
}
|
|
73
|
+
}
|