@soulbatical/tetra-core 0.1.7 → 0.1.9

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 (37) hide show
  1. package/dist/index.d.ts +9 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +9 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/middleware/entitlementsMiddleware.d.ts +142 -0
  6. package/dist/middleware/entitlementsMiddleware.d.ts.map +1 -0
  7. package/dist/middleware/entitlementsMiddleware.js +246 -0
  8. package/dist/middleware/entitlementsMiddleware.js.map +1 -0
  9. package/dist/middleware/permissionsMiddleware.d.ts +181 -0
  10. package/dist/middleware/permissionsMiddleware.d.ts.map +1 -0
  11. package/dist/middleware/permissionsMiddleware.js +237 -0
  12. package/dist/middleware/permissionsMiddleware.js.map +1 -0
  13. package/dist/shared/affiliate/AffiliateAttributionService.d.ts +47 -0
  14. package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +1 -0
  15. package/dist/shared/affiliate/AffiliateAttributionService.js +308 -0
  16. package/dist/shared/affiliate/AffiliateAttributionService.js.map +1 -0
  17. package/dist/shared/affiliate/AffiliateClickService.d.ts +35 -0
  18. package/dist/shared/affiliate/AffiliateClickService.d.ts.map +1 -0
  19. package/dist/shared/affiliate/AffiliateClickService.js +87 -0
  20. package/dist/shared/affiliate/AffiliateClickService.js.map +1 -0
  21. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +11 -0
  22. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +1 -0
  23. package/dist/shared/affiliate/affiliateFeatureConfig.js +242 -0
  24. package/dist/shared/affiliate/affiliateFeatureConfig.js.map +1 -0
  25. package/dist/shared/affiliate/index.d.ts +11 -0
  26. package/dist/shared/affiliate/index.d.ts.map +1 -0
  27. package/dist/shared/affiliate/index.js +13 -0
  28. package/dist/shared/affiliate/index.js.map +1 -0
  29. package/dist/shared/affiliate/routes.d.ts +87 -0
  30. package/dist/shared/affiliate/routes.d.ts.map +1 -0
  31. package/dist/shared/affiliate/routes.js +404 -0
  32. package/dist/shared/affiliate/routes.js.map +1 -0
  33. package/dist/shared/affiliate/types.d.ts +170 -0
  34. package/dist/shared/affiliate/types.d.ts.map +1 -0
  35. package/dist/shared/affiliate/types.js +11 -0
  36. package/dist/shared/affiliate/types.js.map +1 -0
  37. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -52,6 +52,10 @@ export { validateQueryParams, sanitizeSearchTerm, validatePagination, validateSo
52
52
  export { registerFeatureValidators, batchRegisterValidators } from './middleware/autoRegisterValidators.js';
53
53
  export { configureSecurity } from './middleware/securityMiddleware.js';
54
54
  export type { SecurityConfig } from './middleware/securityMiddleware.js';
55
+ export { configureEntitlements, requireFeature, requireWithinLimit, hasFeature, getLimit, getOrgFeatures, invalidatePlanCache } from './middleware/entitlementsMiddleware.js';
56
+ export type { EntitlementsConfig, PlanFeatures, LimitCheckResult } from './middleware/entitlementsMiddleware.js';
57
+ export { configurePermissions, registerFeaturePermissions, requirePermission, requireAnyPermission, checkPermission, canSeeNav, canSeeTab, canDoAction, getAllPermissionConfigs, getPermissionsForRole } from './middleware/permissionsMiddleware.js';
58
+ export type { FeaturePermissions, RolePermissions, PermissionValue, UIPermissions } from './middleware/permissionsMiddleware.js';
55
59
  export { createLogger, rootLogger } from './utils/logger.js';
56
60
  export type { Logger } from './utils/logger.js';
57
61
  export { validateEnvironment } from './utils/validateEnvironment.js';
@@ -74,6 +78,11 @@ export { userDB } from './core/userDb.js';
74
78
  export { superadminDB } from './core/superadminDb.js';
75
79
  export { webhookDB } from './core/webhookDb.js';
76
80
  export { SupabaseUserClient } from './core/SupabaseUserClient.js';
81
+ export { AffiliateAttributionService } from './shared/affiliate/AffiliateAttributionService.js';
82
+ export { AffiliateClickService } from './shared/affiliate/AffiliateClickService.js';
83
+ export { defaultAffiliateFeatureConfig } from './shared/affiliate/affiliateFeatureConfig.js';
84
+ export { addAffiliateAdminRoutes, addAffiliateUserRoutes, addAffiliatePublicRoutes } from './shared/affiliate/routes.js';
85
+ export type { AffiliateConfig, AffiliateTierConfig, AffiliateAttribution, AffiliateOrder, Affiliate, AffiliateCommission, AffiliateClick, AffiliatePayment, AffiliateTierHistory, AffiliateFilters, AffiliateDashboard, AffiliateDashboardStats, AffiliateDashboardCommission, AffiliateTierProgress } from './shared/affiliate/types.js';
77
86
  export { createApp } from './core/createApp.js';
78
87
  export type { CreateAppConfig } from './core/createApp.js';
79
88
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -46,6 +46,10 @@ export { authenticateToken, authenticateTokenOnly, optionalAuth, requireSuperAdm
46
46
  export { validateQueryParams, sanitizeSearchTerm, validatePagination, validateSort, validateFields } from './middleware/validateQueryParams.js';
47
47
  export { registerFeatureValidators, batchRegisterValidators } from './middleware/autoRegisterValidators.js';
48
48
  export { configureSecurity } from './middleware/securityMiddleware.js';
49
+ // Entitlements (plan-based feature gating)
50
+ export { configureEntitlements, requireFeature, requireWithinLimit, hasFeature, getLimit, getOrgFeatures, invalidatePlanCache } from './middleware/entitlementsMiddleware.js';
51
+ // Permissions (config-driven RBAC)
52
+ export { configurePermissions, registerFeaturePermissions, requirePermission, requireAnyPermission, checkPermission, canSeeNav, canSeeTab, canDoAction, getAllPermissionConfigs, getPermissionsForRole } from './middleware/permissionsMiddleware.js';
49
53
  // ─── Utils ──────────────────────────────────────────────────
50
54
  export { createLogger, rootLogger } from './utils/logger.js';
51
55
  export { validateEnvironment } from './utils/validateEnvironment.js';
@@ -70,6 +74,11 @@ export { userDB } from './core/userDb.js';
70
74
  export { superadminDB } from './core/superadminDb.js';
71
75
  export { webhookDB } from './core/webhookDb.js';
72
76
  export { SupabaseUserClient } from './core/SupabaseUserClient.js';
77
+ // ─── Affiliate Module ──────────────────────────────────────
78
+ export { AffiliateAttributionService } from './shared/affiliate/AffiliateAttributionService.js';
79
+ export { AffiliateClickService } from './shared/affiliate/AffiliateClickService.js';
80
+ export { defaultAffiliateFeatureConfig } from './shared/affiliate/affiliateFeatureConfig.js';
81
+ export { addAffiliateAdminRoutes, addAffiliateUserRoutes, addAffiliatePublicRoutes } from './shared/affiliate/routes.js';
73
82
  // ─── App Bootstrap ──────────────────────────────────────────
74
83
  export { createApp } from './core/createApp.js';
75
84
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @tetra/core — Entitlements Middleware (Plan-Based Feature Gating)
3
+ *
4
+ * Controls what an ORGANIZATION can do based on their subscription plan.
5
+ * This is separate from auth/RBAC (what a USER can do based on their role).
6
+ *
7
+ * Evaluation order per request:
8
+ * 1. Auth → Is there a valid JWT?
9
+ * 2. Entitlement → Does this ORG's plan include this feature?
10
+ * 3. RBAC → Does this USER's role allow this action?
11
+ *
12
+ * Projects call configureEntitlements() once at startup with their plan definitions
13
+ * and a getPlanId function that reads the org's current plan from the database.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * configureEntitlements({
18
+ * plans: {
19
+ * free: { max_active_tracks: 3, calendar_sync: false },
20
+ * pro: { max_active_tracks: -1, calendar_sync: true },
21
+ * },
22
+ * getPlanId: async (orgId) => {
23
+ * const { data } = await systemDB('entitlements')
24
+ * .from('organizations').select('plan').eq('id', orgId).single();
25
+ * return data?.plan ?? 'free';
26
+ * },
27
+ * });
28
+ *
29
+ * // In routes:
30
+ * router.post('/upload', requireFeature('calendar_sync'), handler);
31
+ * router.post('/', requireWithinLimit('max_active_tracks', countFn), handler);
32
+ * ```
33
+ */
34
+ import { Response, NextFunction } from 'express';
35
+ import type { AuthenticatedRequest } from './authMiddleware.js';
36
+ /**
37
+ * Plan features map. Keys are feature names, values are:
38
+ * - boolean: feature is on/off
39
+ * - number: numeric limit (-1 = unlimited, 0 = disabled)
40
+ */
41
+ export type PlanFeatures = Record<string, boolean | number>;
42
+ /**
43
+ * Configuration for the entitlements system.
44
+ */
45
+ export interface EntitlementsConfig {
46
+ /**
47
+ * Plan definitions. Key is the plan ID (e.g., 'free', 'pro').
48
+ * Values are feature maps.
49
+ *
50
+ * @example
51
+ * plans: {
52
+ * free: { max_projects: 3, ai_autofix: false },
53
+ * pro: { max_projects: -1, ai_autofix: true },
54
+ * }
55
+ */
56
+ plans: Record<string, PlanFeatures>;
57
+ /**
58
+ * Async function to get the current plan ID for an organization.
59
+ * Called on each entitlement check (results are cached per request).
60
+ *
61
+ * @example
62
+ * getPlanId: async (orgId) => {
63
+ * const { data } = await db.from('organizations').select('plan').eq('id', orgId).single();
64
+ * return data?.plan ?? 'free';
65
+ * }
66
+ */
67
+ getPlanId: (organizationId: string) => Promise<string>;
68
+ /**
69
+ * Default plan ID when org has no plan or plan is unknown.
70
+ * Defaults to 'free'.
71
+ */
72
+ defaultPlanId?: string;
73
+ /**
74
+ * Cache TTL in milliseconds for plan lookups.
75
+ * Defaults to 60000 (1 minute).
76
+ */
77
+ cacheTtlMs?: number;
78
+ }
79
+ /**
80
+ * Result of a limit check.
81
+ */
82
+ export interface LimitCheckResult {
83
+ allowed: boolean;
84
+ current: number;
85
+ limit: number;
86
+ planId: string;
87
+ }
88
+ /**
89
+ * Configure the entitlements system. Call once at app startup.
90
+ */
91
+ export declare function configureEntitlements(entitlementsConfig: EntitlementsConfig): void;
92
+ /**
93
+ * Check if a plan has a specific feature enabled.
94
+ *
95
+ * - Boolean features: returns the boolean value
96
+ * - Numeric features: returns true if value !== 0
97
+ * - Unknown features: returns false (fail closed)
98
+ */
99
+ export declare function hasFeature(planId: string, feature: string): boolean;
100
+ /**
101
+ * Get the numeric limit for a feature on a plan.
102
+ * Returns -1 for unlimited, 0 for disabled.
103
+ */
104
+ export declare function getLimit(planId: string, feature: string): number;
105
+ /**
106
+ * Get all features for an organization (resolved from their plan).
107
+ * Useful for frontend: GET /api/org/entitlements
108
+ */
109
+ export declare function getOrgFeatures(organizationId: string): Promise<{
110
+ planId: string;
111
+ features: PlanFeatures;
112
+ }>;
113
+ /**
114
+ * Invalidate the plan cache for an organization.
115
+ * Call this after plan changes (e.g., Stripe webhook).
116
+ */
117
+ export declare function invalidatePlanCache(organizationId?: string): void;
118
+ /**
119
+ * Express middleware: require a feature to be enabled on the org's plan.
120
+ *
121
+ * @param feature - The feature key to check (must match a key in plan features)
122
+ *
123
+ * @example
124
+ * router.post('/upload', requireFeature('chat_attachments'), handler);
125
+ */
126
+ export declare function requireFeature(feature: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
127
+ /**
128
+ * Express middleware: enforce a numeric limit before creating a resource.
129
+ *
130
+ * @param feature - The feature key for the limit (e.g., 'max_active_tracks')
131
+ * @param countFn - Async function that returns the current usage count for the org
132
+ *
133
+ * @example
134
+ * const countTracks = async (orgId: string) => {
135
+ * const { count } = await db.from('tracks').select('id', { count: 'exact', head: true })
136
+ * .eq('organization_id', orgId).eq('status', 'active');
137
+ * return count ?? 0;
138
+ * };
139
+ * router.post('/', requireWithinLimit('max_active_tracks', countTracks), handler);
140
+ */
141
+ export declare function requireWithinLimit(feature: string, countFn: (organizationId: string) => Promise<number>): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
142
+ //# sourceMappingURL=entitlementsMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlementsMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/entitlementsMiddleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAMhE;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAEpC;;;;;;;;;OASG;IACH,SAAS,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvD;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAWD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,IAAI,CAOlF;AAgDD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAOnE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOhE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,CAAC,CAGhH;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAMjE;AAID;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,IAC9B,KAAK,oBAAoB,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,wDAiC3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,IAEtC,KAAK,oBAAoB,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,wDA6D3E"}
@@ -0,0 +1,246 @@
1
+ /**
2
+ * @tetra/core — Entitlements Middleware (Plan-Based Feature Gating)
3
+ *
4
+ * Controls what an ORGANIZATION can do based on their subscription plan.
5
+ * This is separate from auth/RBAC (what a USER can do based on their role).
6
+ *
7
+ * Evaluation order per request:
8
+ * 1. Auth → Is there a valid JWT?
9
+ * 2. Entitlement → Does this ORG's plan include this feature?
10
+ * 3. RBAC → Does this USER's role allow this action?
11
+ *
12
+ * Projects call configureEntitlements() once at startup with their plan definitions
13
+ * and a getPlanId function that reads the org's current plan from the database.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * configureEntitlements({
18
+ * plans: {
19
+ * free: { max_active_tracks: 3, calendar_sync: false },
20
+ * pro: { max_active_tracks: -1, calendar_sync: true },
21
+ * },
22
+ * getPlanId: async (orgId) => {
23
+ * const { data } = await systemDB('entitlements')
24
+ * .from('organizations').select('plan').eq('id', orgId).single();
25
+ * return data?.plan ?? 'free';
26
+ * },
27
+ * });
28
+ *
29
+ * // In routes:
30
+ * router.post('/upload', requireFeature('calendar_sync'), handler);
31
+ * router.post('/', requireWithinLimit('max_active_tracks', countFn), handler);
32
+ * ```
33
+ */
34
+ import { createLogger } from '../utils/logger.js';
35
+ import { RFC7807ErrorResponse } from '../shared/rfc7807ErrorResponse.js';
36
+ const logger = createLogger('middleware:entitlements');
37
+ // ─── State ──────────────────────────────────────────────────
38
+ let config = null;
39
+ // Simple in-memory cache: orgId → { planId, expiresAt }
40
+ const planCache = new Map();
41
+ // ─── Configure ──────────────────────────────────────────────
42
+ /**
43
+ * Configure the entitlements system. Call once at app startup.
44
+ */
45
+ export function configureEntitlements(entitlementsConfig) {
46
+ config = entitlementsConfig;
47
+ planCache.clear();
48
+ logger.info({ planCount: Object.keys(entitlementsConfig.plans).length }, 'Entitlements configured');
49
+ }
50
+ // ─── Helpers ────────────────────────────────────────────────
51
+ function getDefaultPlanId() {
52
+ return config?.defaultPlanId ?? 'free';
53
+ }
54
+ function getCacheTtl() {
55
+ return config?.cacheTtlMs ?? 60_000;
56
+ }
57
+ /**
58
+ * Get the plan ID for an organization, with caching.
59
+ */
60
+ async function getOrgPlanId(organizationId) {
61
+ if (!config)
62
+ return getDefaultPlanId();
63
+ const cached = planCache.get(organizationId);
64
+ if (cached && cached.expiresAt > Date.now()) {
65
+ return cached.planId;
66
+ }
67
+ try {
68
+ const planId = await config.getPlanId(organizationId);
69
+ const resolvedPlanId = planId && config.plans[planId] ? planId : getDefaultPlanId();
70
+ planCache.set(organizationId, {
71
+ planId: resolvedPlanId,
72
+ expiresAt: Date.now() + getCacheTtl(),
73
+ });
74
+ return resolvedPlanId;
75
+ }
76
+ catch (error) {
77
+ logger.error({ error, organizationId }, 'Failed to get org plan, falling back to default');
78
+ return getDefaultPlanId();
79
+ }
80
+ }
81
+ /**
82
+ * Get the features for a plan ID.
83
+ */
84
+ function getPlanFeatures(planId) {
85
+ return config?.plans[planId] ?? config?.plans[getDefaultPlanId()] ?? {};
86
+ }
87
+ // ─── Public API ─────────────────────────────────────────────
88
+ /**
89
+ * Check if a plan has a specific feature enabled.
90
+ *
91
+ * - Boolean features: returns the boolean value
92
+ * - Numeric features: returns true if value !== 0
93
+ * - Unknown features: returns false (fail closed)
94
+ */
95
+ export function hasFeature(planId, feature) {
96
+ const features = getPlanFeatures(planId);
97
+ const value = features[feature];
98
+ if (value === undefined)
99
+ return false;
100
+ if (typeof value === 'boolean')
101
+ return value;
102
+ return value !== 0;
103
+ }
104
+ /**
105
+ * Get the numeric limit for a feature on a plan.
106
+ * Returns -1 for unlimited, 0 for disabled.
107
+ */
108
+ export function getLimit(planId, feature) {
109
+ const features = getPlanFeatures(planId);
110
+ const value = features[feature];
111
+ if (value === undefined)
112
+ return 0;
113
+ if (typeof value === 'boolean')
114
+ return value ? -1 : 0;
115
+ return value;
116
+ }
117
+ /**
118
+ * Get all features for an organization (resolved from their plan).
119
+ * Useful for frontend: GET /api/org/entitlements
120
+ */
121
+ export async function getOrgFeatures(organizationId) {
122
+ const planId = await getOrgPlanId(organizationId);
123
+ return { planId, features: getPlanFeatures(planId) };
124
+ }
125
+ /**
126
+ * Invalidate the plan cache for an organization.
127
+ * Call this after plan changes (e.g., Stripe webhook).
128
+ */
129
+ export function invalidatePlanCache(organizationId) {
130
+ if (organizationId) {
131
+ planCache.delete(organizationId);
132
+ }
133
+ else {
134
+ planCache.clear();
135
+ }
136
+ }
137
+ // ─── Middleware ──────────────────────────────────────────────
138
+ /**
139
+ * Express middleware: require a feature to be enabled on the org's plan.
140
+ *
141
+ * @param feature - The feature key to check (must match a key in plan features)
142
+ *
143
+ * @example
144
+ * router.post('/upload', requireFeature('chat_attachments'), handler);
145
+ */
146
+ export function requireFeature(feature) {
147
+ return async (req, res, next) => {
148
+ if (!config) {
149
+ logger.warn('requireFeature called but entitlements not configured');
150
+ return next();
151
+ }
152
+ const orgId = req.user?.organizationId || req.user?.active_organization_id;
153
+ if (!orgId) {
154
+ return RFC7807ErrorResponse.unauthorized(res, 'Authentication with organization required');
155
+ }
156
+ // Superadmins bypass entitlement checks
157
+ if (req.user?.is_superadmin || req.user?.isSuperAdmin || req.user?.isSuperadmin) {
158
+ return next();
159
+ }
160
+ const planId = await getOrgPlanId(orgId);
161
+ const allowed = hasFeature(planId, feature);
162
+ if (!allowed) {
163
+ return res.status(403).json({
164
+ type: 'about:blank',
165
+ title: 'Feature not available on your plan',
166
+ detail: `The feature "${feature}" is not included in the "${planId}" plan. Please upgrade.`,
167
+ status: 403,
168
+ instance: req.originalUrl,
169
+ feature,
170
+ currentPlan: planId,
171
+ });
172
+ }
173
+ next();
174
+ };
175
+ }
176
+ /**
177
+ * Express middleware: enforce a numeric limit before creating a resource.
178
+ *
179
+ * @param feature - The feature key for the limit (e.g., 'max_active_tracks')
180
+ * @param countFn - Async function that returns the current usage count for the org
181
+ *
182
+ * @example
183
+ * const countTracks = async (orgId: string) => {
184
+ * const { count } = await db.from('tracks').select('id', { count: 'exact', head: true })
185
+ * .eq('organization_id', orgId).eq('status', 'active');
186
+ * return count ?? 0;
187
+ * };
188
+ * router.post('/', requireWithinLimit('max_active_tracks', countTracks), handler);
189
+ */
190
+ export function requireWithinLimit(feature, countFn) {
191
+ return async (req, res, next) => {
192
+ if (!config) {
193
+ logger.warn('requireWithinLimit called but entitlements not configured');
194
+ return next();
195
+ }
196
+ const orgId = req.user?.organizationId || req.user?.active_organization_id;
197
+ if (!orgId) {
198
+ return RFC7807ErrorResponse.unauthorized(res, 'Authentication with organization required');
199
+ }
200
+ // Superadmins bypass limit checks
201
+ if (req.user?.is_superadmin || req.user?.isSuperAdmin || req.user?.isSuperadmin) {
202
+ return next();
203
+ }
204
+ const planId = await getOrgPlanId(orgId);
205
+ const limit = getLimit(planId, feature);
206
+ // -1 means unlimited
207
+ if (limit === -1) {
208
+ return next();
209
+ }
210
+ // 0 means disabled
211
+ if (limit === 0) {
212
+ return res.status(403).json({
213
+ type: 'about:blank',
214
+ title: 'Feature not available on your plan',
215
+ detail: `The feature "${feature}" is not included in the "${planId}" plan. Please upgrade.`,
216
+ status: 403,
217
+ instance: req.originalUrl,
218
+ feature,
219
+ currentPlan: planId,
220
+ });
221
+ }
222
+ try {
223
+ const current = await countFn(orgId);
224
+ if (current >= limit) {
225
+ return res.status(403).json({
226
+ type: 'about:blank',
227
+ title: 'Plan limit reached',
228
+ detail: `You have reached the limit of ${limit} for "${feature}" on the "${planId}" plan.`,
229
+ status: 403,
230
+ instance: req.originalUrl,
231
+ feature,
232
+ currentPlan: planId,
233
+ current,
234
+ limit,
235
+ });
236
+ }
237
+ next();
238
+ }
239
+ catch (error) {
240
+ logger.error({ error, orgId, feature }, 'Failed to check usage limit');
241
+ // Fail open for count errors — the DB will catch any real violations
242
+ next();
243
+ }
244
+ };
245
+ }
246
+ //# sourceMappingURL=entitlementsMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlementsMiddleware.js","sourceRoot":"","sources":["../../src/middleware/entitlementsMiddleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * @tetra/core — Permissions Middleware (Config-Driven RBAC)
3
+ *
4
+ * Controls what a USER can do based on their role within a feature.
5
+ * This is separate from entitlements (what an ORG can do based on their plan).
6
+ *
7
+ * Projects call configurePermissions() once at startup with their feature
8
+ * permission configs. Each feature defines which roles can create/read/update/delete.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * configurePermissions({
13
+ * notes: {
14
+ * resource: 'notes',
15
+ * roles: {
16
+ * admin: { create: true, read: true, update: true, delete: true },
17
+ * coach: { create: true, read: true, update: 'own', delete: 'own' },
18
+ * client: { create: false, read: true, update: false, delete: false },
19
+ * },
20
+ * ui: {
21
+ * nav: ['admin', 'coach'],
22
+ * tabs: { notities: ['admin', 'coach'] },
23
+ * actions: { create_note: ['admin', 'coach'] },
24
+ * },
25
+ * },
26
+ * });
27
+ *
28
+ * // In routes:
29
+ * router.post('/', requirePermission('notes', 'create'), handler);
30
+ * router.delete('/:id', requirePermission('notes', 'delete'), handler);
31
+ * ```
32
+ */
33
+ import { Response, NextFunction } from 'express';
34
+ import type { AuthenticatedRequest } from './authMiddleware.js';
35
+ /**
36
+ * Permission value for a role on a specific action.
37
+ * - true: allowed (all records)
38
+ * - false: denied
39
+ * - 'own': allowed on own records only (ownerField must match user id)
40
+ * - 'track_member': allowed on records within tracks the user is a member of
41
+ * - string: custom scope (project-specific, middleware passes through)
42
+ */
43
+ export type PermissionValue = boolean | 'own' | 'track_member' | string;
44
+ /**
45
+ * CRUD permissions for a role on a feature.
46
+ */
47
+ export interface RolePermissions {
48
+ create?: PermissionValue;
49
+ read?: PermissionValue;
50
+ update?: PermissionValue;
51
+ delete?: PermissionValue;
52
+ list?: PermissionValue;
53
+ /** Custom actions beyond CRUD */
54
+ [action: string]: PermissionValue | undefined;
55
+ }
56
+ /**
57
+ * UI visibility config for a feature.
58
+ * Used by frontend usePermissions() hook.
59
+ */
60
+ export interface UIPermissions {
61
+ /** Which roles see this feature in the nav/sidebar */
62
+ nav?: string[];
63
+ /** Per-tab visibility: { tabId: [roles] } */
64
+ tabs?: Record<string, string[]>;
65
+ /** Per-action visibility: { actionId: [roles] } */
66
+ actions?: Record<string, string[]>;
67
+ /** Per-field visibility: { fieldName: [roles] } */
68
+ fields?: Record<string, string[]>;
69
+ }
70
+ /**
71
+ * Permission config for a single feature.
72
+ */
73
+ export interface FeaturePermissions {
74
+ /** The resource name (e.g., 'notes', 'tracks', 'messages') */
75
+ resource: string;
76
+ /** Permission matrix: role → action → permission value */
77
+ roles: Record<string, RolePermissions>;
78
+ /** UI visibility rules (consumed by frontend) */
79
+ ui?: UIPermissions;
80
+ }
81
+ /**
82
+ * Register permission configs for features. Call once at app startup.
83
+ * Can be called multiple times to add more features.
84
+ *
85
+ * @param configs - Map of resource name → FeaturePermissions
86
+ *
87
+ * @example
88
+ * configurePermissions({
89
+ * notes: notesPermissions,
90
+ * tracks: tracksPermissions,
91
+ * });
92
+ */
93
+ export declare function configurePermissions(configs: Record<string, FeaturePermissions>): void;
94
+ /**
95
+ * Register a single feature's permissions.
96
+ */
97
+ export declare function registerFeaturePermissions(config: FeaturePermissions): void;
98
+ /**
99
+ * Check if a role is allowed to perform an action on a resource.
100
+ *
101
+ * Returns the PermissionValue:
102
+ * - true: allowed
103
+ * - false: denied
104
+ * - 'own': allowed with scope restriction
105
+ * - string: custom scope
106
+ *
107
+ * Unknown roles/resources/actions default to false (fail closed).
108
+ */
109
+ export declare function checkPermission(resource: string, action: string, role: string): PermissionValue;
110
+ /**
111
+ * Check if a role can see a nav item for a resource.
112
+ */
113
+ export declare function canSeeNav(resource: string, role: string): boolean;
114
+ /**
115
+ * Check if a role can see a specific tab within a resource.
116
+ */
117
+ export declare function canSeeTab(resource: string, tabId: string, role: string): boolean;
118
+ /**
119
+ * Check if a role can perform a specific UI action.
120
+ */
121
+ export declare function canDoAction(resource: string, actionId: string, role: string): boolean;
122
+ /**
123
+ * Get all permission configs. Useful for:
124
+ * - Frontend: GET /api/permissions → returns all UI permissions for the user's role
125
+ * - Documentation: generate permission matrices
126
+ */
127
+ export declare function getAllPermissionConfigs(): Record<string, FeaturePermissions>;
128
+ /**
129
+ * Get the resolved permissions for a specific role across all features.
130
+ * Returns only UI-relevant data for the frontend.
131
+ */
132
+ export declare function getPermissionsForRole(role: string): Record<string, {
133
+ nav: boolean;
134
+ tabs: Record<string, boolean>;
135
+ actions: Record<string, boolean>;
136
+ crud: Record<string, PermissionValue>;
137
+ }>;
138
+ /**
139
+ * Express middleware: require a specific permission on a resource.
140
+ *
141
+ * Checks the user's role against the registered permission config.
142
+ * Superadmins bypass all permission checks.
143
+ *
144
+ * When the permission value is 'own' or another scope string,
145
+ * the middleware allows the request but attaches `req.permissionScope`
146
+ * so the controller can apply the appropriate filter.
147
+ *
148
+ * @param resource - The resource name (must match a registered config)
149
+ * @param action - The action to check (create, read, update, delete, or custom)
150
+ *
151
+ * @example
152
+ * router.post('/', requirePermission('notes', 'create'), handler);
153
+ * router.delete('/:id', requirePermission('notes', 'delete'), handler);
154
+ *
155
+ * // In controller, check scope:
156
+ * if (req.permissionScope === 'own') {
157
+ * query = query.eq('author_id', req.user.id);
158
+ * }
159
+ */
160
+ export declare function requirePermission(resource: string, action: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
161
+ /**
162
+ * Express middleware: require ANY of the listed permissions.
163
+ *
164
+ * @example
165
+ * router.get('/feed', requireAnyPermission([
166
+ * { resource: 'notes', action: 'read' },
167
+ * { resource: 'messages', action: 'read' },
168
+ * ]), handler);
169
+ */
170
+ export declare function requireAnyPermission(permissions: Array<{
171
+ resource: string;
172
+ action: string;
173
+ }>): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
174
+ declare global {
175
+ namespace Express {
176
+ interface Request {
177
+ permissionScope?: string;
178
+ }
179
+ }
180
+ }
181
+ //# sourceMappingURL=permissionsMiddleware.d.ts.map