@blimu/nestjs 1.1.1 → 1.1.3

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.
Files changed (80) hide show
  1. package/dist/__tests__/example.test.cjs +17135 -0
  2. package/dist/__tests__/example.test.cjs.map +1 -0
  3. package/dist/__tests__/example.test.d.cts +2 -0
  4. package/dist/__tests__/example.test.d.ts +2 -0
  5. package/dist/__tests__/example.test.mjs +17134 -0
  6. package/dist/__tests__/example.test.mjs.map +1 -0
  7. package/dist/config/blimu.config.cjs +31 -0
  8. package/dist/config/blimu.config.cjs.map +1 -0
  9. package/dist/config/blimu.config.d.cts +11 -0
  10. package/dist/config/blimu.config.d.ts +8 -6
  11. package/dist/config/blimu.config.mjs +6 -0
  12. package/dist/config/blimu.config.mjs.map +1 -0
  13. package/dist/decorators/entitlement.decorator.cjs +178 -0
  14. package/dist/decorators/entitlement.decorator.cjs.map +1 -0
  15. package/dist/decorators/entitlement.decorator.d.cts +9 -0
  16. package/dist/decorators/entitlement.decorator.d.ts +9 -4
  17. package/dist/decorators/entitlement.decorator.mjs +161 -0
  18. package/dist/decorators/entitlement.decorator.mjs.map +1 -0
  19. package/dist/exceptions/blimu-forbidden.exception.cjs +86 -0
  20. package/dist/exceptions/blimu-forbidden.exception.cjs.map +1 -0
  21. package/dist/exceptions/blimu-forbidden.exception.d.cts +14 -0
  22. package/dist/exceptions/blimu-forbidden.exception.d.ts +6 -4
  23. package/dist/exceptions/blimu-forbidden.exception.mjs +61 -0
  24. package/dist/exceptions/blimu-forbidden.exception.mjs.map +1 -0
  25. package/dist/guards/entitlement.guard.cjs +174 -0
  26. package/dist/guards/entitlement.guard.cjs.map +1 -0
  27. package/dist/guards/entitlement.guard.d.cts +24 -0
  28. package/dist/guards/entitlement.guard.d.ts +12 -12
  29. package/dist/guards/entitlement.guard.mjs +154 -0
  30. package/dist/guards/entitlement.guard.mjs.map +1 -0
  31. package/dist/index.cjs +445 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +9 -0
  34. package/dist/index.d.ts +9 -7
  35. package/dist/index.mjs +420 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/modules/blimu.module.cjs +413 -0
  38. package/dist/modules/blimu.module.cjs.map +1 -0
  39. package/dist/modules/blimu.module.d.cts +14 -0
  40. package/dist/modules/blimu.module.d.ts +11 -8
  41. package/dist/modules/blimu.module.mjs +398 -0
  42. package/dist/modules/blimu.module.mjs.map +1 -0
  43. package/dist/services/index.cjs +93 -0
  44. package/dist/services/index.cjs.map +1 -0
  45. package/dist/services/index.d.cts +2 -0
  46. package/dist/services/index.d.ts +2 -2
  47. package/dist/services/index.mjs +69 -0
  48. package/dist/services/index.mjs.map +1 -0
  49. package/dist/services/jwk.service.cjs +91 -0
  50. package/dist/services/jwk.service.cjs.map +1 -0
  51. package/dist/services/jwk.service.d.cts +12 -0
  52. package/dist/services/jwk.service.d.ts +5 -3
  53. package/dist/services/jwk.service.mjs +69 -0
  54. package/dist/services/jwk.service.mjs.map +1 -0
  55. package/package.json +34 -13
  56. package/dist/config/blimu.config.d.ts.map +0 -1
  57. package/dist/config/blimu.config.js +0 -5
  58. package/dist/config/blimu.config.js.map +0 -1
  59. package/dist/decorators/entitlement.decorator.d.ts.map +0 -1
  60. package/dist/decorators/entitlement.decorator.js +0 -10
  61. package/dist/decorators/entitlement.decorator.js.map +0 -1
  62. package/dist/exceptions/blimu-forbidden.exception.d.ts.map +0 -1
  63. package/dist/exceptions/blimu-forbidden.exception.js +0 -38
  64. package/dist/exceptions/blimu-forbidden.exception.js.map +0 -1
  65. package/dist/guards/entitlement.guard.d.ts.map +0 -1
  66. package/dist/guards/entitlement.guard.js +0 -82
  67. package/dist/guards/entitlement.guard.js.map +0 -1
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.js +0 -23
  70. package/dist/index.js.map +0 -1
  71. package/dist/modules/blimu.module.d.ts.map +0 -1
  72. package/dist/modules/blimu.module.js +0 -91
  73. package/dist/modules/blimu.module.js.map +0 -1
  74. package/dist/services/index.d.ts.map +0 -1
  75. package/dist/services/index.js +0 -18
  76. package/dist/services/index.js.map +0 -1
  77. package/dist/services/jwk.service.d.ts.map +0 -1
  78. package/dist/services/jwk.service.js +0 -59
  79. package/dist/services/jwk.service.js.map +0 -1
  80. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,7 +1,8 @@
1
1
  import { ForbiddenException } from '@nestjs/common';
