tenantforge 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial public release.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aftab Ahmad Khan
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.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # tenantforge
2
+
3
+ **Topics:** `express` · `mern` · `mern-packages` · `merndev` · `middleware` · `mongodb` · `multi-tenant` · `nodejs` · `npm-pm` · `observability` · `saas` · `tenantforge` · `typescript`
4
+
5
+ **Multi-tenant guard** for Express: reads **`x-tenant-id`** (configurable), supports an optional allow-list, attaches **`req.tenantId`**, and ships `tenantScope()` for Mongo filters.
6
+
7
+ ```ts
8
+ import { tenantforge, tenantScope } from "tenantforge";
9
+
10
+ app.use(tenantforge({ allowList: new Set(process.env.TENANT_ALLOWLIST!.split(",")) }));
11
+
12
+ app.get("/items", async (req, res) => {
13
+ const items = await Item.find(tenantScope(req.tenantId));
14
+ res.json(items);
15
+ });
16
+ ```
17
+
18
+ ## License
19
+
20
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,51 @@
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
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ tenantScope: () => tenantScope,
24
+ tenantforge: () => tenantforge
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+ function tenantforge(opts = {}) {
28
+ return (req, res, next) => {
29
+ const id = req.get(opts.header ?? "x-tenant-id");
30
+ if (!id || typeof id !== "string") {
31
+ res.status(400).json({ error: "tenant_required" });
32
+ return;
33
+ }
34
+ if (opts.allowList && !opts.allowList.has(id)) {
35
+ res.status(403).json({ error: "tenant_forbidden" });
36
+ return;
37
+ }
38
+ const prop = opts.property ?? "tenantId";
39
+ Object.assign(req, { [prop]: id });
40
+ next();
41
+ };
42
+ }
43
+ function tenantScope(tenantId) {
44
+ return { tenantId };
45
+ }
46
+ // Annotate the CommonJS export names for ESM import in node:
47
+ 0 && (module.exports = {
48
+ tenantScope,
49
+ tenantforge
50
+ });
51
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Request, RequestHandler } from \"express\";\n\nexport interface TenantforgeRequest extends Request {\n tenantId: string;\n}\n\nexport interface TenantforgeOptions {\n /** Header containing tenant id (default `x-tenant-id`). */\n header?: string;\n /** Express `req` property name (default `tenantId`). */\n property?: keyof TenantforgeRequest;\n /** Optional allow-list of tenant ids; missing = any non-empty string allowed. */\n allowList?: Set<string>;\n}\n\n/** Require tenant header and attach `req.tenantId` (or custom property). */\nexport function tenantforge(opts: TenantforgeOptions = {}): RequestHandler {\n return (req, res, next) => {\n const id = req.get(opts.header ?? \"x-tenant-id\");\n if (!id || typeof id !== \"string\") {\n res.status(400).json({ error: \"tenant_required\" });\n return;\n }\n if (opts.allowList && !opts.allowList.has(id)) {\n res.status(403).json({ error: \"tenant_forbidden\" });\n return;\n }\n const prop = opts.property ?? \"tenantId\";\n Object.assign(req, { [prop]: id });\n next();\n };\n}\n\n/** Mongo-style filter helper for multi-tenant collections. */\nexport function tenantScope(tenantId: string): { tenantId: string } {\n return { tenantId };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,SAAS,YAAY,OAA2B,CAAC,GAAmB;AACzE,SAAO,CAAC,KAAK,KAAK,SAAS;AACzB,UAAM,KAAK,IAAI,IAAI,KAAK,UAAU,aAAa;AAC/C,QAAI,CAAC,MAAM,OAAO,OAAO,UAAU;AACjC,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,QAAI,KAAK,aAAa,CAAC,KAAK,UAAU,IAAI,EAAE,GAAG;AAC7C,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;AAAA,IACF;AACA,UAAM,OAAO,KAAK,YAAY;AAC9B,WAAO,OAAO,KAAK,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC;AACjC,SAAK;AAAA,EACP;AACF;AAGO,SAAS,YAAY,UAAwC;AAClE,SAAO,EAAE,SAAS;AACpB;","names":[]}
@@ -0,0 +1,21 @@
1
+ import { Request, RequestHandler } from 'express';
2
+
3
+ interface TenantforgeRequest extends Request {
4
+ tenantId: string;
5
+ }
6
+ interface TenantforgeOptions {
7
+ /** Header containing tenant id (default `x-tenant-id`). */
8
+ header?: string;
9
+ /** Express `req` property name (default `tenantId`). */
10
+ property?: keyof TenantforgeRequest;
11
+ /** Optional allow-list of tenant ids; missing = any non-empty string allowed. */
12
+ allowList?: Set<string>;
13
+ }
14
+ /** Require tenant header and attach `req.tenantId` (or custom property). */
15
+ declare function tenantforge(opts?: TenantforgeOptions): RequestHandler;
16
+ /** Mongo-style filter helper for multi-tenant collections. */
17
+ declare function tenantScope(tenantId: string): {
18
+ tenantId: string;
19
+ };
20
+
21
+ export { type TenantforgeOptions, type TenantforgeRequest, tenantScope, tenantforge };
@@ -0,0 +1,21 @@
1
+ import { Request, RequestHandler } from 'express';
2
+
3
+ interface TenantforgeRequest extends Request {
4
+ tenantId: string;
5
+ }
6
+ interface TenantforgeOptions {
7
+ /** Header containing tenant id (default `x-tenant-id`). */
8
+ header?: string;
9
+ /** Express `req` property name (default `tenantId`). */
10
+ property?: keyof TenantforgeRequest;
11
+ /** Optional allow-list of tenant ids; missing = any non-empty string allowed. */
12
+ allowList?: Set<string>;
13
+ }
14
+ /** Require tenant header and attach `req.tenantId` (or custom property). */
15
+ declare function tenantforge(opts?: TenantforgeOptions): RequestHandler;
16
+ /** Mongo-style filter helper for multi-tenant collections. */
17
+ declare function tenantScope(tenantId: string): {
18
+ tenantId: string;
19
+ };
20
+
21
+ export { type TenantforgeOptions, type TenantforgeRequest, tenantScope, tenantforge };
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ // src/index.ts
2
+ function tenantforge(opts = {}) {
3
+ return (req, res, next) => {
4
+ const id = req.get(opts.header ?? "x-tenant-id");
5
+ if (!id || typeof id !== "string") {
6
+ res.status(400).json({ error: "tenant_required" });
7
+ return;
8
+ }
9
+ if (opts.allowList && !opts.allowList.has(id)) {
10
+ res.status(403).json({ error: "tenant_forbidden" });
11
+ return;
12
+ }
13
+ const prop = opts.property ?? "tenantId";
14
+ Object.assign(req, { [prop]: id });
15
+ next();
16
+ };
17
+ }
18
+ function tenantScope(tenantId) {
19
+ return { tenantId };
20
+ }
21
+ export {
22
+ tenantScope,
23
+ tenantforge
24
+ };
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Request, RequestHandler } from \"express\";\n\nexport interface TenantforgeRequest extends Request {\n tenantId: string;\n}\n\nexport interface TenantforgeOptions {\n /** Header containing tenant id (default `x-tenant-id`). */\n header?: string;\n /** Express `req` property name (default `tenantId`). */\n property?: keyof TenantforgeRequest;\n /** Optional allow-list of tenant ids; missing = any non-empty string allowed. */\n allowList?: Set<string>;\n}\n\n/** Require tenant header and attach `req.tenantId` (or custom property). */\nexport function tenantforge(opts: TenantforgeOptions = {}): RequestHandler {\n return (req, res, next) => {\n const id = req.get(opts.header ?? \"x-tenant-id\");\n if (!id || typeof id !== \"string\") {\n res.status(400).json({ error: \"tenant_required\" });\n return;\n }\n if (opts.allowList && !opts.allowList.has(id)) {\n res.status(403).json({ error: \"tenant_forbidden\" });\n return;\n }\n const prop = opts.property ?? \"tenantId\";\n Object.assign(req, { [prop]: id });\n next();\n };\n}\n\n/** Mongo-style filter helper for multi-tenant collections. */\nexport function tenantScope(tenantId: string): { tenantId: string } {\n return { tenantId };\n}\n"],"mappings":";AAgBO,SAAS,YAAY,OAA2B,CAAC,GAAmB;AACzE,SAAO,CAAC,KAAK,KAAK,SAAS;AACzB,UAAM,KAAK,IAAI,IAAI,KAAK,UAAU,aAAa;AAC/C,QAAI,CAAC,MAAM,OAAO,OAAO,UAAU;AACjC,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,QAAI,KAAK,aAAa,CAAC,KAAK,UAAU,IAAI,EAAE,GAAG;AAC7C,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;AAAA,IACF;AACA,UAAM,OAAO,KAAK,YAAY;AAC9B,WAAO,OAAO,KAAK,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC;AACjC,SAAK;AAAA,EACP;AACF;AAGO,SAAS,YAAY,UAAwC;AAClE,SAAO,EAAE,SAAS;AACpB;","names":[]}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "tenantforge",
3
+ "version": "0.2.0",
4
+ "description": "Tenant resolution from headers, scoped filters for Express and Mongo-style stacks.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "test": "vitest run",
26
+ "typecheck": "tsc --noEmit",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "express",
31
+ "mern",
32
+ "mern-packages",
33
+ "merndev",
34
+ "middleware",
35
+ "mongodb",
36
+ "multi-tenant",
37
+ "nodejs",
38
+ "npm-pm",
39
+ "observability",
40
+ "saas",
41
+ "tenantforge",
42
+ "typescript"
43
+ ],
44
+ "dependencies": {},
45
+ "devDependencies": {
46
+ "@types/express": "^4.17.21",
47
+ "@types/node": "^20.11.0",
48
+ "@types/supertest": "^6.0.2",
49
+ "express": "^4.19.2",
50
+ "supertest": "^7.0.0",
51
+ "tsup": "^8.0.0",
52
+ "typescript": "^5.4.0",
53
+ "vitest": "^1.4.0"
54
+ },
55
+ "peerDependencies": {
56
+ "express": "^4.0.0 || ^5.0.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "express": {
60
+ "optional": true
61
+ }
62
+ },
63
+ "engines": {
64
+ "node": ">=18"
65
+ },
66
+ "author": "Aftab Ahmad Khan (https://github.com/aftab-ahmad-khan-dev)",
67
+ "repository": {
68
+ "type": "git",
69
+ "url": "git+https://github.com/NPM-Packages-Modules/mern.git",
70
+ "directory": "tenantforge"
71
+ },
72
+ "bugs": {
73
+ "url": "https://github.com/NPM-Packages-Modules/mern/issues"
74
+ },
75
+ "homepage": "https://github.com/NPM-Packages-Modules/mern/tree/main/tenantforge",
76
+ "publishConfig": {
77
+ "access": "public"
78
+ }
79
+ }