@grantjs/server 1.0.0 → 1.1.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/README.md +1 -1
  2. package/dist/debug-DICR8lwQ.js +15 -0
  3. package/dist/debug-DICR8lwQ.js.map +1 -0
  4. package/dist/debug-DInHA4QB.cjs +20 -0
  5. package/dist/debug-DInHA4QB.cjs.map +1 -0
  6. package/dist/errors-BUeTn_3G.cjs +91 -0
  7. package/dist/errors-BUeTn_3G.cjs.map +1 -0
  8. package/dist/errors-CSC_Z_KP.js +62 -0
  9. package/dist/errors-CSC_Z_KP.js.map +1 -0
  10. package/dist/express/index.d.ts +3 -3
  11. package/dist/express/index.d.ts.map +1 -1
  12. package/dist/express/middleware.d.ts +2 -2
  13. package/dist/express/middleware.d.ts.map +1 -1
  14. package/dist/express.cjs +106 -95
  15. package/dist/express.cjs.map +1 -1
  16. package/dist/express.mjs +101 -99
  17. package/dist/express.mjs.map +1 -1
  18. package/dist/fastify/index.d.ts +4 -4
  19. package/dist/fastify/index.d.ts.map +1 -1
  20. package/dist/fastify/plugin.d.ts +2 -2
  21. package/dist/fastify/plugin.d.ts.map +1 -1
  22. package/dist/fastify.cjs +133 -97
  23. package/dist/fastify.cjs.map +1 -1
  24. package/dist/fastify.mjs +128 -101
  25. package/dist/fastify.mjs.map +1 -1
  26. package/dist/grant-client-f6IKMyk5.cjs +228 -0
  27. package/dist/grant-client-f6IKMyk5.cjs.map +1 -0
  28. package/dist/grant-client-t3KadxFo.js +205 -0
  29. package/dist/grant-client-t3KadxFo.js.map +1 -0
  30. package/dist/grant-client.d.ts +1 -1
  31. package/dist/grant-client.d.ts.map +1 -1
  32. package/dist/index.cjs +10 -49
  33. package/dist/index.d.ts +4 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.mjs +3 -50
  36. package/dist/nest/grant.guard.d.ts +1 -1
  37. package/dist/nest/grant.guard.d.ts.map +1 -1
  38. package/dist/nest/grant.module.d.ts.map +1 -1
  39. package/dist/nest/index.d.ts +6 -6
  40. package/dist/nest/index.d.ts.map +1 -1
  41. package/dist/nest.cjs +163 -143
  42. package/dist/nest.cjs.map +1 -1
  43. package/dist/nest.mjs +150 -149
  44. package/dist/nest.mjs.map +1 -1
  45. package/dist/next/index.d.ts +4 -4
  46. package/dist/next/index.d.ts.map +1 -1
  47. package/dist/next/with-grant.d.ts +1 -1
  48. package/dist/next/with-grant.d.ts.map +1 -1
  49. package/dist/next.cjs +75 -58
  50. package/dist/next.cjs.map +1 -1
  51. package/dist/next.mjs +70 -60
  52. package/dist/next.mjs.map +1 -1
  53. package/package.json +17 -17
  54. package/dist/debug-C8ibbrE5.js +0 -9
  55. package/dist/debug-C8ibbrE5.js.map +0 -1
  56. package/dist/debug-Crb5gNDM.cjs +0 -8
  57. package/dist/debug-Crb5gNDM.cjs.map +0 -1
  58. package/dist/grant-client-D1LZI2f4.js +0 -180
  59. package/dist/grant-client-D1LZI2f4.js.map +0 -1
  60. package/dist/grant-client-DywfJN5P.cjs +0 -179
  61. package/dist/grant-client-DywfJN5P.cjs.map +0 -1
  62. package/dist/index.cjs.map +0 -1
  63. package/dist/index.mjs.map +0 -1
