@grantjs/server 1.0.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +443 -0
  3. package/dist/debug-C8ibbrE5.js +9 -0
  4. package/dist/debug-C8ibbrE5.js.map +1 -0
  5. package/dist/debug-Crb5gNDM.cjs +8 -0
  6. package/dist/debug-Crb5gNDM.cjs.map +1 -0
  7. package/dist/errors.d.ts +38 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/express/index.d.ts +6 -0
  10. package/dist/express/index.d.ts.map +1 -0
  11. package/dist/express/middleware.d.ts +38 -0
  12. package/dist/express/middleware.d.ts.map +1 -0
  13. package/dist/express.cjs +99 -0
  14. package/dist/express.cjs.map +1 -0
  15. package/dist/express.mjs +101 -0
  16. package/dist/express.mjs.map +1 -0
  17. package/dist/fastify/index.d.ts +6 -0
  18. package/dist/fastify/index.d.ts.map +1 -0
  19. package/dist/fastify/plugin.d.ts +74 -0
  20. package/dist/fastify/plugin.d.ts.map +1 -0
  21. package/dist/fastify.cjs +103 -0
  22. package/dist/fastify.cjs.map +1 -0
  23. package/dist/fastify.mjs +104 -0
  24. package/dist/fastify.mjs.map +1 -0
  25. package/dist/grant-client-D1LZI2f4.js +180 -0
  26. package/dist/grant-client-D1LZI2f4.js.map +1 -0
  27. package/dist/grant-client-DywfJN5P.cjs +179 -0
  28. package/dist/grant-client-DywfJN5P.cjs.map +1 -0
  29. package/dist/grant-client.d.ts +50 -0
  30. package/dist/grant-client.d.ts.map +1 -0
  31. package/dist/index.cjs +50 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.ts +6 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.mjs +50 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/nest/grant.decorator.d.ts +38 -0
  38. package/dist/nest/grant.decorator.d.ts.map +1 -0
  39. package/dist/nest/grant.guard.d.ts +29 -0
  40. package/dist/nest/grant.guard.d.ts.map +1 -0
  41. package/dist/nest/grant.module.d.ts +28 -0
  42. package/dist/nest/grant.module.d.ts.map +1 -0
  43. package/dist/nest/index.d.ts +8 -0
  44. package/dist/nest/index.d.ts.map +1 -0
  45. package/dist/nest.cjs +154 -0
  46. package/dist/nest.cjs.map +1 -0
  47. package/dist/nest.mjs +156 -0
  48. package/dist/nest.mjs.map +1 -0
  49. package/dist/next/index.d.ts +6 -0
  50. package/dist/next/index.d.ts.map +1 -0
  51. package/dist/next/with-grant.d.ts +57 -0
  52. package/dist/next/with-grant.d.ts.map +1 -0
  53. package/dist/next.cjs +62 -0
  54. package/dist/next.cjs.map +1 -0
  55. package/dist/next.mjs +63 -0
  56. package/dist/next.mjs.map +1 -0
  57. package/dist/types.d.ts +92 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/utils/debug.d.ts +9 -0
  60. package/dist/utils/debug.d.ts.map +1 -0
  61. package/dist/utils/token-extractor.d.ts +14 -0
  62. package/dist/utils/token-extractor.d.ts.map +1 -0
  63. package/package.json +109 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-client-D1LZI2f4.js","sources":["../src/utils/token-extractor.ts","../src/grant-client.ts"],"sourcesContent":["import type { GrantServerConfig } from '../types';\n\n/**\n * Extract Bearer token from Authorization header\n */\nexport function extractBearerToken(authHeader: string | undefined): string | null {\n if (!authHeader) return null;\n\n const parts = authHeader.trim().split(/\\s+/);\n if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {\n return null;\n }\n\n return parts[1];\n}\n\n/**\n * Parse cookie header string into key-value pairs\n */\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n cookieHeader.split(';').forEach((cookie) => {\n const [name, ...rest] = cookie.trim().split('=');\n if (name && rest.length > 0) {\n cookies[name.trim()] = rest.join('=').trim();\n }\n });\n\n return cookies;\n}\n\n/**\n * Extract token from cookies\n */\nexport function extractTokenFromCookies(\n cookieHeader: string | undefined,\n cookieName: string\n): string | null {\n if (!cookieHeader) return null;\n\n const cookies = parseCookieHeader(cookieHeader);\n return cookies[cookieName] || null;\n}\n\n/**\n * Extract token from Express request\n */\nexport async function extractTokenFromRequest(\n request: unknown,\n config: GrantServerConfig\n): Promise<string | null> {\n // Use custom token resolver if provided\n if (config.getToken) {\n const token = await config.getToken(request);\n if (token) return token;\n }\n\n // Try to extract from request (Express-style or Web API / NextRequest)\n const req = request as {\n headers?:\n | { authorization?: string; cookie?: string }\n | { get: (name: string) => string | null };\n cookies?: Record<string, string>;\n };\n\n if (!req?.headers) return null;\n\n // Web API Headers (NextRequest, Request): headers.get(name)\n const headersGet = (req.headers as { get?: (name: string) => string | null }).get;\n if (typeof headersGet === 'function') {\n const authHeader = headersGet.call(req.headers, 'authorization');\n const bearerToken = extractBearerToken(authHeader ?? undefined);\n if (bearerToken) return bearerToken;\n const cookieName = config.cookieName || 'grant-access-token';\n const cookieHeader = headersGet.call(req.headers, 'cookie');\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n return null;\n }\n\n // Express-style: headers.authorization, headers.cookie\n const plainHeaders = req.headers as { authorization?: string; cookie?: string };\n const authHeader = plainHeaders.authorization;\n const bearerToken = extractBearerToken(authHeader);\n if (bearerToken) return bearerToken;\n\n const cookieName = config.cookieName || 'grant-access-token';\n\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n const cookieHeader = plainHeaders.cookie;\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n\n return null;\n}\n","import { extractTokenFromRequest } from './utils/token-extractor';\n\nimport type { GrantServerConfig, AuthorizationResult, PermissionCheckOptions } from './types';\n\n/**\n * Grant Client for server-side applications\n *\n * Makes HTTP requests to the Grant API to check permissions.\n * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.\n * No caching - server-side caching is handled by grant-api.\n */\nexport class GrantClient {\n public readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;\n\n constructor(config: GrantServerConfig) {\n this.config = config;\n }\n\n // ============================================================================\n // Public API - Permission Checks\n // ============================================================================\n\n /**\n * Check if the current user has a specific permission\n *\n * @example\n * ```ts\n * const canEdit = await grant.isGranted('document', 'update', { scope });\n * if (canEdit) {\n * // Proceed with operation\n * }\n * ```\n */\n async isGranted(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<boolean> {\n const result = await this.isAuthorized(resource, action, options, request);\n return result.authorized;\n }\n\n /**\n * Check authorization with full result details\n *\n * @example\n * ```ts\n * const result = await grant.isAuthorized('document', 'update', { scope }, request);\n * if (!result.authorized) {\n * console.log('Denied:', result.reason);\n * }\n * ```\n */\n async isAuthorized(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<AuthorizationResult> {\n try {\n // API expects: { permission: { resource, action }, context: { resource?: any }, scope?: { tenant, id } }\n const response = await this.fetchWithAuth(\n '/api/auth/is-authorized',\n {\n method: 'POST',\n body: JSON.stringify({\n permission: {\n resource,\n action,\n },\n context: {\n resource: options?.context?.resource || null,\n },\n }),\n },\n request\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n return {\n authorized: false,\n reason: error.message || `API error: ${response.status}`,\n };\n }\n\n const json = await response.json();\n // API returns { success: true, data: { authorized, ... } }\n const result: AuthorizationResult = json.data ?? json;\n return result;\n } catch (error) {\n return {\n authorized: false,\n reason: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Extract token from a request object\n * Useful for middleware that needs to extract token before making authorization calls\n */\n async getTokenFromRequest(request: unknown): Promise<string | null> {\n return extractTokenFromRequest(request, this.config);\n }\n\n // ============================================================================\n // Private - HTTP & Authentication\n // ============================================================================\n\n /**\n * Make an authenticated fetch request (token from request; no refresh).\n */\n private async fetchWithAuth(\n url: string,\n init?: RequestInit,\n request?: unknown\n ): Promise<Response> {\n return this.doFetch(url, init, request);\n }\n\n /**\n * Perform the actual fetch request\n */\n private async doFetch(url: string, init?: RequestInit, request?: unknown): Promise<Response> {\n const fetchFn = this.config.fetch ?? globalThis.fetch;\n const fullUrl = url.startsWith('http') ? url : `${this.config.apiUrl}${url}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(init?.headers as Record<string, string>),\n };\n\n // Add authorization header if token is available\n if (request) {\n const token = await extractTokenFromRequest(request, this.config);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Forward cookies if using cookie-based auth\n const req = request as { headers?: { cookie?: string } };\n if (req.headers?.cookie) {\n headers['Cookie'] = req.headers.cookie;\n }\n }\n\n return fetchFn(fullUrl, {\n ...init,\n headers,\n // Include cookies for same-origin requests (supports cookie-based auth)\n credentials: this.config.credentials ?? 'include',\n });\n }\n}\n"],"names":["authHeader","bearerToken","cookieName","cookieHeader"],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ function extractBearerToken(authHeader) {
6
+ if (!authHeader) return null;
7
+ const parts = authHeader.trim().split(/\s+/);
8
+ if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
9
+ return null;
10
+ }
11
+ return parts[1];
12
+ }
13
+ function parseCookieHeader(cookieHeader) {
14
+ const cookies = {};
15
+ cookieHeader.split(";").forEach((cookie) => {
16
+ const [name, ...rest] = cookie.trim().split("=");
17
+ if (name && rest.length > 0) {
18
+ cookies[name.trim()] = rest.join("=").trim();
19
+ }
20
+ });
21
+ return cookies;
22
+ }
23
+ function extractTokenFromCookies(cookieHeader, cookieName) {
24
+ if (!cookieHeader) return null;
25
+ const cookies = parseCookieHeader(cookieHeader);
26
+ return cookies[cookieName] || null;
27
+ }
28
+ async function extractTokenFromRequest(request, config) {
29
+ if (config.getToken) {
30
+ const token = await config.getToken(request);
31
+ if (token) return token;
32
+ }
33
+ const req = request;
34
+ if (!req?.headers) return null;
35
+ const headersGet = req.headers.get;
36
+ if (typeof headersGet === "function") {
37
+ const authHeader2 = headersGet.call(req.headers, "authorization");
38
+ const bearerToken2 = extractBearerToken(authHeader2 ?? void 0);
39
+ if (bearerToken2) return bearerToken2;
40
+ const cookieName2 = config.cookieName || "grant-access-token";
41
+ const cookieHeader2 = headersGet.call(req.headers, "cookie");
42
+ if (cookieHeader2) {
43
+ const cookieToken = extractTokenFromCookies(cookieHeader2, cookieName2);
44
+ if (cookieToken) return cookieToken;
45
+ }
46
+ return null;
47
+ }
48
+ const plainHeaders = req.headers;
49
+ const authHeader = plainHeaders.authorization;
50
+ const bearerToken = extractBearerToken(authHeader);
51
+ if (bearerToken) return bearerToken;
52
+ const cookieName = config.cookieName || "grant-access-token";
53
+ if (req.cookies?.[cookieName]) {
54
+ return req.cookies[cookieName];
55
+ }
56
+ const cookieHeader = plainHeaders.cookie;
57
+ if (cookieHeader) {
58
+ const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);
59
+ if (cookieToken) return cookieToken;
60
+ }
61
+ return null;
62
+ }
63
+ class GrantClient {
64
+ constructor(config) {
65
+ __publicField(this, "config");
66
+ this.config = config;
67
+ }
68
+ // ============================================================================
69
+ // Public API - Permission Checks
70
+ // ============================================================================
71
+ /**
72
+ * Check if the current user has a specific permission
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const canEdit = await grant.isGranted('document', 'update', { scope });
77
+ * if (canEdit) {
78
+ * // Proceed with operation
79
+ * }
80
+ * ```
81
+ */
82
+ async isGranted(resource, action, options, request) {
83
+ const result = await this.isAuthorized(resource, action, options, request);
84
+ return result.authorized;
85
+ }
86
+ /**
87
+ * Check authorization with full result details
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const result = await grant.isAuthorized('document', 'update', { scope }, request);
92
+ * if (!result.authorized) {
93
+ * console.log('Denied:', result.reason);
94
+ * }
95
+ * ```
96
+ */
97
+ async isAuthorized(resource, action, options, request) {
98
+ try {
99
+ const response = await this.fetchWithAuth(
100
+ "/api/auth/is-authorized",
101
+ {
102
+ method: "POST",
103
+ body: JSON.stringify({
104
+ permission: {
105
+ resource,
106
+ action
107
+ },
108
+ context: {
109
+ resource: options?.context?.resource || null
110
+ }
111
+ })
112
+ },
113
+ request
114
+ );
115
+ if (!response.ok) {
116
+ const error = await response.json().catch(() => ({}));
117
+ return {
118
+ authorized: false,
119
+ reason: error.message || `API error: ${response.status}`
120
+ };
121
+ }
122
+ const json = await response.json();
123
+ const result = json.data ?? json;
124
+ return result;
125
+ } catch (error) {
126
+ return {
127
+ authorized: false,
128
+ reason: error instanceof Error ? error.message : "Unknown error"
129
+ };
130
+ }
131
+ }
132
+ /**
133
+ * Extract token from a request object
134
+ * Useful for middleware that needs to extract token before making authorization calls
135
+ */
136
+ async getTokenFromRequest(request) {
137
+ return extractTokenFromRequest(request, this.config);
138
+ }
139
+ // ============================================================================
140
+ // Private - HTTP & Authentication
141
+ // ============================================================================
142
+ /**
143
+ * Make an authenticated fetch request (token from request; no refresh).
144
+ */
145
+ async fetchWithAuth(url, init, request) {
146
+ return this.doFetch(url, init, request);
147
+ }
148
+ /**
149
+ * Perform the actual fetch request
150
+ */
151
+ async doFetch(url, init, request) {
152
+ const fetchFn = this.config.fetch ?? globalThis.fetch;
153
+ const fullUrl = url.startsWith("http") ? url : `${this.config.apiUrl}${url}`;
154
+ const headers = {
155
+ "Content-Type": "application/json",
156
+ ...init?.headers
157
+ };
158
+ if (request) {
159
+ const token = await extractTokenFromRequest(request, this.config);
160
+ if (token) {
161
+ headers["Authorization"] = `Bearer ${token}`;
162
+ }
163
+ const req = request;
164
+ if (req.headers?.cookie) {
165
+ headers["Cookie"] = req.headers.cookie;
166
+ }
167
+ }
168
+ return fetchFn(fullUrl, {
169
+ ...init,
170
+ headers,
171
+ // Include cookies for same-origin requests (supports cookie-based auth)
172
+ credentials: this.config.credentials ?? "include"
173
+ });
174
+ }
175
+ }
176
+ exports.GrantClient = GrantClient;
177
+ exports.extractBearerToken = extractBearerToken;
178
+ exports.extractTokenFromRequest = extractTokenFromRequest;
179
+ //# sourceMappingURL=grant-client-DywfJN5P.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-client-DywfJN5P.cjs","sources":["../src/utils/token-extractor.ts","../src/grant-client.ts"],"sourcesContent":["import type { GrantServerConfig } from '../types';\n\n/**\n * Extract Bearer token from Authorization header\n */\nexport function extractBearerToken(authHeader: string | undefined): string | null {\n if (!authHeader) return null;\n\n const parts = authHeader.trim().split(/\\s+/);\n if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {\n return null;\n }\n\n return parts[1];\n}\n\n/**\n * Parse cookie header string into key-value pairs\n */\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n cookieHeader.split(';').forEach((cookie) => {\n const [name, ...rest] = cookie.trim().split('=');\n if (name && rest.length > 0) {\n cookies[name.trim()] = rest.join('=').trim();\n }\n });\n\n return cookies;\n}\n\n/**\n * Extract token from cookies\n */\nexport function extractTokenFromCookies(\n cookieHeader: string | undefined,\n cookieName: string\n): string | null {\n if (!cookieHeader) return null;\n\n const cookies = parseCookieHeader(cookieHeader);\n return cookies[cookieName] || null;\n}\n\n/**\n * Extract token from Express request\n */\nexport async function extractTokenFromRequest(\n request: unknown,\n config: GrantServerConfig\n): Promise<string | null> {\n // Use custom token resolver if provided\n if (config.getToken) {\n const token = await config.getToken(request);\n if (token) return token;\n }\n\n // Try to extract from request (Express-style or Web API / NextRequest)\n const req = request as {\n headers?:\n | { authorization?: string; cookie?: string }\n | { get: (name: string) => string | null };\n cookies?: Record<string, string>;\n };\n\n if (!req?.headers) return null;\n\n // Web API Headers (NextRequest, Request): headers.get(name)\n const headersGet = (req.headers as { get?: (name: string) => string | null }).get;\n if (typeof headersGet === 'function') {\n const authHeader = headersGet.call(req.headers, 'authorization');\n const bearerToken = extractBearerToken(authHeader ?? undefined);\n if (bearerToken) return bearerToken;\n const cookieName = config.cookieName || 'grant-access-token';\n const cookieHeader = headersGet.call(req.headers, 'cookie');\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n return null;\n }\n\n // Express-style: headers.authorization, headers.cookie\n const plainHeaders = req.headers as { authorization?: string; cookie?: string };\n const authHeader = plainHeaders.authorization;\n const bearerToken = extractBearerToken(authHeader);\n if (bearerToken) return bearerToken;\n\n const cookieName = config.cookieName || 'grant-access-token';\n\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n const cookieHeader = plainHeaders.cookie;\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n\n return null;\n}\n","import { extractTokenFromRequest } from './utils/token-extractor';\n\nimport type { GrantServerConfig, AuthorizationResult, PermissionCheckOptions } from './types';\n\n/**\n * Grant Client for server-side applications\n *\n * Makes HTTP requests to the Grant API to check permissions.\n * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.\n * No caching - server-side caching is handled by grant-api.\n */\nexport class GrantClient {\n public readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;\n\n constructor(config: GrantServerConfig) {\n this.config = config;\n }\n\n // ============================================================================\n // Public API - Permission Checks\n // ============================================================================\n\n /**\n * Check if the current user has a specific permission\n *\n * @example\n * ```ts\n * const canEdit = await grant.isGranted('document', 'update', { scope });\n * if (canEdit) {\n * // Proceed with operation\n * }\n * ```\n */\n async isGranted(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<boolean> {\n const result = await this.isAuthorized(resource, action, options, request);\n return result.authorized;\n }\n\n /**\n * Check authorization with full result details\n *\n * @example\n * ```ts\n * const result = await grant.isAuthorized('document', 'update', { scope }, request);\n * if (!result.authorized) {\n * console.log('Denied:', result.reason);\n * }\n * ```\n */\n async isAuthorized(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<AuthorizationResult> {\n try {\n // API expects: { permission: { resource, action }, context: { resource?: any }, scope?: { tenant, id } }\n const response = await this.fetchWithAuth(\n '/api/auth/is-authorized',\n {\n method: 'POST',\n body: JSON.stringify({\n permission: {\n resource,\n action,\n },\n context: {\n resource: options?.context?.resource || null,\n },\n }),\n },\n request\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n return {\n authorized: false,\n reason: error.message || `API error: ${response.status}`,\n };\n }\n\n const json = await response.json();\n // API returns { success: true, data: { authorized, ... } }\n const result: AuthorizationResult = json.data ?? json;\n return result;\n } catch (error) {\n return {\n authorized: false,\n reason: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Extract token from a request object\n * Useful for middleware that needs to extract token before making authorization calls\n */\n async getTokenFromRequest(request: unknown): Promise<string | null> {\n return extractTokenFromRequest(request, this.config);\n }\n\n // ============================================================================\n // Private - HTTP & Authentication\n // ============================================================================\n\n /**\n * Make an authenticated fetch request (token from request; no refresh).\n */\n private async fetchWithAuth(\n url: string,\n init?: RequestInit,\n request?: unknown\n ): Promise<Response> {\n return this.doFetch(url, init, request);\n }\n\n /**\n * Perform the actual fetch request\n */\n private async doFetch(url: string, init?: RequestInit, request?: unknown): Promise<Response> {\n const fetchFn = this.config.fetch ?? globalThis.fetch;\n const fullUrl = url.startsWith('http') ? url : `${this.config.apiUrl}${url}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(init?.headers as Record<string, string>),\n };\n\n // Add authorization header if token is available\n if (request) {\n const token = await extractTokenFromRequest(request, this.config);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Forward cookies if using cookie-based auth\n const req = request as { headers?: { cookie?: string } };\n if (req.headers?.cookie) {\n headers['Cookie'] = req.headers.cookie;\n }\n }\n\n return fetchFn(fullUrl, {\n ...init,\n headers,\n // Include cookies for same-origin requests (supports cookie-based auth)\n credentials: this.config.credentials ?? 'include',\n });\n }\n}\n"],"names":["authHeader","bearerToken","cookieName","cookieHeader"],"mappings":";;;;AAKO,SAAS,mBAAmB,YAA+C;AAChF,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,QAAQ,WAAW,KAAA,EAAO,MAAM,KAAK;AAC3C,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,EAAE,YAAA,MAAkB,UAAU;AAC7D,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,CAAC;AAChB;AAKA,SAAS,kBAAkB,cAA8C;AACvE,QAAM,UAAkC,CAAA;AAExC,eAAa,MAAM,GAAG,EAAE,QAAQ,CAAC,WAAW;AAC1C,UAAM,CAAC,MAAM,GAAG,IAAI,IAAI,OAAO,KAAA,EAAO,MAAM,GAAG;AAC/C,QAAI,QAAQ,KAAK,SAAS,GAAG;AAC3B,cAAQ,KAAK,MAAM,IAAI,KAAK,KAAK,GAAG,EAAE,KAAA;AAAA,IACxC;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAKO,SAAS,wBACd,cACA,YACe;AACf,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,UAAU,kBAAkB,YAAY;AAC9C,SAAO,QAAQ,UAAU,KAAK;AAChC;AAKA,eAAsB,wBACpB,SACA,QACwB;AAExB,MAAI,OAAO,UAAU;AACnB,UAAM,QAAQ,MAAM,OAAO,SAAS,OAAO;AAC3C,QAAI,MAAO,QAAO;AAAA,EACpB;AAGA,QAAM,MAAM;AAOZ,MAAI,CAAC,KAAK,QAAS,QAAO;AAG1B,QAAM,aAAc,IAAI,QAAsD;AAC9E,MAAI,OAAO,eAAe,YAAY;AACpC,UAAMA,cAAa,WAAW,KAAK,IAAI,SAAS,eAAe;AAC/D,UAAMC,eAAc,mBAAmBD,eAAc,MAAS;AAC9D,QAAIC,aAAa,QAAOA;AACxB,UAAMC,cAAa,OAAO,cAAc;AACxC,UAAMC,gBAAe,WAAW,KAAK,IAAI,SAAS,QAAQ;AAC1D,QAAIA,eAAc;AAChB,YAAM,cAAc,wBAAwBA,eAAcD,WAAU;AACpE,UAAI,YAAa,QAAO;AAAA,IAC1B;AACA,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,IAAI;AACzB,QAAM,aAAa,aAAa;AAChC,QAAM,cAAc,mBAAmB,UAAU;AACjD,MAAI,YAAa,QAAO;AAExB,QAAM,aAAa,OAAO,cAAc;AAExC,MAAI,IAAI,UAAU,UAAU,GAAG;AAC7B,WAAO,IAAI,QAAQ,UAAU;AAAA,EAC/B;AAEA,QAAM,eAAe,aAAa;AAClC,MAAI,cAAc;AAChB,UAAM,cAAc,wBAAwB,cAAc,UAAU;AACpE,QAAI,YAAa,QAAO;AAAA,EAC1B;AAEA,SAAO;AACT;AC3FO,MAAM,YAAY;AAAA,EAGvB,YAAY,QAA2B;AAFvB;AAGd,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,UACJ,UACA,QACA,SACA,SACkB;AAClB,UAAM,SAAS,MAAM,KAAK,aAAa,UAAU,QAAQ,SAAS,OAAO;AACzE,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,aACJ,UACA,QACA,SACA,SAC8B;AAC9B,QAAI;AAEF,YAAM,WAAW,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,MAAM,KAAK,UAAU;AAAA,YACnB,YAAY;AAAA,cACV;AAAA,cACA;AAAA,YAAA;AAAA,YAEF,SAAS;AAAA,cACP,UAAU,SAAS,SAAS,YAAY;AAAA,YAAA;AAAA,UAC1C,CACD;AAAA,QAAA;AAAA,QAEH;AAAA,MAAA;AAGF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,eAAO;AAAA,UACL,YAAY;AAAA,UACZ,QAAQ,MAAM,WAAW,cAAc,SAAS,MAAM;AAAA,QAAA;AAAA,MAE1D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAE5B,YAAM,SAA8B,KAAK,QAAQ;AACjD,aAAO;AAAA,IACT,SAAS,OAAO;AACd,aAAO;AAAA,QACL,YAAY;AAAA,QACZ,QAAQ,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAAA;AAAA,IAErD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBAAoB,SAA0C;AAClE,WAAO,wBAAwB,SAAS,KAAK,MAAM;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,cACZ,KACA,MACA,SACmB;AACnB,WAAO,KAAK,QAAQ,KAAK,MAAM,OAAO;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,QAAQ,KAAa,MAAoB,SAAsC;AAC3F,UAAM,UAAU,KAAK,OAAO,SAAS,WAAW;AAChD,UAAM,UAAU,IAAI,WAAW,MAAM,IAAI,MAAM,GAAG,KAAK,OAAO,MAAM,GAAG,GAAG;AAE1E,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,GAAI,MAAM;AAAA,IAAA;AAIZ,QAAI,SAAS;AACX,YAAM,QAAQ,MAAM,wBAAwB,SAAS,KAAK,MAAM;AAChE,UAAI,OAAO;AACT,gBAAQ,eAAe,IAAI,UAAU,KAAK;AAAA,MAC5C;AAGA,YAAM,MAAM;AACZ,UAAI,IAAI,SAAS,QAAQ;AACvB,gBAAQ,QAAQ,IAAI,IAAI,QAAQ;AAAA,MAClC;AAAA,IACF;AAEA,WAAO,QAAQ,SAAS;AAAA,MACtB,GAAG;AAAA,MACH;AAAA;AAAA,MAEA,aAAa,KAAK,OAAO,eAAe;AAAA,IAAA,CACzC;AAAA,EACH;AACF;;;;"}
@@ -0,0 +1,50 @@
1
+ import { GrantServerConfig, AuthorizationResult, PermissionCheckOptions } from './types';
2
+ /**
3
+ * Grant Client for server-side applications
4
+ *
5
+ * Makes HTTP requests to the Grant API to check permissions.
6
+ * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.
7
+ * No caching - server-side caching is handled by grant-api.
8
+ */
9
+ export declare class GrantClient {
10
+ readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;
11
+ constructor(config: GrantServerConfig);
12
+ /**
13
+ * Check if the current user has a specific permission
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const canEdit = await grant.isGranted('document', 'update', { scope });
18
+ * if (canEdit) {
19
+ * // Proceed with operation
20
+ * }
21
+ * ```
22
+ */
23
+ isGranted(resource: string, action: string, options?: PermissionCheckOptions, request?: unknown): Promise<boolean>;
24
+ /**
25
+ * Check authorization with full result details
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const result = await grant.isAuthorized('document', 'update', { scope }, request);
30
+ * if (!result.authorized) {
31
+ * console.log('Denied:', result.reason);
32
+ * }
33
+ * ```
34
+ */
35
+ isAuthorized(resource: string, action: string, options?: PermissionCheckOptions, request?: unknown): Promise<AuthorizationResult>;
36
+ /**
37
+ * Extract token from a request object
38
+ * Useful for middleware that needs to extract token before making authorization calls
39
+ */
40
+ getTokenFromRequest(request: unknown): Promise<string | null>;
41
+ /**
42
+ * Make an authenticated fetch request (token from request; no refresh).
43
+ */
44
+ private fetchWithAuth;
45
+ /**
46
+ * Perform the actual fetch request
47
+ */
48
+ private doFetch;
49
+ }
50
+ //# sourceMappingURL=grant-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-client.d.ts","sourceRoot":"","sources":["../src/grant-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAE9F;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,SAAgB,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,GAAG,iBAAiB,CAAC;gBAE5E,MAAM,EAAE,iBAAiB;IAQrC;;;;;;;;;;OAUG;IACG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,sBAAsB,EAChC,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,OAAO,CAAC;IAKnB;;;;;;;;;;OAUG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,sBAAsB,EAChC,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,mBAAmB,CAAC;IAwC/B;;;OAGG;IACG,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQnE;;OAEG;YACW,aAAa;IAQ3B;;OAEG;YACW,OAAO;CA8BtB"}
package/dist/index.cjs ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const grantClient = require("./grant-client-DywfJN5P.cjs");
4
+ class GrantServerError extends Error {
5
+ constructor(message, code, statusCode = 500) {
6
+ super(message);
7
+ this.code = code;
8
+ this.statusCode = statusCode;
9
+ this.name = "GrantServerError";
10
+ Object.setPrototypeOf(this, GrantServerError.prototype);
11
+ }
12
+ }
13
+ class AuthenticationError extends GrantServerError {
14
+ constructor(message = "Unauthorized", code = "UNAUTHENTICATED") {
15
+ super(message, code, 401);
16
+ this.name = "AuthenticationError";
17
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
18
+ }
19
+ }
20
+ class AuthorizationError extends GrantServerError {
21
+ constructor(message = "Forbidden", code = "FORBIDDEN", reason) {
22
+ super(message, code, 403);
23
+ this.reason = reason;
24
+ this.name = "AuthorizationError";
25
+ Object.setPrototypeOf(this, AuthorizationError.prototype);
26
+ }
27
+ }
28
+ class BadRequestError extends GrantServerError {
29
+ constructor(message = "Bad Request", code = "BAD_REQUEST") {
30
+ super(message, code, 400);
31
+ this.name = "BadRequestError";
32
+ Object.setPrototypeOf(this, BadRequestError.prototype);
33
+ }
34
+ }
35
+ class NotFoundError extends GrantServerError {
36
+ constructor(message = "Not Found", code = "NOT_FOUND") {
37
+ super(message, code, 404);
38
+ this.name = "NotFoundError";
39
+ Object.setPrototypeOf(this, NotFoundError.prototype);
40
+ }
41
+ }
42
+ exports.GrantClient = grantClient.GrantClient;
43
+ exports.extractBearerToken = grantClient.extractBearerToken;
44
+ exports.extractTokenFromRequest = grantClient.extractTokenFromRequest;
45
+ exports.AuthenticationError = AuthenticationError;
46
+ exports.AuthorizationError = AuthorizationError;
47
+ exports.BadRequestError = BadRequestError;
48
+ exports.GrantServerError = GrantServerError;
49
+ exports.NotFoundError = NotFoundError;
50
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base error class for Grant server errors\n */\nexport class GrantServerError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode: number = 500\n ) {\n super(message);\n this.name = 'GrantServerError';\n Object.setPrototypeOf(this, GrantServerError.prototype);\n }\n}\n\n/**\n * Authentication error (401)\n * Thrown when user is not authenticated\n */\nexport class AuthenticationError extends GrantServerError {\n constructor(message: string = 'Unauthorized', code: string = 'UNAUTHENTICATED') {\n super(message, code, 401);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Authorization error (403)\n * Thrown when user is authenticated but lacks required permission\n */\nexport class AuthorizationError extends GrantServerError {\n constructor(\n message: string = 'Forbidden',\n code: string = 'FORBIDDEN',\n public readonly reason?: string\n ) {\n super(message, code, 403);\n this.name = 'AuthorizationError';\n Object.setPrototypeOf(this, AuthorizationError.prototype);\n }\n}\n\n/**\n * Bad request error (400)\n * Thrown when request is malformed or missing required data\n */\nexport class BadRequestError extends GrantServerError {\n constructor(message: string = 'Bad Request', code: string = 'BAD_REQUEST') {\n super(message, code, 400);\n this.name = 'BadRequestError';\n Object.setPrototypeOf(this, BadRequestError.prototype);\n }\n}\n\n/**\n * Not found error (404)\n * Thrown when resource is not found\n */\nexport class NotFoundError extends GrantServerError {\n constructor(message: string = 'Not Found', code: string = 'NOT_FOUND') {\n super(message, code, 404);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n"],"names":[],"mappings":";;;AAGO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACE,SACgB,MACA,aAAqB,KACrC;AACA,UAAM,OAAO;AAHG,SAAA,OAAA;AACA,SAAA,aAAA;AAGhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,iBAAiB,SAAS;AAAA,EACxD;AACF;AAMO,MAAM,4BAA4B,iBAAiB;AAAA,EACxD,YAAY,UAAkB,gBAAgB,OAAe,mBAAmB;AAC9E,UAAM,SAAS,MAAM,GAAG;AACxB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,oBAAoB,SAAS;AAAA,EAC3D;AACF;AAMO,MAAM,2BAA2B,iBAAiB;AAAA,EACvD,YACE,UAAkB,aAClB,OAAe,aACC,QAChB;AACA,UAAM,SAAS,MAAM,GAAG;AAFR,SAAA,SAAA;AAGhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,mBAAmB,SAAS;AAAA,EAC1D;AACF;AAMO,MAAM,wBAAwB,iBAAiB;AAAA,EACpD,YAAY,UAAkB,eAAe,OAAe,eAAe;AACzE,UAAM,SAAS,MAAM,GAAG;AACxB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,gBAAgB,SAAS;AAAA,EACvD;AACF;AAMO,MAAM,sBAAsB,iBAAiB;AAAA,EAClD,YAAY,UAAkB,aAAa,OAAe,aAAa;AACrE,UAAM,SAAS,MAAM,GAAG;AACxB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,cAAc,SAAS;AAAA,EACrD;AACF;;;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ export { GrantClient } from './grant-client';
2
+ export type { GrantServerConfig, AuthorizationResult, PermissionCheckOptions } from './types';
3
+ export { GrantServerError, AuthenticationError, AuthorizationError, BadRequestError, NotFoundError, } from './errors';
4
+ export { extractTokenFromRequest, extractBearerToken } from './utils/token-extractor';
5
+ export type { Scope, Tenant, ResourceResolver, ResourceResolverParams, ResourceResolverResult, } from './types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAG9F,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,aAAa,GACd,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGtF,YAAY,EACV,KAAK,EACL,MAAM,EACN,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,SAAS,CAAC"}
package/dist/index.mjs ADDED
@@ -0,0 +1,50 @@
1
+ import { G, a, e } from "./grant-client-D1LZI2f4.js";
2
+ class GrantServerError extends Error {
3
+ constructor(message, code, statusCode = 500) {
4
+ super(message);
5
+ this.code = code;
6
+ this.statusCode = statusCode;
7
+ this.name = "GrantServerError";
8
+ Object.setPrototypeOf(this, GrantServerError.prototype);
9
+ }
10
+ }
11
+ class AuthenticationError extends GrantServerError {
12
+ constructor(message = "Unauthorized", code = "UNAUTHENTICATED") {
13
+ super(message, code, 401);
14
+ this.name = "AuthenticationError";
15
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
16
+ }
17
+ }
18
+ class AuthorizationError extends GrantServerError {
19
+ constructor(message = "Forbidden", code = "FORBIDDEN", reason) {
20
+ super(message, code, 403);
21
+ this.reason = reason;
22
+ this.name = "AuthorizationError";
23
+ Object.setPrototypeOf(this, AuthorizationError.prototype);
24
+ }
25
+ }
26
+ class BadRequestError extends GrantServerError {
27
+ constructor(message = "Bad Request", code = "BAD_REQUEST") {
28
+ super(message, code, 400);
29
+ this.name = "BadRequestError";
30
+ Object.setPrototypeOf(this, BadRequestError.prototype);
31
+ }
32
+ }
33
+ class NotFoundError extends GrantServerError {
34
+ constructor(message = "Not Found", code = "NOT_FOUND") {
35
+ super(message, code, 404);
36
+ this.name = "NotFoundError";
37
+ Object.setPrototypeOf(this, NotFoundError.prototype);
38
+ }
39
+ }
40
+ export {
41
+ AuthenticationError,
42
+ AuthorizationError,
43
+ BadRequestError,
44
+ G as GrantClient,
45
+ GrantServerError,
46
+ NotFoundError,
47
+ a as extractBearerToken,
48
+ e as extractTokenFromRequest
49
+ };
50
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base error class for Grant server errors\n */\nexport class GrantServerError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode: number = 500\n ) {\n super(message);\n this.name = 'GrantServerError';\n Object.setPrototypeOf(this, GrantServerError.prototype);\n }\n}\n\n/**\n * Authentication error (401)\n * Thrown when user is not authenticated\n */\nexport class AuthenticationError extends GrantServerError {\n constructor(message: string = 'Unauthorized', code: string = 'UNAUTHENTICATED') {\n super(message, code, 401);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Authorization error (403)\n * Thrown when user is authenticated but lacks required permission\n */\nexport class AuthorizationError extends GrantServerError {\n constructor(\n message: string = 'Forbidden',\n code: string = 'FORBIDDEN',\n public readonly reason?: string\n ) {\n super(message, code, 403);\n this.name = 'AuthorizationError';\n Object.setPrototypeOf(this, AuthorizationError.prototype);\n }\n}\n\n/**\n * Bad request error (400)\n * Thrown when request is malformed or missing required data\n */\nexport class BadRequestError extends GrantServerError {\n constructor(message: string = 'Bad Request', code: string = 'BAD_REQUEST') {\n super(message, code, 400);\n this.name = 'BadRequestError';\n Object.setPrototypeOf(this, BadRequestError.prototype);\n }\n}\n\n/**\n * Not found error (404)\n * Thrown when resource is not found\n */\nexport class NotFoundError extends GrantServerError {\n constructor(message: string = 'Not Found', code: string = 'NOT_FOUND') {\n super(message, code, 404);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n"],"names":[],"mappings":";AAGO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACE,SACgB,MACA,aAAqB,KACrC;AACA,UAAM,OAAO;AAHG,SAAA,OAAA;AACA,SAAA,aAAA;AAGhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,iBAAiB,SAAS;AAAA,EACxD;AACF;AAMO,MAAM,4BAA4B,iBAAiB;AAAA,EACxD,YAAY,UAAkB,gBAAgB,OAAe,mBAAmB;AAC9E,UAAM,SAAS,MAAM,GAAG;AACxB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,oBAAoB,SAAS;AAAA,EAC3D;AACF;AAMO,MAAM,2BAA2B,iBAAiB;AAAA,EACvD,YACE,UAAkB,aAClB,OAAe,aACC,QAChB;AACA,UAAM,SAAS,MAAM,GAAG;AAFR,SAAA,SAAA;AAGhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,mBAAmB,SAAS;AAAA,EAC1D;AACF;AAMO,MAAM,wBAAwB,iBAAiB;AAAA,EACpD,YAAY,UAAkB,eAAe,OAAe,eAAe;AACzE,UAAM,SAAS,MAAM,GAAG;AACxB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,gBAAgB,SAAS;AAAA,EACvD;AACF;AAMO,MAAM,sBAAsB,iBAAiB;AAAA,EAClD,YAAY,UAAkB,aAAa,OAAe,aAAa;AACrE,UAAM,SAAS,MAAM,GAAG;AACxB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,cAAc,SAAS;AAAA,EACrD;AACF;"}
@@ -0,0 +1,38 @@
1
+ import { SetMetadata } from '@nestjs/common';
2
+ /**
3
+ * Metadata key for Grant options (resource, action) on route handlers.
4
+ * Used by GrantGuard when options are not passed in the constructor.
5
+ */
6
+ export declare const GRANT_OPTIONS_KEY = "grant:options";
7
+ /**
8
+ * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).
9
+ */
10
+ export interface GrantOptions {
11
+ /** The resource slug to check (e.g., "Organization", "Project", "Document") */
12
+ resource: string;
13
+ /** The action to check (e.g., "Query", "Create", "Update", "Delete") */
14
+ action: string;
15
+ }
16
+ /**
17
+ * Decorator to require a Grant permission on a route.
18
+ * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * @Get()
23
+ * @Grant('Document', 'Query')
24
+ * @UseGuards(GrantGuard)
25
+ * list() { ... }
26
+ *
27
+ * @Post()
28
+ * @Grant('Document', 'Create')
29
+ * @UseGuards(GrantGuard)
30
+ * create(@Body() dto: CreateDocumentDto) { ... }
31
+ * ```
32
+ *
33
+ * For dynamic resource resolution (resourceResolver), use the guard with explicit options:
34
+ * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`
35
+ */
36
+ export declare function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;
37
+ export declare function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;
38
+ //# sourceMappingURL=grant.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant.decorator.d.ts","sourceRoot":"","sources":["../../src/nest/grant.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AACxF,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { GrantClient } from '../grant-client';
4
+ import { ResourceResolver, AuthorizationResult } from '../types';
5
+ /** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */
6
+ export declare const GRANT_GUARD = "GrantGuard";
7
+ /**
8
+ * Extended request type with Grant authorization result (for use in controllers).
9
+ */
10
+ export interface AuthorizedRequest {
11
+ authorization?: AuthorizationResult;
12
+ }
13
+ /**
14
+ * Guard options when using explicit options (includes optional resourceResolver).
15
+ */
16
+ export interface GrantGuardOptions {
17
+ resource: string;
18
+ action: string;
19
+ resourceResolver?: ResourceResolver;
20
+ }
21
+ export declare class GrantGuard implements CanActivate {
22
+ private readonly client;
23
+ private readonly explicitOptions?;
24
+ private readonly reflector?;
25
+ constructor(client: GrantClient, reflectorOrOptions?: Reflector | GrantGuardOptions);
26
+ private getOptionsFromContext;
27
+ canActivate(context: ExecutionContext): Promise<boolean>;
28
+ }
29
+ //# sourceMappingURL=grant.guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant.guard.d.ts","sourceRoot":"","sources":["../../src/nest/grant.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAO9C,kJAAkJ;AAClJ,eAAO,MAAM,WAAW,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGtE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AA6BD,qBACa,UAAW,YAAW,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAoB;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAY;gBAGE,MAAM,EAAE,WAAW,EAC1D,kBAAkB,CAAC,EAAE,SAAS,GAAG,iBAAiB;IASpD,OAAO,CAAC,qBAAqB;IAsBvB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CA0E/D"}
@@ -0,0 +1,28 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import { GrantServerConfig } from '../types';
3
+ export declare const GRANT_CLIENT = "GrantClient";
4
+ /**
5
+ * NestJS module that provides GrantClient for injection.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // app.module.ts
10
+ * import { GrantModule } from '@grantjs/server/nest';
11
+ *
12
+ * @Module({
13
+ * imports: [
14
+ * GrantModule.forRoot({
15
+ * apiUrl: process.env.GRANT_API_URL!,
16
+ * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,
17
+ * }),
18
+ * ],
19
+ * })
20
+ * export class AppModule {}
21
+ * ```
22
+ *
23
+ * Then inject GrantClient in controllers and use with GrantGuard.
24
+ */
25
+ export declare class GrantModule {
26
+ static forRoot(config: GrantServerConfig): DynamicModule;
27
+ }
28
+ //# sourceMappingURL=grant.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant.module.d.ts","sourceRoot":"","sources":["../../src/nest/grant.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,gBAAgB,CAAC;AAI/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAEa,WAAW;IACtB,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa;CAWzD"}
@@ -0,0 +1,8 @@
1
+ export { GrantGuard, GRANT_GUARD } from './grant.guard';
2
+ export type { AuthorizedRequest, GrantGuardOptions } from './grant.guard';
3
+ export { Grant, GRANT_OPTIONS_KEY } from './grant.decorator';
4
+ export type { GrantOptions } from './grant.decorator';
5
+ export { GrantModule, GRANT_CLIENT } from './grant.module';
6
+ export type { GrantServerConfig, AuthorizationResult, PermissionCheckOptions, Scope, ResourceResolver, } from '../types';
7
+ export { GrantClient } from '../grant-client';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1E,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE3D,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,EACL,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}