@usebetterdev/tenant-express 0.2.1-beta.1 → 0.4.0-beta.2
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/README.md +33 -1
- package/dist/index.cjs +10 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
1
|
# @usebetterdev/tenant-express
|
|
2
2
|
|
|
3
|
-
Express middleware
|
|
3
|
+
Express middleware for [@usebetterdev/tenant](https://github.com/usebetter-dev/usebetter). Resolves the tenant from the incoming request and opens a database transaction with `SET LOCAL` so Postgres RLS applies.
|
|
4
|
+
|
|
5
|
+
Requires **Express 5+**.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @usebetterdev/tenant express
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import express from "express";
|
|
17
|
+
import { createExpressMiddleware } from "@usebetterdev/tenant/express";
|
|
18
|
+
import { tenant } from "./tenant.js";
|
|
19
|
+
|
|
20
|
+
const app = express();
|
|
21
|
+
app.use(express.json());
|
|
22
|
+
app.use("/api", createExpressMiddleware(tenant));
|
|
23
|
+
|
|
24
|
+
app.get("/api/projects", async (req, res) => {
|
|
25
|
+
const db = tenant.getDatabase();
|
|
26
|
+
if (!db) {
|
|
27
|
+
return res.status(500).json({ error: "No tenant-scoped database" });
|
|
28
|
+
}
|
|
29
|
+
res.json(await db.project.findMany());
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Peer dependency
|
|
34
|
+
|
|
35
|
+
Requires `express` (>= 5.0.0).
|
package/dist/index.cjs
CHANGED
|
@@ -30,9 +30,17 @@ function createExpressMiddleware(tenant, options = {}) {
|
|
|
30
30
|
try {
|
|
31
31
|
await tenant.handleRequest(
|
|
32
32
|
(0, import_tenant_core.toResolvableRequest)(request),
|
|
33
|
-
|
|
33
|
+
() => new Promise((resolve) => {
|
|
34
|
+
const done = () => resolve();
|
|
35
|
+
if (response.on) {
|
|
36
|
+
response.on("finish", done);
|
|
37
|
+
response.on("close", done);
|
|
38
|
+
}
|
|
34
39
|
next();
|
|
35
|
-
|
|
40
|
+
if (!response.on) {
|
|
41
|
+
done();
|
|
42
|
+
}
|
|
43
|
+
}),
|
|
36
44
|
{
|
|
37
45
|
onMissingTenant: async () => {
|
|
38
46
|
if (options.onMissingTenant) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { toResolvableRequest } from \"@usebetterdev/tenant-core\";\nimport type {\n BetterTenantInstance,\n NodeLikeRequest,\n ResolvableRequest,\n TenantContext,\n} from \"@usebetterdev/tenant-core\";\n\nexport interface ExpressRequestLike extends NodeLikeRequest {\n headers: Record<string, string | string[] | number | undefined>;\n originalUrl?: string;\n hostname?: string;\n}\n\nexport interface ExpressResponseLike {\n status: (code: number) => ExpressResponseLike;\n json: (body: unknown) => unknown;\n}\n\nexport type ExpressNextFunction = (error?: unknown) => void;\n\nexport interface CreateExpressMiddlewareOptions {\n missingTenantStatus?: 401 | 404;\n onMissingTenant?: (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n ) => void | Promise<void>;\n}\n\nexport interface ExpressTenantLike extends Pick<\n BetterTenantInstance,\n \"handleRequest\" | \"resolverStrategies\"\n> {}\n\nexport type ExpressMiddleware = (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n next: ExpressNextFunction,\n) => Promise<void>;\n\nexport function createExpressMiddleware(\n tenant: ExpressTenantLike,\n options: CreateExpressMiddlewareOptions = {},\n): ExpressMiddleware {\n const missingTenantStatus = options.missingTenantStatus ?? 404;\n\n return async (request, response, next) => {\n try {\n await tenant.handleRequest<ResolvableRequest, void>(\n toResolvableRequest(request),\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { toResolvableRequest } from \"@usebetterdev/tenant-core\";\nimport type {\n BetterTenantInstance,\n NodeLikeRequest,\n ResolvableRequest,\n TenantContext,\n} from \"@usebetterdev/tenant-core\";\n\nexport interface ExpressRequestLike extends NodeLikeRequest {\n headers: Record<string, string | string[] | number | undefined>;\n originalUrl?: string;\n hostname?: string;\n}\n\nexport interface ExpressResponseLike {\n status: (code: number) => ExpressResponseLike;\n json: (body: unknown) => unknown;\n /** Used to detect when the response is finished so the transaction stays open. Real Express responses always have this (inherited from http.ServerResponse). */\n on?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n}\n\nexport type ExpressNextFunction = (error?: unknown) => void;\n\nexport interface CreateExpressMiddlewareOptions {\n missingTenantStatus?: 401 | 404;\n onMissingTenant?: (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n ) => void | Promise<void>;\n}\n\nexport interface ExpressTenantLike extends Pick<\n BetterTenantInstance,\n \"handleRequest\" | \"resolverStrategies\"\n> {}\n\nexport type ExpressMiddleware = (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n next: ExpressNextFunction,\n) => Promise<void>;\n\nexport function createExpressMiddleware(\n tenant: ExpressTenantLike,\n options: CreateExpressMiddlewareOptions = {},\n): ExpressMiddleware {\n const missingTenantStatus = options.missingTenantStatus ?? 404;\n\n return async (request, response, next) => {\n try {\n await tenant.handleRequest<ResolvableRequest, void>(\n toResolvableRequest(request),\n () =>\n new Promise<void>((resolve) => {\n const done = () => resolve();\n if (response.on) {\n response.on(\"finish\", done);\n response.on(\"close\", done);\n }\n next();\n if (!response.on) {\n done();\n }\n }),\n {\n onMissingTenant: async () => {\n if (options.onMissingTenant) {\n await options.onMissingTenant(request, response);\n return;\n }\n const checked = tenant.resolverStrategies;\n const message =\n checked.length > 0\n ? `Tenant could not be resolved. Checked: ${checked.join(\", \")}`\n : \"Tenant could not be resolved. No resolution strategies configured\";\n response.status(missingTenantStatus).json({ error: message });\n },\n },\n );\n } catch (error) {\n next(error);\n }\n };\n}\n\nexport type { TenantContext };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAoC;AA0C7B,SAAS,wBACd,QACA,UAA0C,CAAC,GACxB;AACnB,QAAM,sBAAsB,QAAQ,uBAAuB;AAE3D,SAAO,OAAO,SAAS,UAAU,SAAS;AACxC,QAAI;AACF,YAAM,OAAO;AAAA,YACX,wCAAoB,OAAO;AAAA,QAC3B,MACE,IAAI,QAAc,CAAC,YAAY;AAC7B,gBAAM,OAAO,MAAM,QAAQ;AAC3B,cAAI,SAAS,IAAI;AACf,qBAAS,GAAG,UAAU,IAAI;AAC1B,qBAAS,GAAG,SAAS,IAAI;AAAA,UAC3B;AACA,eAAK;AACL,cAAI,CAAC,SAAS,IAAI;AAChB,iBAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,QACH;AAAA,UACE,iBAAiB,YAAY;AAC3B,gBAAI,QAAQ,iBAAiB;AAC3B,oBAAM,QAAQ,gBAAgB,SAAS,QAAQ;AAC/C;AAAA,YACF;AACA,kBAAM,UAAU,OAAO;AACvB,kBAAM,UACJ,QAAQ,SAAS,IACb,0CAA0C,QAAQ,KAAK,IAAI,CAAC,KAC5D;AACN,qBAAS,OAAO,mBAAmB,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,8 @@ interface ExpressRequestLike extends NodeLikeRequest {
|
|
|
9
9
|
interface ExpressResponseLike {
|
|
10
10
|
status: (code: number) => ExpressResponseLike;
|
|
11
11
|
json: (body: unknown) => unknown;
|
|
12
|
+
/** Used to detect when the response is finished so the transaction stays open. Real Express responses always have this (inherited from http.ServerResponse). */
|
|
13
|
+
on?: (event: string, listener: (...args: unknown[]) => void) => unknown;
|
|
12
14
|
}
|
|
13
15
|
type ExpressNextFunction = (error?: unknown) => void;
|
|
14
16
|
interface CreateExpressMiddlewareOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface ExpressRequestLike extends NodeLikeRequest {
|
|
|
9
9
|
interface ExpressResponseLike {
|
|
10
10
|
status: (code: number) => ExpressResponseLike;
|
|
11
11
|
json: (body: unknown) => unknown;
|
|
12
|
+
/** Used to detect when the response is finished so the transaction stays open. Real Express responses always have this (inherited from http.ServerResponse). */
|
|
13
|
+
on?: (event: string, listener: (...args: unknown[]) => void) => unknown;
|
|
12
14
|
}
|
|
13
15
|
type ExpressNextFunction = (error?: unknown) => void;
|
|
14
16
|
interface CreateExpressMiddlewareOptions {
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,17 @@ function createExpressMiddleware(tenant, options = {}) {
|
|
|
6
6
|
try {
|
|
7
7
|
await tenant.handleRequest(
|
|
8
8
|
toResolvableRequest(request),
|
|
9
|
-
|
|
9
|
+
() => new Promise((resolve) => {
|
|
10
|
+
const done = () => resolve();
|
|
11
|
+
if (response.on) {
|
|
12
|
+
response.on("finish", done);
|
|
13
|
+
response.on("close", done);
|
|
14
|
+
}
|
|
10
15
|
next();
|
|
11
|
-
|
|
16
|
+
if (!response.on) {
|
|
17
|
+
done();
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
12
20
|
{
|
|
13
21
|
onMissingTenant: async () => {
|
|
14
22
|
if (options.onMissingTenant) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { toResolvableRequest } from \"@usebetterdev/tenant-core\";\nimport type {\n BetterTenantInstance,\n NodeLikeRequest,\n ResolvableRequest,\n TenantContext,\n} from \"@usebetterdev/tenant-core\";\n\nexport interface ExpressRequestLike extends NodeLikeRequest {\n headers: Record<string, string | string[] | number | undefined>;\n originalUrl?: string;\n hostname?: string;\n}\n\nexport interface ExpressResponseLike {\n status: (code: number) => ExpressResponseLike;\n json: (body: unknown) => unknown;\n}\n\nexport type ExpressNextFunction = (error?: unknown) => void;\n\nexport interface CreateExpressMiddlewareOptions {\n missingTenantStatus?: 401 | 404;\n onMissingTenant?: (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n ) => void | Promise<void>;\n}\n\nexport interface ExpressTenantLike extends Pick<\n BetterTenantInstance,\n \"handleRequest\" | \"resolverStrategies\"\n> {}\n\nexport type ExpressMiddleware = (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n next: ExpressNextFunction,\n) => Promise<void>;\n\nexport function createExpressMiddleware(\n tenant: ExpressTenantLike,\n options: CreateExpressMiddlewareOptions = {},\n): ExpressMiddleware {\n const missingTenantStatus = options.missingTenantStatus ?? 404;\n\n return async (request, response, next) => {\n try {\n await tenant.handleRequest<ResolvableRequest, void>(\n toResolvableRequest(request),\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { toResolvableRequest } from \"@usebetterdev/tenant-core\";\nimport type {\n BetterTenantInstance,\n NodeLikeRequest,\n ResolvableRequest,\n TenantContext,\n} from \"@usebetterdev/tenant-core\";\n\nexport interface ExpressRequestLike extends NodeLikeRequest {\n headers: Record<string, string | string[] | number | undefined>;\n originalUrl?: string;\n hostname?: string;\n}\n\nexport interface ExpressResponseLike {\n status: (code: number) => ExpressResponseLike;\n json: (body: unknown) => unknown;\n /** Used to detect when the response is finished so the transaction stays open. Real Express responses always have this (inherited from http.ServerResponse). */\n on?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n}\n\nexport type ExpressNextFunction = (error?: unknown) => void;\n\nexport interface CreateExpressMiddlewareOptions {\n missingTenantStatus?: 401 | 404;\n onMissingTenant?: (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n ) => void | Promise<void>;\n}\n\nexport interface ExpressTenantLike extends Pick<\n BetterTenantInstance,\n \"handleRequest\" | \"resolverStrategies\"\n> {}\n\nexport type ExpressMiddleware = (\n request: ExpressRequestLike,\n response: ExpressResponseLike,\n next: ExpressNextFunction,\n) => Promise<void>;\n\nexport function createExpressMiddleware(\n tenant: ExpressTenantLike,\n options: CreateExpressMiddlewareOptions = {},\n): ExpressMiddleware {\n const missingTenantStatus = options.missingTenantStatus ?? 404;\n\n return async (request, response, next) => {\n try {\n await tenant.handleRequest<ResolvableRequest, void>(\n toResolvableRequest(request),\n () =>\n new Promise<void>((resolve) => {\n const done = () => resolve();\n if (response.on) {\n response.on(\"finish\", done);\n response.on(\"close\", done);\n }\n next();\n if (!response.on) {\n done();\n }\n }),\n {\n onMissingTenant: async () => {\n if (options.onMissingTenant) {\n await options.onMissingTenant(request, response);\n return;\n }\n const checked = tenant.resolverStrategies;\n const message =\n checked.length > 0\n ? `Tenant could not be resolved. Checked: ${checked.join(\", \")}`\n : \"Tenant could not be resolved. No resolution strategies configured\";\n response.status(missingTenantStatus).json({ error: message });\n },\n },\n );\n } catch (error) {\n next(error);\n }\n };\n}\n\nexport type { TenantContext };\n"],"mappings":";AAAA,SAAS,2BAA2B;AA0C7B,SAAS,wBACd,QACA,UAA0C,CAAC,GACxB;AACnB,QAAM,sBAAsB,QAAQ,uBAAuB;AAE3D,SAAO,OAAO,SAAS,UAAU,SAAS;AACxC,QAAI;AACF,YAAM,OAAO;AAAA,QACX,oBAAoB,OAAO;AAAA,QAC3B,MACE,IAAI,QAAc,CAAC,YAAY;AAC7B,gBAAM,OAAO,MAAM,QAAQ;AAC3B,cAAI,SAAS,IAAI;AACf,qBAAS,GAAG,UAAU,IAAI;AAC1B,qBAAS,GAAG,SAAS,IAAI;AAAA,UAC3B;AACA,eAAK;AACL,cAAI,CAAC,SAAS,IAAI;AAChB,iBAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,QACH;AAAA,UACE,iBAAiB,YAAY;AAC3B,gBAAI,QAAQ,iBAAiB;AAC3B,oBAAM,QAAQ,gBAAgB,SAAS,QAAQ;AAC/C;AAAA,YACF;AACA,kBAAM,UAAU,OAAO;AACvB,kBAAM,UACJ,QAAQ,SAAS,IACb,0CAA0C,QAAQ,KAAK,IAAI,CAAC,KAC5D;AACN,qBAAS,OAAO,mBAAmB,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebetterdev/tenant-express",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.2",
|
|
4
4
|
"repository": "github:usebetter-dev/usebetter",
|
|
5
5
|
"bugs": "https://github.com/usebetter-dev/usebetter/issues",
|
|
6
6
|
"homepage": "https://github.com/usebetter-dev/usebetter#readme",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"README.md"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@usebetterdev/tenant-core": "0.
|
|
27
|
+
"@usebetterdev/tenant-core": "0.4.0-beta.2"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"express": ">=
|
|
30
|
+
"express": ">=5.0.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/express": "^5.0.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"tsup": "^8.3.5",
|
|
39
39
|
"typescript": "~5.7.2",
|
|
40
40
|
"vitest": "^2.1.6",
|
|
41
|
-
"@usebetterdev/test-utils": "0.
|
|
41
|
+
"@usebetterdev/test-utils": "0.4.0-beta.2"
|
|
42
42
|
},
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=22"
|