package/dist/next.mjs CHANGED
@@ -1,63 +1,73 @@
1
+ import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-t3KadxFo.js";
2
+ import { a as NotFoundError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-CSC_Z_KP.js";
3
+ import { t as debugGrant } from "./debug-DICR8lwQ.js";
1
4
  import { NextResponse } from "next/server";
2
- import { d as debugGrant } from "./debug-C8ibbrE5.js";
3
- import { e as extractTokenFromRequest } from "./grant-client-D1LZI2f4.js";
4
- import { G } from "./grant-client-D1LZI2f4.js";
5
- import { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from "./index.mjs";
5
+ //#region src/next/with-grant.ts
6
+ /**
7
+ * Wrap a Next.js App Router route handler with Grant authorization.
8
+ * Use for GET, POST, PUT, PATCH, DELETE in app/api/.../route.ts.
9
+ *
10
+ * Compatible with Next.js 13–16. In Next 15+, the route context's `params` is a Promise;
11
+ * this wrapper does not use params (only `request` is passed to the resolver/handler).
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // app/api/documents/route.ts
16
+ * import { withGrant } from '@grantjs/server/next';
17
+ * import { GrantClient } from '@grantjs/server';
18
+ *
19
+ * const grantClient = new GrantClient({ apiUrl: process.env.GRANT_API_URL! });
20
+ *
21
+ * export const GET = withGrant(
22
+ * grantClient,
23
+ * { resource: 'Document', action: 'Query' },
24
+ * async (request) => NextResponse.json({ data: [] })
25
+ * );
26
+ *
27
+ * export const POST = withGrant(
28
+ * grantClient,
29
+ * { resource: 'Document', action: 'Create' },
30
+ * async (request) => { ... }
31
+ * );
32
+ * ```
33
+ */
6
34
  function withGrant(client, options, handler) {
7
- return async (request, _routeContext) => {
8
- debugGrant("Next", { resource: options.resource, action: options.action });
9
- const token = await extractTokenFromRequest(request, client.config);
10
- if (!token) {
11
- return NextResponse.json({ error: "Unauthorized", code: "UNAUTHENTICATED" }, { status: 401 });
12
- }
13
- let resolvedResource = null;
14
- if (options.resourceResolver) {
15
- resolvedResource = await options.resourceResolver({
16
- resourceSlug: options.resource,
17
- request
18
- });
19
- if (!resolvedResource) {
20
- return NextResponse.json(
21
- { error: "Resource not found", code: "NOT_FOUND" },
22
- { status: 404 }
23
- );
24
- }
25
- }
26
- const result = await client.isAuthorized(
27
- options.resource,
28
- options.action,
29
- {
30
- context: {
31
- resource: resolvedResource ?? void 0
32
- }
33
- },
34
- request
35
- );
36
- debugGrant("Next", {
37
- resource: options.resource,
38
- action: options.action,
39
- authorized: result.authorized,
40
- ...result.authorized ? {} : { reason: result.reason }
41
- });
42
- if (!result.authorized) {
43
- return NextResponse.json(
44
- {
45
- error: "Forbidden",
46
- code: "FORBIDDEN",
47
- reason: result.reason
48
- },
49
- { status: 403 }
50
- );
51
- }
52
- return handler(request, { authorization: result });
53
- };
35
+ return async (request, _routeContext) => {
36
+ debugGrant("Next", {
37
+ resource: options.resource,
38
+ action: options.action
39
+ });
40
+ if (!await extractTokenFromRequest(request, client.config)) return NextResponse.json({
41
+ error: "Unauthorized",
42
+ code: "UNAUTHENTICATED"
43
+ }, { status: 401 });
44
+ let resolvedResource = null;
45
+ if (options.resourceResolver) {
46
+ resolvedResource = await options.resourceResolver({
47
+ resourceSlug: options.resource,
48
+ request
49
+ });
50
+ if (!resolvedResource) return NextResponse.json({
51
+ error: "Resource not found",
52
+ code: "NOT_FOUND"
53
+ }, { status: 404 });
54
+ }
55
+ const result = await client.isAuthorized(options.resource, options.action, { context: { resource: resolvedResource ?? void 0 } }, request);
56
+ debugGrant("Next", {
57
+ resource: options.resource,
58
+ action: options.action,
59
+ authorized: result.authorized,
60
+ ...result.authorized ? {} : { reason: result.reason }
61
+ });
62
+ if (!result.authorized) return NextResponse.json({
63
+ error: "Forbidden",
64
+ code: "FORBIDDEN",
65
+ reason: result.reason
66
+ }, { status: 403 });
67
+ return handler(request, { authorization: result });
68
+ };
54
69
  }
55
- export {
56
- AuthenticationError,
57
- AuthorizationError,
58
- BadRequestError,
59
- G as GrantClient,
60
- NotFoundError,
61
- withGrant
62
- };
63
- //# sourceMappingURL=next.mjs.map
70
+ //#endregion
71
+ export { AuthenticationError, AuthorizationError, BadRequestError, GrantClient, NotFoundError, withGrant };
72
+
73
+ //# sourceMappingURL=next.mjs.map
package/dist/next.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"next.mjs","sources":["../src/next/with-grant.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\nimport type { ResourceResolver, AuthorizationResult } from '../types';\n\n/**\n * Options for Next.js route handler guard\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n /** Custom resource resolver for condition evaluation */\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * Context passed to the handler after authorization (for downstream use)\n */\nexport interface WithGrantContext {\n authorization: AuthorizationResult;\n}\n\n/**\n * Next.js App Router route handler type (GET, POST, etc.)\n * Second argument is optional context; params are in the request URL for resolver use.\n */\nexport type GrantRouteHandler = (\n request: NextRequest,\n context?: WithGrantContext\n) => Promise<Response> | Response;\n\n/**\n * Wrap a Next.js App Router route handler with Grant authorization.\n * Use for GET, POST, PUT, PATCH, DELETE in app/api/.../route.ts.\n *\n * Compatible with Next.js 13–16. In Next 15+, the route context's `params` is a Promise;\n * this wrapper does not use params (only `request` is passed to the resolver/handler).\n *\n * @example\n * ```ts\n * // app/api/documents/route.ts\n * import { withGrant } from '@grantjs/server/next';\n * import { GrantClient } from '@grantjs/server';\n *\n * const grantClient = new GrantClient({ apiUrl: process.env.GRANT_API_URL! });\n *\n * export const GET = withGrant(\n * grantClient,\n * { resource: 'Document', action: 'Query' },\n * async (request) => NextResponse.json({ data: [] })\n * );\n *\n * export const POST = withGrant(\n * grantClient,\n * { resource: 'Document', action: 'Create' },\n * async (request) => { ... }\n * );\n * ```\n */\nexport function withGrant(\n client: GrantClient,\n options: GrantOptions,\n handler: GrantRouteHandler\n): (\n request: NextRequest,\n context?: { params?: Promise<Record<string, string>> }\n) => Promise<Response> {\n return async (\n request: NextRequest,\n _routeContext?: { params?: Promise<Record<string, string>> }\n ): Promise<Response> => {\n debugGrant('Next', { resource: options.resource, action: options.action });\n\n const token = await extractTokenFromRequest(request, client.config);\n if (!token) {\n return NextResponse.json({ error: 'Unauthorized', code: 'UNAUTHENTICATED' }, { status: 401 });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n if (options.resourceResolver) {\n resolvedResource = await options.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n return NextResponse.json(\n { error: 'Resource not found', code: 'NOT_FOUND' },\n { status: 404 }\n );\n }\n }\n\n const result = await client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Next', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n return NextResponse.json(\n {\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n },\n { status: 403 }\n );\n }\n\n return handler(request, { authorization: result });\n };\n}\n"],"names":[],"mappings":";;;;;AAgEO,SAAS,UACd,QACA,SACA,SAIqB;AACrB,SAAO,OACL,SACA,kBACsB;AACtB,eAAW,QAAQ,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AAEzE,UAAM,QAAQ,MAAM,wBAAwB,SAAS,OAAO,MAAM;AAClE,QAAI,CAAC,OAAO;AACV,aAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,MAAM,qBAAqB,EAAE,QAAQ,KAAK;AAAA,IAC9F;AAEA,QAAI,mBAAmD;AAEvD,QAAI,QAAQ,kBAAkB;AAC5B,yBAAmB,MAAM,QAAQ,iBAAiB;AAAA,QAChD,cAAc,QAAQ;AAAA,QACtB;AAAA,MAAA,CACD;AACD,UAAI,CAAC,kBAAkB;AACrB,eAAO,aAAa;AAAA,UAClB,EAAE,OAAO,sBAAsB,MAAM,YAAA;AAAA,UACrC,EAAE,QAAQ,IAAA;AAAA,QAAI;AAAA,MAElB;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,QACE,SAAS;AAAA,UACP,UAAU,oBAAoB;AAAA,QAAA;AAAA,MAChC;AAAA,MAEF;AAAA,IAAA;AAGF,eAAW,QAAQ;AAAA,MACjB,UAAU,QAAQ;AAAA,MAClB,QAAQ,QAAQ;AAAA,MAChB,YAAY,OAAO;AAAA,MACnB,GAAI,OAAO,aAAa,CAAA,IAAK,EAAE,QAAQ,OAAO,OAAA;AAAA,IAAO,CACtD;AAED,QAAI,CAAC,OAAO,YAAY;AACtB,aAAO,aAAa;AAAA,QAClB;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,UACN,QAAQ,OAAO;AAAA,QAAA;AAAA,QAEjB,EAAE,QAAQ,IAAA;AAAA,MAAI;AAAA,IAElB;AAEA,WAAO,QAAQ,SAAS,EAAE,eAAe,QAAQ;AAAA,EACnD;AACF;"}
1
+ {"version":3,"file":"next.mjs","names":[],"sources":["../src/next/with-grant.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\n\nimport { GrantClient } from '../grant-client';\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\n/**\n * Options for Next.js route handler guard\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n /** Custom resource resolver for condition evaluation */\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * Context passed to the handler after authorization (for downstream use)\n */\nexport interface WithGrantContext {\n authorization: AuthorizationResult;\n}\n\n/**\n * Next.js App Router route handler type (GET, POST, etc.)\n * Second argument is optional context; params are in the request URL for resolver use.\n */\nexport type GrantRouteHandler = (\n request: NextRequest,\n context?: WithGrantContext\n) => Promise<Response> | Response;\n\n/**\n * Wrap a Next.js App Router route handler with Grant authorization.\n * Use for GET, POST, PUT, PATCH, DELETE in app/api/.../route.ts.\n *\n * Compatible with Next.js 13–16. In Next 15+, the route context's `params` is a Promise;\n * this wrapper does not use params (only `request` is passed to the resolver/handler).\n *\n * @example\n * ```ts\n * // app/api/documents/route.ts\n * import { withGrant } from '@grantjs/server/next';\n * import { GrantClient } from '@grantjs/server';\n *\n * const grantClient = new GrantClient({ apiUrl: process.env.GRANT_API_URL! });\n *\n * export const GET = withGrant(\n * grantClient,\n * { resource: 'Document', action: 'Query' },\n * async (request) => NextResponse.json({ data: [] })\n * );\n *\n * export const POST = withGrant(\n * grantClient,\n * { resource: 'Document', action: 'Create' },\n * async (request) => { ... }\n * );\n * ```\n */\nexport function withGrant(\n client: GrantClient,\n options: GrantOptions,\n handler: GrantRouteHandler\n): (\n request: NextRequest,\n context?: { params?: Promise<Record<string, string>> }\n) => Promise<Response> {\n return async (\n request: NextRequest,\n _routeContext?: { params?: Promise<Record<string, string>> }\n ): Promise<Response> => {\n debugGrant('Next', { resource: options.resource, action: options.action });\n\n const token = await extractTokenFromRequest(request, client.config);\n if (!token) {\n return NextResponse.json({ error: 'Unauthorized', code: 'UNAUTHENTICATED' }, { status: 401 });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n if (options.resourceResolver) {\n resolvedResource = await options.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n return NextResponse.json(\n { error: 'Resource not found', code: 'NOT_FOUND' },\n { status: 404 }\n );\n }\n }\n\n const result = await client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Next', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n return NextResponse.json(\n {\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n },\n { status: 403 }\n );\n }\n\n return handler(request, { authorization: result });\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,UACd,QACA,SACA,SAIqB;AACrB,QAAO,OACL,SACA,kBACsB;AACtB,aAAW,QAAQ;GAAE,UAAU,QAAQ;GAAU,QAAQ,QAAQ;GAAQ,CAAC;AAG1E,MAAI,CAAC,MADe,wBAAwB,SAAS,OAAO,OAAO,CAEjE,QAAO,aAAa,KAAK;GAAE,OAAO;GAAgB,MAAM;GAAmB,EAAE,EAAE,QAAQ,KAAK,CAAC;EAG/F,IAAI,mBAAmD;AAEvD,MAAI,QAAQ,kBAAkB;AAC5B,sBAAmB,MAAM,QAAQ,iBAAiB;IAChD,cAAc,QAAQ;IACtB;IACD,CAAC;AACF,OAAI,CAAC,iBACH,QAAO,aAAa,KAClB;IAAE,OAAO;IAAsB,MAAM;IAAa,EAClD,EAAE,QAAQ,KAAK,CAChB;;EAIL,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,GAC/B,EACF,EACD,QACD;AAED,aAAW,QAAQ;GACjB,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB,YAAY,OAAO;GACnB,GAAI,OAAO,aAAa,EAAE,GAAG,EAAE,QAAQ,OAAO,QAAQ;GACvD,CAAC;AAEF,MAAI,CAAC,OAAO,WACV,QAAO,aAAa,KAClB;GACE,OAAO;GACP,MAAM;GACN,QAAQ,OAAO;GAChB,EACD,EAAE,QAAQ,KAAK,CAChB;AAGH,SAAO,QAAQ,SAAS,EAAE,eAAe,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grantjs/server",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Server SDK for Grant authorization - Middleware and guards for Express, Fastify, NestJS, and Next.js",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -57,13 +57,13 @@
57
57
  "server"
58
58
  ],
59
59
  "author": {
60
- "name": "Alejandro Heredia",
61
- "email": "ale@logus.graphics",
62
- "url": "https://logus.graphics"
60
+ "name": "Ale Heredia",
61
+ "email": "ale@grantjs.org",
62
+ "url": "https://grantjs.org"
63
63
  },
64
64
  "repository": {
65
65
  "type": "git",
66
- "url": "https://github.com/logusgraphics/grant.git",
66
+ "url": "https://github.com/grant-js/grant.git",
67
67
  "directory": "packages/@grantjs/server"
68
68
  },
69
69
  "license": "MIT",
@@ -72,22 +72,22 @@
72
72
  "registry": "https://registry.npmjs.org/"
73
73
  },
74
74
  "dependencies": {
75
- "@grantjs/schema": "1.0.0"
75
+ "@grantjs/schema": "1.1.0"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@types/express": "^5.0.0",
79
- "@types/node": "^24.7.2",
80
- "@vitest/coverage-v8": "^3.2.4",
81
- "eslint": "^9.37.0",
79
+ "@types/node": "^25.6.0",
80
+ "@vitest/coverage-v8": "^4.1.5",
81
+ "eslint": "^10.3.0",
82
82
  "express": "^5.1.0",
83
- "fastify": "^5.7.2",
84
- "next": "^16.0.0",
85
- "@nestjs/common": "^11.0.0",
86
- "@nestjs/core": "^11.0.0",
87
- "typescript": "^5",
88
- "vite": "^6.0.0",
89
- "vite-plugin-dts": "^4.3.0",
90
- "vitest": "^3.2.4"
83
+ "fastify": "^5.8.5",
84
+ "next": "^16.2.4",
85
+ "@nestjs/common": "^11.1.19",
86
+ "@nestjs/core": "^11.1.19",
87
+ "typescript": "^6",
88
+ "vite": "^8.0.10",
89
+ "vite-plugin-dts": "^5.0.0",
90
+ "vitest": "^4.1.5"
91
91
  },
92
92
  "peerDependencies": {
93
93
  "express": "^4 || ^5",
@@ -1,9 +0,0 @@
1
- const DEBUG_GRANT_ENV = "DEBUG_GRANT";
2
- function debugGrant(integration, data) {
3
- if (process.env[DEBUG_GRANT_ENV] !== "1") return;
4
- console.debug(`[Grant ${integration}]`, data);
5
- }
6
- export {
7
- debugGrant as d
8
- };
9
- //# sourceMappingURL=debug-C8ibbrE5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"debug-C8ibbrE5.js","sources":["../src/utils/debug.ts"],"sourcesContent":["const DEBUG_GRANT_ENV = 'DEBUG_GRANT';\n\n/** Whether Grant debug logging is enabled (set DEBUG_GRANT=1 in development). */\nexport function isDebugGrant(): boolean {\n return process.env[DEBUG_GRANT_ENV] === '1';\n}\n\n/**\n * Log Grant integration debug info when DEBUG_GRANT=1.\n * Used by Express middleware, Fastify grant hook, Next withGrant, and Nest GrantGuard.\n * Set DEBUG_GRANT=1 in .env or the environment to see resource, action, and outcome in development.\n */\nexport function debugGrant(integration: string, data: Record<string, unknown>): void {\n if (process.env[DEBUG_GRANT_ENV] !== '1') return;\n console.debug(`[Grant ${integration}]`, data);\n}\n"],"names":[],"mappings":"AAAA,MAAM,kBAAkB;AAYjB,SAAS,WAAW,aAAqB,MAAqC;AACnF,MAAI,QAAQ,IAAI,eAAe,MAAM,IAAK;AAC1C,UAAQ,MAAM,UAAU,WAAW,KAAK,IAAI;AAC9C;"}
@@ -1,8 +0,0 @@
1
- "use strict";
2
- const DEBUG_GRANT_ENV = "DEBUG_GRANT";
3
- function debugGrant(integration, data) {
4
- if (process.env[DEBUG_GRANT_ENV] !== "1") return;
5
- console.debug(`[Grant ${integration}]`, data);
6
- }
7
- exports.debugGrant = debugGrant;
8
- //# sourceMappingURL=debug-Crb5gNDM.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"debug-Crb5gNDM.cjs","sources":["../src/utils/debug.ts"],"sourcesContent":["const DEBUG_GRANT_ENV = 'DEBUG_GRANT';\n\n/** Whether Grant debug logging is enabled (set DEBUG_GRANT=1 in development). */\nexport function isDebugGrant(): boolean {\n return process.env[DEBUG_GRANT_ENV] === '1';\n}\n\n/**\n * Log Grant integration debug info when DEBUG_GRANT=1.\n * Used by Express middleware, Fastify grant hook, Next withGrant, and Nest GrantGuard.\n * Set DEBUG_GRANT=1 in .env or the environment to see resource, action, and outcome in development.\n */\nexport function debugGrant(integration: string, data: Record<string, unknown>): void {\n if (process.env[DEBUG_GRANT_ENV] !== '1') return;\n console.debug(`[Grant ${integration}]`, data);\n}\n"],"names":[],"mappings":";AAAA,MAAM,kBAAkB;AAYjB,SAAS,WAAW,aAAqB,MAAqC;AACnF,MAAI,QAAQ,IAAI,eAAe,MAAM,IAAK;AAC1C,UAAQ,MAAM,UAAU,WAAW,KAAK,IAAI;AAC9C;;"}
@@ -1,180 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- function extractBearerToken(authHeader) {
5
- if (!authHeader) return null;
6
- const parts = authHeader.trim().split(/\s+/);
7
- if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
8
- return null;
9
- }
10
- return parts[1];
11
- }
12
- function parseCookieHeader(cookieHeader) {
13
- const cookies = {};
14
- cookieHeader.split(";").forEach((cookie) => {
15
- const [name, ...rest] = cookie.trim().split("=");
16
- if (name && rest.length > 0) {
17
- cookies[name.trim()] = rest.join("=").trim();
18
- }
19
- });
20
- return cookies;
21
- }
22
- function extractTokenFromCookies(cookieHeader, cookieName) {
23
- if (!cookieHeader) return null;
24
- const cookies = parseCookieHeader(cookieHeader);
25
- return cookies[cookieName] || null;
26
- }
27
- async function extractTokenFromRequest(request, config) {
28
- if (config.getToken) {
29
- const token = await config.getToken(request);
30
- if (token) return token;
31
- }
32
- const req = request;
33
- if (!req?.headers) return null;
34
- const headersGet = req.headers.get;
35
- if (typeof headersGet === "function") {
36
- const authHeader2 = headersGet.call(req.headers, "authorization");
37
- const bearerToken2 = extractBearerToken(authHeader2 ?? void 0);
38
- if (bearerToken2) return bearerToken2;
39
- const cookieName2 = config.cookieName || "grant-access-token";
40
- const cookieHeader2 = headersGet.call(req.headers, "cookie");
41
- if (cookieHeader2) {
42
- const cookieToken = extractTokenFromCookies(cookieHeader2, cookieName2);
43
- if (cookieToken) return cookieToken;
44
- }
45
- return null;
46
- }
47
- const plainHeaders = req.headers;
48
- const authHeader = plainHeaders.authorization;
49
- const bearerToken = extractBearerToken(authHeader);
50
- if (bearerToken) return bearerToken;
51
- const cookieName = config.cookieName || "grant-access-token";
52
- if (req.cookies?.[cookieName]) {
53
- return req.cookies[cookieName];
54
- }
55
- const cookieHeader = plainHeaders.cookie;
56
- if (cookieHeader) {
57
- const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);
58
- if (cookieToken) return cookieToken;
59
- }
60
- return null;
61
- }
62
- class GrantClient {
63
- constructor(config) {
64
- __publicField(this, "config");
65
- this.config = config;
66
- }
67
- // ============================================================================
68
- // Public API - Permission Checks
69
- // ============================================================================
70
- /**
71
- * Check if the current user has a specific permission
72
- *
73
- * @example
74
- * ```ts
75
- * const canEdit = await grant.isGranted('document', 'update', { scope });
76
- * if (canEdit) {
77
- * // Proceed with operation
78
- * }
79
- * ```
80
- */
81
- async isGranted(resource, action, options, request) {
82
- const result = await this.isAuthorized(resource, action, options, request);
83
- return result.authorized;
84
- }
85
- /**
86
- * Check authorization with full result details
87
- *
88
- * @example
89
- * ```ts
90
- * const result = await grant.isAuthorized('document', 'update', { scope }, request);
91
- * if (!result.authorized) {
92
- * console.log('Denied:', result.reason);
93
- * }
94
- * ```
95
- */
96
- async isAuthorized(resource, action, options, request) {
97
- try {
98
- const response = await this.fetchWithAuth(
99
- "/api/auth/is-authorized",
100
- {
101
- method: "POST",
102
- body: JSON.stringify({
103
- permission: {
104
- resource,
105
- action
106
- },
107
- context: {
108
- resource: options?.context?.resource || null
109
- }
110
- })
111
- },
112
- request
113
- );
114
- if (!response.ok) {
115
- const error = await response.json().catch(() => ({}));
116
- return {
117
- authorized: false,
118
- reason: error.message || `API error: ${response.status}`
119
- };
120
- }
121
- const json = await response.json();
122
- const result = json.data ?? json;
123
- return result;
124
- } catch (error) {
125
- return {
126
- authorized: false,
127
- reason: error instanceof Error ? error.message : "Unknown error"
128
- };
129
- }
130
- }
131
- /**
132
- * Extract token from a request object
133
- * Useful for middleware that needs to extract token before making authorization calls
134
- */
135
- async getTokenFromRequest(request) {
136
- return extractTokenFromRequest(request, this.config);
137
- }
138
- // ============================================================================
139
- // Private - HTTP & Authentication
140
- // ============================================================================
141
- /**
142
- * Make an authenticated fetch request (token from request; no refresh).
143
- */
144
- async fetchWithAuth(url, init, request) {
145
- return this.doFetch(url, init, request);
146
- }
147
- /**
148
- * Perform the actual fetch request
149
- */
150
- async doFetch(url, init, request) {
151
- const fetchFn = this.config.fetch ?? globalThis.fetch;
152
- const fullUrl = url.startsWith("http") ? url : `${this.config.apiUrl}${url}`;
153
- const headers = {
154
- "Content-Type": "application/json",
155
- ...init?.headers
156
- };
157
- if (request) {
158
- const token = await extractTokenFromRequest(request, this.config);
159
- if (token) {
160
- headers["Authorization"] = `Bearer ${token}`;
161
- }
162
- const req = request;
163
- if (req.headers?.cookie) {
164
- headers["Cookie"] = req.headers.cookie;
165
- }
166
- }
167
- return fetchFn(fullUrl, {
168
- ...init,
169
- headers,
170
- // Include cookies for same-origin requests (supports cookie-based auth)
171
- credentials: this.config.credentials ?? "include"
172
- });
173
- }
174
- }
175
- export {
176
- GrantClient as G,
177
- extractBearerToken as a,
178
- extractTokenFromRequest as e
179
- };
180
- //# sourceMappingURL=grant-client-D1LZI2f4.js.map
@@ -1 +0,0 @@
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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"}
@@ -1,179 +0,0 @@
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