2
- import type { Schema } from '@blimu/backend';
3
- import type { EntitlementType } from '@blimu/types';
4
- export declare class BlimuForbiddenException extends ForbiddenException {
2
+ import { Schema } from '@blimu/backend';
3
+ import { EntitlementType } from '@blimu/types';
4
+
5
+ declare class BlimuForbiddenException extends ForbiddenException {
5
6
  readonly entitlementResult: Schema.EntitlementCheckResult;
6
7
  readonly entitlementKey: EntitlementType;
7
8
  readonly resourceId: string;
@@ -9,4 +10,5 @@ export declare class BlimuForbiddenException extends ForbiddenException {
9
10
  constructor(entitlementResult: Schema.EntitlementCheckResult, entitlementKey: EntitlementType, resourceId: string, userId: string);
10
11
  private static buildMessage;
11
12
  }
12
- //# sourceMappingURL=blimu-forbidden.exception.d.ts.map
13
+
14
+ export { BlimuForbiddenException };
@@ -0,0 +1,61 @@
1
+ // src/exceptions/blimu-forbidden.exception.ts
2
+ import { ForbiddenException } from "@nestjs/common";
3
+ var BlimuForbiddenException = class _BlimuForbiddenException extends ForbiddenException {
4
+ /**
5
+ * The entitlement check result containing detailed failure information
6
+ */
7
+ entitlementResult;
8
+ /**
9
+ * The entitlement key that was checked
10
+ */
11
+ entitlementKey;
12
+ /**
13
+ * The resource ID that was checked
14
+ */
15
+ resourceId;
16
+ /**
17
+ * The user ID that was checked
18
+ */
19
+ userId;
20
+ constructor(entitlementResult, entitlementKey, resourceId, userId) {
21
+ const message = _BlimuForbiddenException.buildMessage(entitlementResult, entitlementKey);
22
+ super({
23
+ message,
24
+ entitlementResult,
25
+ entitlementKey,
26
+ resourceId,
27
+ userId
28
+ });
29
+ this.entitlementResult = entitlementResult;
30
+ this.entitlementKey = entitlementKey;
31
+ this.resourceId = resourceId;
32
+ this.userId = userId;
33
+ }
34
+ /**
35
+ * Builds a user-friendly error message from the entitlement check result
36
+ */
37
+ static buildMessage(result, entitlementKey) {
38
+ const reasons = [];
39
+ if (result.roles && !result.roles.allowed) {
40
+ reasons.push(
41
+ `Insufficient roles. Required: ${result.roles.allowedRoles?.join(", ") || "unknown"}. User has: ${result.roles.userRoles?.join(", ") || "none"}.`
42
+ );
43
+ }
44
+ if (result.plans && !result.plans.allowed) {
45
+ reasons.push(
46
+ `Plan restriction. Required plans: ${result.plans.allowedPlans?.join(", ") || "unknown"}. Current plan: ${result.plans.plan || "none"}.`
47
+ );
48
+ }
49
+ if (result.limit && !result.limit.allowed) {
50
+ reasons.push(`Usage limit exceeded. ${result.limit.reason || "Limit has been reached"}.`);
51
+ }
52
+ if (reasons.length === 0) {
53
+ return `Access denied for entitlement: ${entitlementKey}`;
54
+ }
55
+ return `Access denied for entitlement "${entitlementKey}": ${reasons.join(" ")}`;
56
+ }
57
+ };
58
+ export {
59
+ BlimuForbiddenException
60
+ };
61
+ //# sourceMappingURL=blimu-forbidden.exception.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exceptions/blimu-forbidden.exception.ts"],"sourcesContent":["import { ForbiddenException } from '@nestjs/common';\nimport type { Schema } from '@blimu/backend';\nimport type { EntitlementType } from '@blimu/types';\n/**\n * Custom exception for Blimu entitlement check failures\n *\n * This exception extends NestJS's ForbiddenException and includes\n * the typed EntitlementCheckResult, providing detailed information\n * about why the entitlement check failed (roles, plans, limits, etc.)\n */\nexport class BlimuForbiddenException extends ForbiddenException {\n /**\n * The entitlement check result containing detailed failure information\n */\n public readonly entitlementResult: Schema.EntitlementCheckResult;\n\n /**\n * The entitlement key that was checked\n */\n public readonly entitlementKey: EntitlementType;\n\n /**\n * The resource ID that was checked\n */\n public readonly resourceId: string;\n\n /**\n * The user ID that was checked\n */\n public readonly userId: string;\n\n constructor(\n entitlementResult: Schema.EntitlementCheckResult,\n entitlementKey: EntitlementType,\n resourceId: string,\n userId: string,\n ) {\n // Create a user-friendly message based on the failure reason\n const message = BlimuForbiddenException.buildMessage(entitlementResult, entitlementKey);\n\n super({\n message,\n entitlementResult,\n entitlementKey,\n resourceId,\n userId,\n });\n\n this.entitlementResult = entitlementResult;\n this.entitlementKey = entitlementKey;\n this.resourceId = resourceId;\n this.userId = userId;\n }\n\n /**\n * Builds a user-friendly error message from the entitlement check result\n */\n private static buildMessage(\n result: Schema.EntitlementCheckResult,\n entitlementKey: EntitlementType,\n ): string {\n const reasons: string[] = [];\n\n if (result.roles && !result.roles.allowed) {\n reasons.push(\n `Insufficient roles. Required: ${result.roles.allowedRoles?.join(', ') || 'unknown'}. User has: ${result.roles.userRoles?.join(', ') || 'none'}.`,\n );\n }\n\n if (result.plans && !result.plans.allowed) {\n reasons.push(\n `Plan restriction. Required plans: ${result.plans.allowedPlans?.join(', ') || 'unknown'}. Current plan: ${result.plans.plan || 'none'}.`,\n );\n }\n\n if (result.limit && !result.limit.allowed) {\n reasons.push(`Usage limit exceeded. ${result.limit.reason || 'Limit has been reached'}.`);\n }\n\n if (reasons.length === 0) {\n return `Access denied for entitlement: ${entitlementKey}`;\n }\n\n return `Access denied for entitlement \"${entitlementKey}\": ${reasons.join(' ')}`;\n }\n}\n"],"mappings":";AAAA,SAAS,0BAA0B;AAU5B,IAAM,0BAAN,MAAM,iCAAgC,mBAAmB;AAAA;AAAA;AAAA;AAAA,EAI9C;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,EAEhB,YACE,mBACA,gBACA,YACA,QACA;AAEA,UAAM,UAAU,yBAAwB,aAAa,mBAAmB,cAAc;AAEtF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,SAAK,oBAAoB;AACzB,SAAK,iBAAiB;AACtB,SAAK,aAAa;AAClB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe,aACb,QACA,gBACQ;AACR,UAAM,UAAoB,CAAC;AAE3B,QAAI,OAAO,SAAS,CAAC,OAAO,MAAM,SAAS;AACzC,cAAQ;AAAA,QACN,iCAAiC,OAAO,MAAM,cAAc,KAAK,IAAI,KAAK,SAAS,eAAe,OAAO,MAAM,WAAW,KAAK,IAAI,KAAK,MAAM;AAAA,MAChJ;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,CAAC,OAAO,MAAM,SAAS;AACzC,cAAQ;AAAA,QACN,qCAAqC,OAAO,MAAM,cAAc,KAAK,IAAI,KAAK,SAAS,mBAAmB,OAAO,MAAM,QAAQ,MAAM;AAAA,MACvI;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,CAAC,OAAO,MAAM,SAAS;AACzC,cAAQ,KAAK,yBAAyB,OAAO,MAAM,UAAU,wBAAwB,GAAG;AAAA,IAC1F;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,kCAAkC,cAAc;AAAA,IACzD;AAEA,WAAO,kCAAkC,cAAc,MAAM,QAAQ,KAAK,GAAG,CAAC;AAAA,EAChF;AACF;","names":[]}
@@ -0,0 +1,174 @@
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/guards/entitlement.guard.ts
30
+ var entitlement_guard_exports = {};
31
+ __export(entitlement_guard_exports, {
32
+ ENTITLEMENT_KEY: () => ENTITLEMENT_KEY,
33
+ ENTITLEMENT_METADATA_KEY: () => ENTITLEMENT_METADATA_KEY,
34
+ EntitlementGuard: () => EntitlementGuard,
35
+ SetEntitlementMetadata: () => SetEntitlementMetadata
36
+ });
37
+ module.exports = __toCommonJS(entitlement_guard_exports);
38
+ var import_common2 = require("@nestjs/common");
39
+ var import_reflect_metadata = require("reflect-metadata");
40
+
41
+ // src/exceptions/blimu-forbidden.exception.ts
42
+ var import_common = require("@nestjs/common");
43
+ var BlimuForbiddenException = class _BlimuForbiddenException extends import_common.ForbiddenException {
44
+ /**
45
+ * The entitlement check result containing detailed failure information
46
+ */
47
+ entitlementResult;
48
+ /**
49
+ * The entitlement key that was checked
50
+ */
51
+ entitlementKey;
52
+ /**
53
+ * The resource ID that was checked
54
+ */
55
+ resourceId;
56
+ /**
57
+ * The user ID that was checked
58
+ */
59
+ userId;
60
+ constructor(entitlementResult, entitlementKey, resourceId, userId) {
61
+ const message = _BlimuForbiddenException.buildMessage(entitlementResult, entitlementKey);
62
+ super({
63
+ message,
64
+ entitlementResult,
65
+ entitlementKey,
66
+ resourceId,
67
+ userId
68
+ });
69
+ this.entitlementResult = entitlementResult;
70
+ this.entitlementKey = entitlementKey;
71
+ this.resourceId = resourceId;
72
+ this.userId = userId;
73
+ }
74
+ /**
75
+ * Builds a user-friendly error message from the entitlement check result
76
+ */
77
+ static buildMessage(result, entitlementKey) {
78
+ const reasons = [];
79
+ if (result.roles && !result.roles.allowed) {
80
+ reasons.push(
81
+ `Insufficient roles. Required: ${result.roles.allowedRoles?.join(", ") || "unknown"}. User has: ${result.roles.userRoles?.join(", ") || "none"}.`
82
+ );
83
+ }
84
+ if (result.plans && !result.plans.allowed) {
85
+ reasons.push(
86
+ `Plan restriction. Required plans: ${result.plans.allowedPlans?.join(", ") || "unknown"}. Current plan: ${result.plans.plan || "none"}.`
87
+ );
88
+ }
89
+ if (result.limit && !result.limit.allowed) {
90
+ reasons.push(`Usage limit exceeded. ${result.limit.reason || "Limit has been reached"}.`);
91
+ }
92
+ if (reasons.length === 0) {
93
+ return `Access denied for entitlement: ${entitlementKey}`;
94
+ }
95
+ return `Access denied for entitlement "${entitlementKey}": ${reasons.join(" ")}`;
96
+ }
97
+ };
98
+
99
+ // src/guards/entitlement.guard.ts
100
+ var import_backend = require("@blimu/backend");
101
+
102
+ // src/config/blimu.config.ts
103
+ var BLIMU_CONFIG = /* @__PURE__ */ Symbol("BLIMU_CONFIG");
104
+
105
+ // src/guards/entitlement.guard.ts
106
+ var ENTITLEMENT_KEY = "entitlement";
107
+ var ENTITLEMENT_METADATA_KEY = /* @__PURE__ */ Symbol("entitlement");
108
+ var SetEntitlementMetadata = (entitlementKey, getEntitlementInfo) => (0, import_common2.SetMetadata)(ENTITLEMENT_METADATA_KEY, {
109
+ entitlementKey,
110
+ getEntitlementInfo
111
+ });
112
+ var EntitlementGuard = class {
113
+ constructor(config, runtime) {
114
+ this.config = config;
115
+ this.runtime = runtime;
116
+ }
117
+ async canActivate(context) {
118
+ const request = context.switchToHttp().getRequest();
119
+ const handler = context.getHandler();
120
+ const metadata = Reflect.getMetadata(ENTITLEMENT_METADATA_KEY, handler);
121
+ if (!metadata) {
122
+ return true;
123
+ }
124
+ let userId;
125
+ try {
126
+ userId = await this.config.getUserId(request);
127
+ } catch {
128
+ throw new import_common2.ForbiddenException("Failed to extract user ID from request");
129
+ }
130
+ if (!userId) {
131
+ throw new import_common2.ForbiddenException("User ID is required for entitlement check");
132
+ }
133
+ const entitlementInfo = await metadata.getEntitlementInfo(request);
134
+ if (!entitlementInfo?.resourceId) {
135
+ throw new import_common2.ForbiddenException("Resource ID is required for entitlement check");
136
+ }
137
+ try {
138
+ const result = await this.runtime.entitlements.checkEntitlement({
139
+ userId,
140
+ entitlement: metadata.entitlementKey,
141
+ resourceId: entitlementInfo.resourceId,
142
+ ...entitlementInfo.amount !== void 0 ? { amount: entitlementInfo.amount } : {}
143
+ });
144
+ if (!result.allowed) {
145
+ throw new BlimuForbiddenException(
146
+ result,
147
+ metadata.entitlementKey,
148
+ entitlementInfo.resourceId,
149
+ userId
150
+ );
151
+ }
152
+ return true;
153
+ } catch (error) {
154
+ if (error instanceof BlimuForbiddenException || error instanceof import_common2.ForbiddenException) {
155
+ throw error;
156
+ }
157
+ console.error("Entitlement check failed:", error);
158
+ throw new import_common2.ForbiddenException("Failed to verify entitlements");
159
+ }
160
+ }
161
+ };
162
+ EntitlementGuard = __decorateClass([
163
+ (0, import_common2.Injectable)(),
164
+ __decorateParam(0, (0, import_common2.Inject)(BLIMU_CONFIG)),
165
+ __decorateParam(1, (0, import_common2.Inject)(import_backend.Blimu))
166
+ ], EntitlementGuard);
167
+ // Annotate the CommonJS export names for ESM import in node:
168
+ 0 && (module.exports = {
169
+ ENTITLEMENT_KEY,
170
+ ENTITLEMENT_METADATA_KEY,
171
+ EntitlementGuard,
172
+ SetEntitlementMetadata
173
+ });
174
+ //# sourceMappingURL=entitlement.guard.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/guards/entitlement.guard.ts","../../src/exceptions/blimu-forbidden.exception.ts","../../src/config/blimu.config.ts"],"sourcesContent":["import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Injectable,\n SetMetadata,\n Inject,\n} from '@nestjs/common';\nimport 'reflect-metadata';\n\nimport type { EntitlementType } from '@blimu/types';\nimport { BlimuForbiddenException } from '../exceptions/blimu-forbidden.exception';\nimport { Blimu } from '@blimu/backend';\nimport { BLIMU_CONFIG, type BlimuConfig } from 'config/blimu.config';\n\nexport const ENTITLEMENT_KEY = 'entitlement';\nexport const ENTITLEMENT_METADATA_KEY = Symbol('entitlement');\n\n/**\n * Entitlement information returned by the getEntitlementInfo callback\n */\nexport interface EntitlementInfo {\n resourceId: string;\n amount?: number; // Amount to check against usage limit (for consumption)\n}\n\n/**\n * Metadata interface for entitlement checks\n */\nexport interface EntitlementMetadata<TRequest = unknown> {\n entitlementKey: EntitlementType;\n getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>;\n}\n\n/**\n * Sets entitlement metadata for a route handler\n * @internal This is used internally by the @Entitlement decorator\n */\nexport const SetEntitlementMetadata = <TRequest = unknown>(\n entitlementKey: string,\n getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>,\n): MethodDecorator =>\n SetMetadata(ENTITLEMENT_METADATA_KEY, {\n entitlementKey,\n getEntitlementInfo,\n } as EntitlementMetadata<TRequest>);\n\n/**\n * Guard that checks if the authenticated user has the required entitlement on a resource\n *\n * This guard automatically:\n * 1. Extracts the user from the request\n * 2. Extracts the resource ID using the provided extractor function\n * 3. Calls the Blimu Runtime API to check entitlements\n * 4. Allows or denies access based on the result\n */\n@Injectable()\nexport class EntitlementGuard<TRequest = unknown> implements CanActivate {\n constructor(\n @Inject(BLIMU_CONFIG)\n private readonly config: BlimuConfig<TRequest>,\n @Inject(Blimu)\n private readonly runtime: Blimu,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<TRequest>();\n const handler = context.getHandler();\n const metadata = Reflect.getMetadata(ENTITLEMENT_METADATA_KEY, handler) as\n | EntitlementMetadata<TRequest>\n | undefined;\n\n if (!metadata) {\n // No entitlement check required\n return true;\n }\n\n // Extract user ID using the configured getUserId function\n let userId: string;\n try {\n userId = await this.config.getUserId(request);\n } catch {\n throw new ForbiddenException('Failed to extract user ID from request');\n }\n\n if (!userId) {\n throw new ForbiddenException('User ID is required for entitlement check');\n }\n\n // Extract entitlement info from request\n const entitlementInfo = await metadata.getEntitlementInfo(request);\n\n if (!entitlementInfo?.resourceId) {\n throw new ForbiddenException('Resource ID is required for entitlement check');\n }\n\n try {\n // Check entitlement\n const result = await this.runtime.entitlements.checkEntitlement({\n userId,\n entitlement: metadata.entitlementKey,\n resourceId: entitlementInfo.resourceId,\n ...(entitlementInfo.amount !== undefined ? { amount: entitlementInfo.amount } : {}),\n });\n\n if (!result.allowed) {\n throw new BlimuForbiddenException(\n result,\n metadata.entitlementKey,\n entitlementInfo.resourceId,\n userId,\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof BlimuForbiddenException || error instanceof ForbiddenException) {\n throw error;\n }\n\n // Log the error for debugging but don't expose internal details\n console.error('Entitlement check failed:', error);\n throw new ForbiddenException('Failed to verify entitlements');\n }\n }\n}\n","import { ForbiddenException } from '@nestjs/common';\nimport type { Schema } from '@blimu/backend';\nimport type { EntitlementType } from '@blimu/types';\n/**\n * Custom exception for Blimu entitlement check failures\n *\n * This exception extends NestJS's ForbiddenException and includes\n * the typed EntitlementCheckResult, providing detailed information\n * about why the entitlement check failed (roles, plans, limits, etc.)\n */\nexport class BlimuForbiddenException extends ForbiddenException {\n /**\n * The entitlement check result containing detailed failure information\n */\n public readonly entitlementResult: Schema.EntitlementCheckResult;\n\n /**\n * The entitlement key that was checked\n */\n public readonly entitlementKey: EntitlementType;\n\n /**\n * The resource ID that was checked\n */\n public readonly resourceId: string;\n\n /**\n * The user ID that was checked\n */\n public readonly userId: string;\n\n constructor(\n entitlementResult: Schema.EntitlementCheckResult,\n entitlementKey: EntitlementType,\n resourceId: string,\n userId: string,\n ) {\n // Create a user-friendly message based on the failure reason\n const message = BlimuForbiddenException.buildMessage(entitlementResult, entitlementKey);\n\n super({\n message,\n entitlementResult,\n entitlementKey,\n resourceId,\n userId,\n });\n\n this.entitlementResult = entitlementResult;\n this.entitlementKey = entitlementKey;\n this.resourceId = resourceId;\n this.userId = userId;\n }\n\n /**\n * Builds a user-friendly error message from the entitlement check result\n */\n private static buildMessage(\n result: Schema.EntitlementCheckResult,\n entitlementKey: EntitlementType,\n ): string {\n const reasons: string[] = [];\n\n if (result.roles && !result.roles.allowed) {\n reasons.push(\n `Insufficient roles. Required: ${result.roles.allowedRoles?.join(', ') || 'unknown'}. User has: ${result.roles.userRoles?.join(', ') || 'none'}.`,\n );\n }\n\n if (result.plans && !result.plans.allowed) {\n reasons.push(\n `Plan restriction. Required plans: ${result.plans.allowedPlans?.join(', ') || 'unknown'}. Current plan: ${result.plans.plan || 'none'}.`,\n );\n }\n\n if (result.limit && !result.limit.allowed) {\n reasons.push(`Usage limit exceeded. ${result.limit.reason || 'Limit has been reached'}.`);\n }\n\n if (reasons.length === 0) {\n return `Access denied for entitlement: ${entitlementKey}`;\n }\n\n return `Access denied for entitlement \"${entitlementKey}\": ${reasons.join(' ')}`;\n }\n}\n","/**\n * Configuration interface for Blimu NestJS integration\n */\nexport interface BlimuConfig<TRequest = unknown> {\n global?: boolean | undefined;\n /**\n * The API secret key for authenticating with Blimu Runtime API\n */\n apiKey: string;\n\n /**\n * The base URL for the Blimu Runtime API\n * @default 'https://api.blimu.dev'\n */\n baseURL?: string | undefined;\n\n /**\n * Environment ID for the Blimu environment\n * This will be used in future versions for environment-specific configurations\n */\n environmentId?: string | undefined;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeoutMs?: number | undefined;\n\n /**\n * Function to extract user ID from the request\n *\n * This function is called by the EntitlementGuard to determine which user\n * to check entitlements for. It should return the user ID as a string.\n *\n * @param request - The incoming HTTP request\n * @returns The user ID as a string, or a Promise that resolves to the user ID\n *\n * @example\n * ```typescript\n * // Extract from JWT token in Authorization header\n * getUserId: (req) => {\n * const token = req.headers.authorization?.replace('Bearer ', '');\n * const decoded = jwt.verify(token, secret);\n * return decoded.sub;\n * }\n *\n * // Extract from request.user (common with Passport.js)\n * getUserId: (req) => req.user?.id\n *\n * // Extract from custom header\n * getUserId: (req) => req.headers['x-user-id']\n * ```\n */\n getUserId: (request: TRequest) => string | Promise<string>;\n}\n\n/**\n * Injection token for Blimu configuration\n */\nexport const BLIMU_CONFIG = Symbol('BLIMU_CONFIG');\n"],"mappings":"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","names":["import_common"]}
@@ -0,0 +1,24 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import { EntitlementType } from '@blimu/types';
3
+ import { Blimu } from '@blimu/backend';
4
+ import { BlimuConfig } from '../config/blimu.config.cjs';
5
+
6
+ declare const ENTITLEMENT_KEY = "entitlement";
7
+ declare const ENTITLEMENT_METADATA_KEY: unique symbol;
8
+ interface EntitlementInfo {
9
+ resourceId: string;
10
+ amount?: number;
11
+ }
12
+ interface EntitlementMetadata<TRequest = unknown> {
13
+ entitlementKey: EntitlementType;
14
+ getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>;
15
+ }
16
+ declare const SetEntitlementMetadata: <TRequest = unknown>(entitlementKey: string, getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>) => MethodDecorator;
17
+ declare class EntitlementGuard<TRequest = unknown> implements CanActivate {
18
+ private readonly config;
19
+ private readonly runtime;
20
+ constructor(config: BlimuConfig<TRequest>, runtime: Blimu);
21
+ canActivate(context: ExecutionContext): Promise<boolean>;
22
+ }
23
+
24
+ export { ENTITLEMENT_KEY, ENTITLEMENT_METADATA_KEY, EntitlementGuard, type EntitlementInfo, type EntitlementMetadata, SetEntitlementMetadata };
@@ -1,24 +1,24 @@
1
1
  import { CanActivate, ExecutionContext } from '@nestjs/common';
2
- import { Reflector } from '@nestjs/core';
2
+ import { EntitlementType } from '@blimu/types';
3
3
  import { Blimu } from '@blimu/backend';
4
- import type { EntitlementType } from '@blimu/types';
5
- import type { BlimuConfig } from '../config/blimu.config';
6
- export declare const ENTITLEMENT_KEY = "entitlement";
7
- export declare const ENTITLEMENT_METADATA_KEY: unique symbol;
8
- export interface EntitlementInfo {
4
+ import { BlimuConfig } from '../config/blimu.config.js';
5
+
6
+ declare const ENTITLEMENT_KEY = "entitlement";
7
+ declare const ENTITLEMENT_METADATA_KEY: unique symbol;
8
+ interface EntitlementInfo {
9
9
  resourceId: string;
10
10
  amount?: number;
11
11
  }
12
- export interface EntitlementMetadata<TRequest = any> {
12
+ interface EntitlementMetadata<TRequest = unknown> {
13
13
  entitlementKey: EntitlementType;
14
14
  getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>;
15
15
  }
16
- export declare const SetEntitlementMetadata: <TRequest = any>(entitlementKey: string, getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>) => import("@nestjs/common").CustomDecorator<typeof ENTITLEMENT_METADATA_KEY>;
17
- export declare class EntitlementGuard<TRequest = any> implements CanActivate {
18
- private readonly reflector;
16
+ declare const SetEntitlementMetadata: <TRequest = unknown>(entitlementKey: string, getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>) => MethodDecorator;
17
+ declare class EntitlementGuard<TRequest = unknown> implements CanActivate {
19
18
  private readonly config;
20
19
  private readonly runtime;
21
- constructor(reflector: Reflector, config: BlimuConfig<TRequest>, runtime: Blimu);
20
+ constructor(config: BlimuConfig<TRequest>, runtime: Blimu);
22
21
  canActivate(context: ExecutionContext): Promise<boolean>;
23
22
  }
24
- //# sourceMappingURL=entitlement.guard.d.ts.map
23
+
24
+ export { ENTITLEMENT_KEY, ENTITLEMENT_METADATA_KEY, EntitlementGuard, type EntitlementInfo, type EntitlementMetadata, SetEntitlementMetadata };
@@ -0,0 +1,154 @@
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/guards/entitlement.guard.ts
14
+ import {
15
+ ForbiddenException as ForbiddenException2,
16
+ Injectable,
17
+ SetMetadata,
18
+ Inject
19
+ } from "@nestjs/common";
20
+ import "reflect-metadata";
21
+
22
+ // src/exceptions/blimu-forbidden.exception.ts
23
+ import { ForbiddenException } from "@nestjs/common";
24
+ var BlimuForbiddenException = class _BlimuForbiddenException extends ForbiddenException {
25
+ /**
26
+ * The entitlement check result containing detailed failure information
27
+ */
28
+ entitlementResult;
29
+ /**
30
+ * The entitlement key that was checked
31
+ */
32
+ entitlementKey;
33
+ /**
34
+ * The resource ID that was checked
35
+ */
36
+ resourceId;
37
+ /**
38
+ * The user ID that was checked
39
+ */
40
+ userId;
41
+ constructor(entitlementResult, entitlementKey, resourceId, userId) {
42
+ const message = _BlimuForbiddenException.buildMessage(entitlementResult, entitlementKey);
43
+ super({
44
+ message,
45
+ entitlementResult,
46
+ entitlementKey,
47
+ resourceId,
48
+ userId
49
+ });
50
+ this.entitlementResult = entitlementResult;
51
+ this.entitlementKey = entitlementKey;
52
+ this.resourceId = resourceId;
53
+ this.userId = userId;
54
+ }
55
+ /**
56
+ * Builds a user-friendly error message from the entitlement check result
57
+ */
58
+ static buildMessage(result, entitlementKey) {
59
+ const reasons = [];
60
+ if (result.roles && !result.roles.allowed) {
61
+ reasons.push(
62
+ `Insufficient roles. Required: ${result.roles.allowedRoles?.join(", ") || "unknown"}. User has: ${result.roles.userRoles?.join(", ") || "none"}.`
63
+ );
64
+ }
65
+ if (result.plans && !result.plans.allowed) {
66
+ reasons.push(
67
+ `Plan restriction. Required plans: ${result.plans.allowedPlans?.join(", ") || "unknown"}. Current plan: ${result.plans.plan || "none"}.`
68
+ );
69
+ }
70
+ if (result.limit && !result.limit.allowed) {
71
+ reasons.push(`Usage limit exceeded. ${result.limit.reason || "Limit has been reached"}.`);
72
+ }
73
+ if (reasons.length === 0) {
74
+ return `Access denied for entitlement: ${entitlementKey}`;
75
+ }
76
+ return `Access denied for entitlement "${entitlementKey}": ${reasons.join(" ")}`;
77
+ }
78
+ };
79
+
80
+ // src/guards/entitlement.guard.ts
81
+ import { Blimu } from "@blimu/backend";
82
+
83
+ // src/config/blimu.config.ts
84
+ var BLIMU_CONFIG = /* @__PURE__ */ Symbol("BLIMU_CONFIG");
85
+
86
+ // src/guards/entitlement.guard.ts
87
+ var ENTITLEMENT_KEY = "entitlement";
88
+ var ENTITLEMENT_METADATA_KEY = /* @__PURE__ */ Symbol("entitlement");
89
+ var SetEntitlementMetadata = (entitlementKey, getEntitlementInfo) => SetMetadata(ENTITLEMENT_METADATA_KEY, {
90
+ entitlementKey,
91
+ getEntitlementInfo
92
+ });
93
+ var EntitlementGuard = class {
94
+ constructor(config, runtime) {
95
+ this.config = config;
96
+ this.runtime = runtime;
97
+ }
98
+ async canActivate(context) {
99
+ const request = context.switchToHttp().getRequest();
100
+ const handler = context.getHandler();
101
+ const metadata = Reflect.getMetadata(ENTITLEMENT_METADATA_KEY, handler);
102
+ if (!metadata) {
103
+ return true;
104
+ }
105
+ let userId;
106
+ try {
107
+ userId = await this.config.getUserId(request);
108
+ } catch {
109
+ throw new ForbiddenException2("Failed to extract user ID from request");
110
+ }
111
+ if (!userId) {
112
+ throw new ForbiddenException2("User ID is required for entitlement check");
113
+ }
114
+ const entitlementInfo = await metadata.getEntitlementInfo(request);
115
+ if (!entitlementInfo?.resourceId) {
116
+ throw new ForbiddenException2("Resource ID is required for entitlement check");
117
+ }
118
+ try {
119
+ const result = await this.runtime.entitlements.checkEntitlement({
120
+ userId,
121
+ entitlement: metadata.entitlementKey,
122
+ resourceId: entitlementInfo.resourceId,
123
+ ...entitlementInfo.amount !== void 0 ? { amount: entitlementInfo.amount } : {}
124
+ });
125
+ if (!result.allowed) {
126
+ throw new BlimuForbiddenException(
127
+ result,
128
+ metadata.entitlementKey,
129
+ entitlementInfo.resourceId,
130
+ userId
131
+ );
132
+ }
133
+ return true;
134
+ } catch (error) {
135
+ if (error instanceof BlimuForbiddenException || error instanceof ForbiddenException2) {
136
+ throw error;
137
+ }
138
+ console.error("Entitlement check failed:", error);
139
+ throw new ForbiddenException2("Failed to verify entitlements");
140
+ }
141
+ }
142
+ };
143
+ EntitlementGuard = __decorateClass([
144
+ Injectable(),
145
+ __decorateParam(0, Inject(BLIMU_CONFIG)),
146
+ __decorateParam(1, Inject(Blimu))
147
+ ], EntitlementGuard);
148
+ export {
149
+ ENTITLEMENT_KEY,
150
+ ENTITLEMENT_METADATA_KEY,
151
+ EntitlementGuard,
152
+ SetEntitlementMetadata
153
+ };
154
+ //# sourceMappingURL=entitlement.guard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/guards/entitlement.guard.ts","../../src/exceptions/blimu-forbidden.exception.ts","../../src/config/blimu.config.ts"],"sourcesContent":["import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Injectable,\n SetMetadata,\n Inject,\n} from '@nestjs/common';\nimport 'reflect-metadata';\n\nimport type { EntitlementType } from '@blimu/types';\nimport { BlimuForbiddenException } from '../exceptions/blimu-forbidden.exception';\nimport { Blimu } from '@blimu/backend';\nimport { BLIMU_CONFIG, type BlimuConfig } from 'config/blimu.config';\n\nexport const ENTITLEMENT_KEY = 'entitlement';\nexport const ENTITLEMENT_METADATA_KEY = Symbol('entitlement');\n\n/**\n * Entitlement information returned by the getEntitlementInfo callback\n */\nexport interface EntitlementInfo {\n resourceId: string;\n amount?: number; // Amount to check against usage limit (for consumption)\n}\n\n/**\n * Metadata interface for entitlement checks\n */\nexport interface EntitlementMetadata<TRequest = unknown> {\n entitlementKey: EntitlementType;\n getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>;\n}\n\n/**\n * Sets entitlement metadata for a route handler\n * @internal This is used internally by the @Entitlement decorator\n */\nexport const SetEntitlementMetadata = <TRequest = unknown>(\n entitlementKey: string,\n getEntitlementInfo: (request: TRequest) => EntitlementInfo | Promise<EntitlementInfo>,\n): MethodDecorator =>\n SetMetadata(ENTITLEMENT_METADATA_KEY, {\n entitlementKey,\n getEntitlementInfo,\n } as EntitlementMetadata<TRequest>);\n\n/**\n * Guard that checks if the authenticated user has the required entitlement on a resource\n *\n * This guard automatically:\n * 1. Extracts the user from the request\n * 2. Extracts the resource ID using the provided extractor function\n * 3. Calls the Blimu Runtime API to check entitlements\n * 4. Allows or denies access based on the result\n */\n@Injectable()\nexport class EntitlementGuard<TRequest = unknown> implements CanActivate {\n constructor(\n @Inject(BLIMU_CONFIG)\n private readonly config: BlimuConfig<TRequest>,\n @Inject(Blimu)\n private readonly runtime: Blimu,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<TRequest>();\n const handler = context.getHandler();\n const metadata = Reflect.getMetadata(ENTITLEMENT_METADATA_KEY, handler) as\n | EntitlementMetadata<TRequest>\n | undefined;\n\n if (!metadata) {\n // No entitlement check required\n return true;\n }\n\n // Extract user ID using the configured getUserId function\n let userId: string;\n try {\n userId = await this.config.getUserId(request);\n } catch {\n throw new ForbiddenException('Failed to extract user ID from request');\n }\n\n if (!userId) {\n throw new ForbiddenException('User ID is required for entitlement check');\n }\n\n // Extract entitlement info from request\n const entitlementInfo = await metadata.getEntitlementInfo(request);\n\n if (!entitlementInfo?.resourceId) {\n throw new ForbiddenException('Resource ID is required for entitlement check');\n }\n\n try {\n // Check entitlement\n const result = await this.runtime.entitlements.checkEntitlement({\n userId,\n entitlement: metadata.entitlementKey,\n resourceId: entitlementInfo.resourceId,\n ...(entitlementInfo.amount !== undefined ? { amount: entitlementInfo.amount } : {}),\n });\n\n if (!result.allowed) {\n throw new BlimuForbiddenException(\n result,\n metadata.entitlementKey,\n entitlementInfo.resourceId,\n userId,\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof BlimuForbiddenException || error instanceof ForbiddenException) {\n throw error;\n }\n\n // Log the error for debugging but don't expose internal details\n console.error('Entitlement check failed:', error);\n throw new ForbiddenException('Failed to verify entitlements');\n }\n }\n}\n","import { ForbiddenException } from '@nestjs/common';\nimport type { Schema } from '@blimu/backend';\nimport type { EntitlementType } from '@blimu/types';\n/**\n * Custom exception for Blimu entitlement check failures\n *\n * This exception extends NestJS's ForbiddenException and includes\n * the typed EntitlementCheckResult, providing detailed information\n * about why the entitlement check failed (roles, plans, limits, etc.)\n */\nexport class BlimuForbiddenException extends ForbiddenException {\n /**\n * The entitlement check result containing detailed failure information\n */\n public readonly entitlementResult: Schema.EntitlementCheckResult;\n\n /**\n * The entitlement key that was checked\n */\n public readonly entitlementKey: EntitlementType;\n\n /**\n * The resource ID that was checked\n */\n public readonly resourceId: string;\n\n /**\n * The user ID that was checked\n */\n public readonly userId: string;\n\n constructor(\n entitlementResult: Schema.EntitlementCheckResult,\n entitlementKey: EntitlementType,\n resourceId: string,\n userId: string,\n ) {\n // Create a user-friendly message based on the failure reason\n const message = BlimuForbiddenException.buildMessage(entitlementResult, entitlementKey);\n\n super({\n message,\n entitlementResult,\n entitlementKey,\n resourceId,\n userId,\n });\n\n this.entitlementResult = entitlementResult;\n this.entitlementKey = entitlementKey;\n this.resourceId = resourceId;\n this.userId = userId;\n }\n\n /**\n * Builds a user-friendly error message from the entitlement check result\n */\n private static buildMessage(\n result: Schema.EntitlementCheckResult,\n entitlementKey: EntitlementType,\n ): string {\n const reasons: string[] = [];\n\n if (result.roles && !result.roles.allowed) {\n reasons.push(\n `Insufficient roles. Required: ${result.roles.allowedRoles?.join(', ') || 'unknown'}. User has: ${result.roles.userRoles?.join(', ') || 'none'}.`,\n );\n }\n\n if (result.plans && !result.plans.allowed) {\n reasons.push(\n `Plan restriction. Required plans: ${result.plans.allowedPlans?.join(', ') || 'unknown'}. Current plan: ${result.plans.plan || 'none'}.`,\n );\n }\n\n if (result.limit && !result.limit.allowed) {\n reasons.push(`Usage limit exceeded. ${result.limit.reason || 'Limit has been reached'}.`);\n }\n\n if (reasons.length === 0) {\n return `Access denied for entitlement: ${entitlementKey}`;\n }\n\n return `Access denied for entitlement \"${entitlementKey}\": ${reasons.join(' ')}`;\n }\n}\n","/**\n * Configuration interface for Blimu NestJS integration\n */\nexport interface BlimuConfig<TRequest = unknown> {\n global?: boolean | undefined;\n /**\n * The API secret key for authenticating with Blimu Runtime API\n */\n apiKey: string;\n\n /**\n * The base URL for the Blimu Runtime API\n * @default 'https://api.blimu.dev'\n */\n baseURL?: string | undefined;\n\n /**\n * Environment ID for the Blimu environment\n * This will be used in future versions for environment-specific configurations\n */\n environmentId?: string | undefined;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeoutMs?: number | undefined;\n\n /**\n * Function to extract user ID from the request\n *\n * This function is called by the EntitlementGuard to determine which user\n * to check entitlements for. It should return the user ID as a string.\n *\n * @param request - The incoming HTTP request\n * @returns The user ID as a string, or a Promise that resolves to the user ID\n *\n * @example\n * ```typescript\n * // Extract from JWT token in Authorization header\n * getUserId: (req) => {\n * const token = req.headers.authorization?.replace('Bearer ', '');\n * const decoded = jwt.verify(token, secret);\n * return decoded.sub;\n * }\n *\n * // Extract from request.user (common with Passport.js)\n * getUserId: (req) => req.user?.id\n *\n * // Extract from custom header\n * getUserId: (req) => req.headers['x-user-id']\n * ```\n */\n getUserId: (request: TRequest) => string | Promise<string>;\n}\n\n/**\n * Injection token for Blimu configuration\n */\nexport const BLIMU_CONFIG = Symbol('BLIMU_CONFIG');\n"],"mappings":"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","names":["ForbiddenException","ForbiddenException"]}