@dudousxd/nestjs-authz-testing 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +35 -15
- package/dist/fakes.d.ts +1 -0
- package/dist/fakes.d.ts.map +1 -1
- package/dist/fakes.js +1 -0
- package/dist/fakes.js.map +1 -1
- package/dist/index.d.ts +69 -54
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -52
- package/dist/index.js.map +1 -1
- package/package.json +32 -19
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @dudousxd/nestjs-authz-testing
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#67](https://github.com/DavideCarvalho/nestjs-authz/pull/67) [`32e967f`](https://github.com/DavideCarvalho/nestjs-authz/commit/32e967ffbcd68a97e8effc77af9c16bbbafdc918) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `buildGate` / `buildBoundGate` are async and compile a Nest testing module with the official
|
|
8
|
+
`AuthorizationModule` and `AuthzModule`, so decisions run through `AuthorizationService`.
|
|
9
|
+
`policies` takes classes (or providers) instead of instances; the `after` option is gone. New:
|
|
10
|
+
`createAuthzTestingModule()`, official-style `expectCan(gate, user, Policy, 'ability', ...args)`,
|
|
11
|
+
and `requirementsOf(Controller, 'method')`. Peers on `@nestjs/authorization`,
|
|
12
|
+
`@nestjs/common`/`core`/`testing` 12.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#67](https://github.com/DavideCarvalho/nestjs-authz/pull/67) [`32e967f`](https://github.com/DavideCarvalho/nestjs-authz/commit/32e967ffbcd68a97e8effc77af9c16bbbafdc918) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Fix packages that could be published without files their `exports` point to. `tsc` builds no longer use a `.tsbuildinfo` that can outlive `dist/` and skip emitting files. A new `check:exports` step (also `prepack`) fails the release if any `exports`/`main`/`types` path is missing.
|
|
17
|
+
|
|
18
|
+
## 0.1.1
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [#66](https://github.com/DavideCarvalho/nestjs-authz/pull/66) [`d5fdd23`](https://github.com/DavideCarvalho/nestjs-authz/commit/d5fdd23a2b72ae61fae0f913f11164bc4d2df77b) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Authorization checks within one request no longer hit the database per check. The Gate memoizes `resolveUser`, the permission set and the provider roles on the request context (keyed by the row a user names, its class or `type` plus `id`, so ORMs without an identity map share entries; a plain object without `type` is keyed by identity, so two users that only share an `id` never share an entry), and compiles the granted set once so a check is a hash lookup plus a scan of the wildcard grants. Providers can set the optional `exhaustivePermissions` flag to say `getPermissions` lists everything `hasPermission` grants; the ORM adapters do, and the MikroORM and Prisma providers now expose `getPermissions` (so persisted wildcard grants such as `posts.*` match there too). Grants changed mid-request apply from the next request.
|
package/README.md
CHANGED
|
@@ -1,34 +1,54 @@
|
|
|
1
1
|
# `@dudousxd/nestjs-authz-testing`
|
|
2
2
|
|
|
3
|
-
Test helpers for [`@dudousxd/nestjs-authz`](../core)
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Test helpers for [`@dudousxd/nestjs-authz`](../core) on
|
|
4
|
+
[`@nestjs/authorization`](https://docs.nestjs.com/security/authorization). `buildGate` compiles
|
|
5
|
+
a real Nest testing module with the official `AuthorizationModule` and `AuthzModule`, so every
|
|
6
|
+
policy decision goes through the official `AuthorizationService`, exactly as in the app.
|
|
6
7
|
|
|
7
8
|
```ts
|
|
8
9
|
import 'reflect-metadata';
|
|
9
|
-
import { Policy } from '@
|
|
10
|
+
import { Policy } from '@nestjs/authorization';
|
|
11
|
+
import { PolicyFor } from '@dudousxd/nestjs-authz';
|
|
10
12
|
import { buildGate, expectCan, expectCannot, FakePermissionProvider } from '@dudousxd/nestjs-authz-testing';
|
|
11
13
|
|
|
12
|
-
@Policy(
|
|
14
|
+
@Policy()
|
|
15
|
+
@PolicyFor(Post)
|
|
13
16
|
class PostPolicy {
|
|
14
|
-
update(user, post) { return user?.id === post.authorId; }
|
|
17
|
+
update(user: User | null, post: Post) { return user?.id === post.authorId; }
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
const gate = buildGate({ policies: [
|
|
18
|
-
await expectCan(gate, { id: 1 }, 'update', new Post(10, 1));
|
|
20
|
+
const gate = await buildGate({ policies: [PostPolicy] });
|
|
21
|
+
await expectCan(gate, { id: 1 }, PostPolicy, 'update', new Post(10, 1)); // official style
|
|
22
|
+
await expectCan(gate, { id: 1 }, 'update', new Post(10, 1)); // via @PolicyFor(Post)
|
|
19
23
|
await expectCannot(gate, { id: 2 }, 'update', new Post(10, 1));
|
|
20
24
|
|
|
21
|
-
// RBAC seam, zero DB
|
|
22
|
-
const gate2 = buildGate({ permissionProvider: new FakePermissionProvider({ '1': ['posts.*'] }) });
|
|
25
|
+
// RBAC seam, zero DB; wildcard grants work through the core matcher:
|
|
26
|
+
const gate2 = await buildGate({ permissionProvider: new FakePermissionProvider({ '1': ['posts.*'] }) });
|
|
23
27
|
await expectCan(gate2, { id: 1 }, 'posts.update');
|
|
24
28
|
```
|
|
25
29
|
|
|
30
|
+
Assert what a route requires without booting HTTP:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
expect(requirementsOf(PostController, 'publish')).toEqual([
|
|
34
|
+
{ policy: PermissionPolicy, ability: 'has', staticArgs: ['posts.publish'], dynamic: false },
|
|
35
|
+
]);
|
|
36
|
+
```
|
|
37
|
+
|
|
26
38
|
## API
|
|
27
39
|
|
|
28
|
-
- `buildGate(options?)` → `Gate
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
- `buildGate(options?)` → `Promise<Gate>`; `createAuthzTestingModule(options?)` →
|
|
41
|
+
`{ module, gate }` (close `module` when done). Options: `{ policies, providers, controllers,
|
|
42
|
+
gates, superAdmin, resolveRoles, resourceResolver, getUser, permissionProvider, roleProvider }`.
|
|
43
|
+
`policies` are classes (or providers, e.g. `{ provide: PostPolicy, useValue }`).
|
|
44
|
+
- `buildBoundGate(user, options?)` → `Promise<BoundGate>`.
|
|
45
|
+
- `expectCan(gate, user, ability, resource?)` / `expectCan(gate, user, Policy, ability, ...args)`,
|
|
46
|
+
and `expectCannot(...)`. Accept a `Gate` + user, or a `BoundGate` with `undefined` user.
|
|
47
|
+
`expectCannot` treats an unresolved ability as "cannot".
|
|
48
|
+
- `requirementsOf(Controller, 'method')` — the official `@Can()` requirements the guard
|
|
49
|
+
enforces there, class-level first, with the arguments of static resolvers.
|
|
33
50
|
- `FakePermissionProvider` / `FakeRoleProvider` — in-memory RBAC seams keyed by user id.
|
|
34
51
|
- `AuthzAssertionError` — thrown on assertion failure.
|
|
52
|
+
|
|
53
|
+
Migrating from 0.1: `buildGate` / `buildBoundGate` are now async, take policy classes instead of
|
|
54
|
+
instances, and the `after` option is gone.
|
package/dist/fakes.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type FakePermissionGrants = Record<string, string[]>;
|
|
|
17
17
|
* await expectCan(gate, { id: 1 }, 'posts.update'); // wildcard grant
|
|
18
18
|
*/
|
|
19
19
|
export declare class FakePermissionProvider implements PermissionProvider {
|
|
20
|
+
readonly exhaustivePermissions = true;
|
|
20
21
|
private readonly grants;
|
|
21
22
|
private readonly identity;
|
|
22
23
|
constructor(grants?: FakePermissionGrants, identity?: (user: User) => string);
|
package/dist/fakes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakes.d.ts","sourceRoot":"","sources":["../src/fakes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAErF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAY5D;;;;;;;;;;GAUG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBAGhD,MAAM,GAAE,oBAAyB,EACjC,QAAQ,GAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAwB;IAMpD,gDAAgD;IAChD,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ3C,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAItD,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS;CAIjD;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBAGhD,KAAK,GAAE,oBAAyB,EAChC,QAAQ,GAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAwB;IAMpD,2CAA2C;IAC3C,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQtC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS;CAI3C"}
|
|
1
|
+
{"version":3,"file":"fakes.d.ts","sourceRoot":"","sources":["../src/fakes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAErF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAY5D;;;;;;;;;;GAUG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,QAAQ,CAAC,qBAAqB,QAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBAGhD,MAAM,GAAE,oBAAyB,EACjC,QAAQ,GAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAwB;IAMpD,gDAAgD;IAChD,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ3C,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAItD,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS;CAIjD;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBAGhD,KAAK,GAAE,oBAAyB,EAChC,QAAQ,GAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAwB;IAMpD,2CAA2C;IAC3C,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQtC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS;CAI3C"}
|
package/dist/fakes.js
CHANGED
|
@@ -21,6 +21,7 @@ function defaultIdentity(user) {
|
|
|
21
21
|
* await expectCan(gate, { id: 1 }, 'posts.update'); // wildcard grant
|
|
22
22
|
*/
|
|
23
23
|
export class FakePermissionProvider {
|
|
24
|
+
exhaustivePermissions = true;
|
|
24
25
|
grants;
|
|
25
26
|
identity;
|
|
26
27
|
constructor(grants = {}, identity = defaultIdentity) {
|
package/dist/fakes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakes.js","sourceRoot":"","sources":["../src/fakes.ts"],"names":[],"mappings":"AASA,8EAA8E;AAC9E,SAAS,eAAe,CAAC,IAAU;IACjC,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAI,IAAyB,CAAC,EAAE,CAAC;QACzC,IAAI,EAAE,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,sBAAsB;
|
|
1
|
+
{"version":3,"file":"fakes.js","sourceRoot":"","sources":["../src/fakes.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,84 +1,99 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Test helpers for `@dudousxd/nestjs-authz`.
|
|
2
|
+
* Test helpers for `@dudousxd/nestjs-authz` on `@nestjs/authorization`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* {@link buildGate} compiles a real Nest testing module with the official
|
|
5
|
+
* `AuthorizationModule` and `AuthzModule`, so every policy decision goes through the
|
|
6
|
+
* official `AuthorizationService`, exactly as in the app. Assert with
|
|
7
|
+
* {@link expectCan} / {@link expectCannot}, and read what a route requires with
|
|
8
|
+
* {@link requirementsOf}.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
|
-
* const gate = buildGate({ policies: [
|
|
13
|
-
* await expectCan(gate, { id: 1 }, 'update', new Post(
|
|
14
|
-
* await
|
|
12
|
+
* const gate = await buildGate({ policies: [PostPolicy] });
|
|
13
|
+
* await expectCan(gate, { id: 1 }, 'update', new Post(10, 1)); // linked with @PolicyFor(Post)
|
|
14
|
+
* await expectCan(gate, { id: 1 }, PostPolicy, 'update', new Post(10, 1)); // official style
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import { BoundGate, Gate, type GateFn, type PermissionProvider, type Resource, type ResourceResolver, type RoleProvider, type SuperAdminHook, type User } from '@dudousxd/nestjs-authz';
|
|
20
|
+
import type { ExecutionContext, Provider, Type } from '@nestjs/common';
|
|
21
|
+
import { type TestingModule } from '@nestjs/testing';
|
|
22
|
+
export { FakePermissionProvider, FakeRoleProvider } from './fakes.js';
|
|
21
23
|
export type { FakePermissionGrants } from './fakes.js';
|
|
22
24
|
/** Fixture configuration for {@link buildGate}. Every field is optional. */
|
|
23
25
|
export interface BuildGateOptions {
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/** Ad-hoc, model-less gates keyed by ability name (same as `gate.define(name, fn)`). */
|
|
26
|
+
/** Official `@Policy()` classes (or providers, e.g. `{ provide: PostPolicy, useValue }`). */
|
|
27
|
+
policies?: Provider[];
|
|
28
|
+
/** More providers the policies depend on. */
|
|
29
|
+
providers?: Provider[];
|
|
30
|
+
/** Controllers, when the test also boots routes. */
|
|
31
|
+
controllers?: Type<unknown>[];
|
|
32
|
+
/** Ad-hoc gates keyed by ability name (`gate.define(name, fn)`). */
|
|
32
33
|
gates?: Record<string, GateFn>;
|
|
33
|
-
/**
|
|
34
|
+
/** The global super-admin hook. */
|
|
34
35
|
superAdmin?: SuperAdminHook;
|
|
35
|
-
/**
|
|
36
|
-
after?: AfterHook;
|
|
37
|
-
/** Override how a user's roles are derived (default reads `user.roles`/`user.role`). */
|
|
36
|
+
/** How roles are read off the user (default `user.roles` / `user.role`). */
|
|
38
37
|
resolveRoles?: (user: User) => string[] | undefined | Promise<string[] | undefined>;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
/** The resolver `byId()` uses. */
|
|
39
|
+
resourceResolver?: ResourceResolver;
|
|
40
|
+
/** The official `getUser`, for routes. */
|
|
41
|
+
getUser?: (context: ExecutionContext) => unknown;
|
|
42
|
+
/** An in-memory permission seam. See {@link FakePermissionProvider}. */
|
|
44
43
|
permissionProvider?: PermissionProvider;
|
|
45
|
-
/** An in-memory
|
|
44
|
+
/** An in-memory role seam. See {@link FakeRoleProvider}. */
|
|
46
45
|
roleProvider?: RoleProvider;
|
|
47
46
|
}
|
|
47
|
+
/** The compiled testing module, initialized, and its {@link Gate}. */
|
|
48
|
+
export interface AuthzTestingModule {
|
|
49
|
+
module: TestingModule;
|
|
50
|
+
gate: Gate;
|
|
51
|
+
}
|
|
48
52
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* same resolution order); only the wiring is in-memory.
|
|
52
|
-
*/
|
|
53
|
-
export declare function buildGate(options?: BuildGateOptions): Gate;
|
|
54
|
-
/**
|
|
55
|
-
* Build a {@link BoundGate} for a fixture `user` directly — sugar over
|
|
56
|
-
* `buildGate(options).forUser(user)` for tests that focus on a single principal.
|
|
57
|
-
*/
|
|
58
|
-
export declare function buildBoundGate(user: User, options?: BuildGateOptions): BoundGate;
|
|
59
|
-
/**
|
|
60
|
-
* Accept either a {@link Gate} (then `user` is required) or an already-bound
|
|
61
|
-
* {@link BoundGate}. Lets the assertion helpers serve both styles.
|
|
53
|
+
* Compile and initialize a Nest testing module with the official `AuthorizationModule`,
|
|
54
|
+
* `AuthzModule` and the fixtures. Close `module` when done (or let the test process end).
|
|
62
55
|
*/
|
|
56
|
+
export declare function createAuthzTestingModule(options?: BuildGateOptions): Promise<AuthzTestingModule>;
|
|
57
|
+
/** A real {@link Gate} from fixtures. See {@link createAuthzTestingModule}. */
|
|
58
|
+
export declare function buildGate(options?: BuildGateOptions): Promise<Gate>;
|
|
59
|
+
/** A {@link BoundGate} for a fixture `user`: `(await buildGate(options)).forUser(user)`. */
|
|
60
|
+
export declare function buildBoundGate(user: User, options?: BuildGateOptions): Promise<BoundGate>;
|
|
61
|
+
/** A {@link Gate} (then `user` is required) or an already-bound {@link BoundGate}. */
|
|
63
62
|
export type GateLike = Gate | BoundGate;
|
|
64
63
|
/** A thrown assertion failure from {@link expectCan}/{@link expectCannot}. */
|
|
65
64
|
export declare class AuthzAssertionError extends Error {
|
|
66
65
|
constructor(message: string);
|
|
67
66
|
}
|
|
67
|
+
/** What a check names: a string ability (+ resource), or an official policy + ability (+ args). */
|
|
68
|
+
type Check = [ability: string, resource?: Resource] | [policy: Type<unknown>, ability: string, ...args: unknown[]];
|
|
68
69
|
/**
|
|
69
|
-
* Assert the user CAN
|
|
70
|
-
*
|
|
71
|
-
* {@link Gate} (+ `user`) or a {@link BoundGate} (pass `undefined` for `user`).
|
|
72
|
-
*
|
|
73
|
-
* @example await expectCan(gate, { id: 1 }, 'update', post);
|
|
74
|
-
* @example await expectCan(boundGate, undefined, 'create');
|
|
70
|
+
* Assert the user CAN: `expectCan(gate, user, 'update', post)` or
|
|
71
|
+
* `expectCan(gate, user, PostPolicy, 'update', post)`. Throws {@link AuthzAssertionError}.
|
|
75
72
|
*/
|
|
76
|
-
export declare function expectCan(gate: GateLike, user: User,
|
|
73
|
+
export declare function expectCan(gate: GateLike, user: User, ...check: Check): Promise<void>;
|
|
77
74
|
/**
|
|
78
|
-
* Assert the user CANNOT
|
|
79
|
-
*
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
* Assert the user CANNOT. An unresolved ability (no policy, gate or permission answers
|
|
76
|
+
* it) counts as "cannot", the deny-by-default outcome.
|
|
77
|
+
*/
|
|
78
|
+
export declare function expectCannot(gate: GateLike, user: User, ...check: Check): Promise<void>;
|
|
79
|
+
/** One official `@Can()` requirement of a route, with its static arguments when it has some. */
|
|
80
|
+
export interface RouteRequirement {
|
|
81
|
+
policy: Type<unknown>;
|
|
82
|
+
ability: string;
|
|
83
|
+
/** The arguments of a static resolver (`roles('admin')`, `permission(...)`), else `undefined`. */
|
|
84
|
+
staticArgs: readonly unknown[] | undefined;
|
|
85
|
+
/** True when the resolver depends on the call (`byId(Post)`, a custom function). */
|
|
86
|
+
dynamic: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* What the official guard requires on `controller.method`, class-level first. Lets a test
|
|
90
|
+
* assert a route's protection without booting HTTP:
|
|
91
|
+
*
|
|
92
|
+
* ```ts
|
|
93
|
+
* expect(requirementsOf(PostController, 'publish')).toEqual([
|
|
94
|
+
* { policy: PermissionPolicy, ability: 'has', staticArgs: ['posts.publish'], dynamic: false },
|
|
95
|
+
* ]);
|
|
96
|
+
* ```
|
|
82
97
|
*/
|
|
83
|
-
export declare function
|
|
98
|
+
export declare function requirementsOf(controller: Type<unknown>, method: string): RouteRequirement[];
|
|
84
99
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,50 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Test helpers for `@dudousxd/nestjs-authz`.
|
|
2
|
+
* Test helpers for `@dudousxd/nestjs-authz` on `@nestjs/authorization`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* {@link buildGate} compiles a real Nest testing module with the official
|
|
5
|
+
* `AuthorizationModule` and `AuthzModule`, so every policy decision goes through the
|
|
6
|
+
* official `AuthorizationService`, exactly as in the app. Assert with
|
|
7
|
+
* {@link expectCan} / {@link expectCannot}, and read what a route requires with
|
|
8
|
+
* {@link requirementsOf}.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
|
-
* const gate = buildGate({ policies: [
|
|
13
|
-
* await expectCan(gate, { id: 1 }, 'update', new Post(
|
|
14
|
-
* await
|
|
12
|
+
* const gate = await buildGate({ policies: [PostPolicy] });
|
|
13
|
+
* await expectCan(gate, { id: 1 }, 'update', new Post(10, 1)); // linked with @PolicyFor(Post)
|
|
14
|
+
* await expectCan(gate, { id: 1 }, PostPolicy, 'update', new Post(10, 1)); // official style
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
import { BoundGate, Gate,
|
|
20
|
-
|
|
19
|
+
import { AuthzModule, BoundGate, Gate, PERMISSION_PROVIDER, ROLE_PROVIDER, getStaticCanArgs, routeRequirements, } from '@dudousxd/nestjs-authz';
|
|
20
|
+
import { AuthorizationModule } from '@nestjs/authorization';
|
|
21
|
+
import { Test } from '@nestjs/testing';
|
|
22
|
+
export { FakePermissionProvider, FakeRoleProvider } from './fakes.js';
|
|
21
23
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* same resolution order); only the wiring is in-memory.
|
|
24
|
+
* Compile and initialize a Nest testing module with the official `AuthorizationModule`,
|
|
25
|
+
* `AuthzModule` and the fixtures. Close `module` when done (or let the test process end).
|
|
25
26
|
*/
|
|
26
|
-
export function
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
export async function createAuthzTestingModule(options = {}) {
|
|
28
|
+
const module = await Test.createTestingModule({
|
|
29
|
+
imports: [
|
|
30
|
+
AuthorizationModule.forRoot(options.getUser ? { getUser: options.getUser } : {}),
|
|
31
|
+
AuthzModule.forRoot({
|
|
32
|
+
...(options.superAdmin ? { superAdmin: options.superAdmin } : {}),
|
|
33
|
+
...(options.resolveRoles ? { resolveRoles: options.resolveRoles } : {}),
|
|
34
|
+
...(options.resourceResolver ? { resourceResolver: options.resourceResolver } : {}),
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
controllers: options.controllers ?? [],
|
|
38
|
+
providers: [
|
|
39
|
+
...(options.policies ?? []),
|
|
40
|
+
...(options.providers ?? []),
|
|
41
|
+
...(options.permissionProvider
|
|
42
|
+
? [{ provide: PERMISSION_PROVIDER, useValue: options.permissionProvider }]
|
|
43
|
+
: []),
|
|
44
|
+
...(options.roleProvider ? [{ provide: ROLE_PROVIDER, useValue: options.roleProvider }] : []),
|
|
45
|
+
],
|
|
46
|
+
}).compile();
|
|
47
|
+
module.useLogger(false);
|
|
48
|
+
await module.init();
|
|
49
|
+
const gate = module.get(Gate);
|
|
50
|
+
for (const [ability, fn] of Object.entries(options.gates ?? {}))
|
|
38
51
|
gate.define(ability, fn);
|
|
39
|
-
}
|
|
40
|
-
return gate;
|
|
52
|
+
return { module, gate };
|
|
41
53
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
/** A real {@link Gate} from fixtures. See {@link createAuthzTestingModule}. */
|
|
55
|
+
export async function buildGate(options = {}) {
|
|
56
|
+
return (await createAuthzTestingModule(options)).gate;
|
|
57
|
+
}
|
|
58
|
+
/** A {@link BoundGate} for a fixture `user`: `(await buildGate(options)).forUser(user)`. */
|
|
59
|
+
export async function buildBoundGate(user, options = {}) {
|
|
60
|
+
return (await buildGate(options)).forUser(user);
|
|
48
61
|
}
|
|
49
62
|
function bind(gate, user) {
|
|
50
63
|
return gate instanceof BoundGate ? gate : gate.forUser(user);
|
|
@@ -56,39 +69,59 @@ export class AuthzAssertionError extends Error {
|
|
|
56
69
|
this.name = 'AuthzAssertionError';
|
|
57
70
|
}
|
|
58
71
|
}
|
|
72
|
+
function allows(gate, check) {
|
|
73
|
+
return gate.allows.apply(gate, check);
|
|
74
|
+
}
|
|
75
|
+
function describeCheck(check) {
|
|
76
|
+
if (typeof check[0] === 'function')
|
|
77
|
+
return `${check[0].name}.${String(check[1])}`;
|
|
78
|
+
const [ability, resource] = check;
|
|
79
|
+
return `"${ability}"${describeResource(resource)}`;
|
|
80
|
+
}
|
|
59
81
|
/**
|
|
60
|
-
* Assert the user CAN
|
|
61
|
-
*
|
|
62
|
-
* {@link Gate} (+ `user`) or a {@link BoundGate} (pass `undefined` for `user`).
|
|
63
|
-
*
|
|
64
|
-
* @example await expectCan(gate, { id: 1 }, 'update', post);
|
|
65
|
-
* @example await expectCan(boundGate, undefined, 'create');
|
|
82
|
+
* Assert the user CAN: `expectCan(gate, user, 'update', post)` or
|
|
83
|
+
* `expectCan(gate, user, PostPolicy, 'update', post)`. Throws {@link AuthzAssertionError}.
|
|
66
84
|
*/
|
|
67
|
-
export async function expectCan(gate, user,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
throw new AuthzAssertionError(`Expected user to be able to "${ability}"${describeResource(resource)}, but it was DENIED.`);
|
|
85
|
+
export async function expectCan(gate, user, ...check) {
|
|
86
|
+
if (!(await allows(bind(gate, user), check))) {
|
|
87
|
+
throw new AuthzAssertionError(`Expected user to be able to ${describeCheck(check)}, but it was DENIED.`);
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
/**
|
|
74
|
-
* Assert the user CANNOT
|
|
75
|
-
*
|
|
76
|
-
* (no policy/gate) counts as "cannot" — the deny-by-default outcome a caller wants
|
|
77
|
-
* to assert — so this never leaks the underlying `AbilityNotResolvedException`.
|
|
91
|
+
* Assert the user CANNOT. An unresolved ability (no policy, gate or permission answers
|
|
92
|
+
* it) counts as "cannot", the deny-by-default outcome.
|
|
78
93
|
*/
|
|
79
|
-
export async function expectCannot(gate, user,
|
|
94
|
+
export async function expectCannot(gate, user, ...check) {
|
|
80
95
|
let allowed;
|
|
81
96
|
try {
|
|
82
|
-
allowed = await bind(gate, user)
|
|
97
|
+
allowed = await allows(bind(gate, user), check);
|
|
83
98
|
}
|
|
84
99
|
catch {
|
|
85
|
-
// Unresolved/ambiguous ability → effectively "cannot" (deny by default).
|
|
86
100
|
return;
|
|
87
101
|
}
|
|
88
102
|
if (allowed) {
|
|
89
|
-
throw new AuthzAssertionError(`Expected user to be UNABLE to
|
|
103
|
+
throw new AuthzAssertionError(`Expected user to be UNABLE to ${describeCheck(check)}, but it was ALLOWED.`);
|
|
90
104
|
}
|
|
91
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* What the official guard requires on `controller.method`, class-level first. Lets a test
|
|
108
|
+
* assert a route's protection without booting HTTP:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* expect(requirementsOf(PostController, 'publish')).toEqual([
|
|
112
|
+
* { policy: PermissionPolicy, ability: 'has', staticArgs: ['posts.publish'], dynamic: false },
|
|
113
|
+
* ]);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export function requirementsOf(controller, method) {
|
|
117
|
+
const handler = controller.prototype[method];
|
|
118
|
+
if (!handler)
|
|
119
|
+
throw new Error(`${controller.name} has no method '${method}'.`);
|
|
120
|
+
return routeRequirements(controller, handler).map(({ policy, ability, args }) => {
|
|
121
|
+
const staticArgs = getStaticCanArgs(args);
|
|
122
|
+
return { policy, ability, staticArgs, dynamic: args !== undefined && staticArgs === undefined };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
92
125
|
function describeResource(resource) {
|
|
93
126
|
if (resource === undefined)
|
|
94
127
|
return '';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,WAAW,EACX,SAAS,EACT,IAAI,EAEJ,mBAAmB,EAEnB,aAAa,EAMb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,IAAI,EAAsB,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiCtE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,UAA4B,EAAE;IAE9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC;QAC5C,OAAO,EAAE;YACP,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,WAAW,CAAC,OAAO,CAAC;gBAClB,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvE,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpF,CAAC;SACH;QACD,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;QACtC,SAAS,EAAE;YACT,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC3B,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;YAC5B,GAAG,CAAC,OAAO,CAAC,kBAAkB;gBAC5B,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,CAAC,kBAAkB,EAAE,CAAC;gBAC1E,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9F;KACF,CAAC,CAAC,OAAO,EAAE,CAAC;IACb,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACpB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1F,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAA4B,EAAE;IAC5D,OAAO,CAAC,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACxD,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAU,EACV,UAA4B,EAAE;IAE9B,OAAO,CAAC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAKD,SAAS,IAAI,CAAC,IAAc,EAAE,IAAU;IACtC,OAAO,IAAI,YAAY,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,8EAA8E;AAC9E,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAOD,SAAS,MAAM,CAAC,IAAe,EAAE,KAAY;IAC3C,OAAQ,IAAI,CAAC,MAAgD,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,aAAa,CAAC,KAAY;IACjC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,UAAU;QAAE,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,KAAuC,CAAC;IACpE,OAAO,IAAI,OAAO,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,IAAU,EAAE,GAAG,KAAY;IACzE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,mBAAmB,CAC3B,+BAA+B,aAAa,CAAC,KAAK,CAAC,sBAAsB,CAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc,EAAE,IAAU,EAAE,GAAG,KAAY;IAC5E,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,IAAI,mBAAmB,CAC3B,iCAAiC,aAAa,CAAC,KAAK,CAAC,uBAAuB,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAYD;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,UAAyB,EAAE,MAAc;IACtE,MAAM,OAAO,GAAI,UAAU,CAAC,SAAoC,CAAC,MAAM,CAAC,CAAC;IACzE,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,UAAU,CAAC,IAAI,mBAAmB,MAAM,IAAI,CAAC,CAAC;IAC/E,OAAO,iBAAiB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9E,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC1C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;IAClG,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,QAA8B;IACtD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACtC,MAAM,IAAI,GAAI,QAAgD,CAAC,WAAW,EAAE,IAAI,CAAC;IACjF,OAAO,IAAI,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC;AAC1E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/nestjs-authz-testing",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Test helpers for @dudousxd/nestjs-authz
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Test helpers for @dudousxd/nestjs-authz on @nestjs/authorization: a real Gate on a Nest testing module, expectCan/expectCannot, in-memory RBAC fakes, route requirement inspection.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/DavideCarvalho/nestjs-authz.git",
|
|
9
9
|
"directory": "packages/testing"
|
|
10
10
|
},
|
|
11
|
+
"homepage": "https://davidecarvalho.github.io/aviary/docs/authz",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/DavideCarvalho/nestjs-authz/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"nestjs",
|
|
17
|
+
"authorization",
|
|
18
|
+
"authz",
|
|
19
|
+
"testing",
|
|
20
|
+
"gate",
|
|
21
|
+
"policy",
|
|
22
|
+
"fixtures"
|
|
23
|
+
],
|
|
11
24
|
"author": "Davi Carvalho <davi@goflip.ai>",
|
|
12
25
|
"type": "module",
|
|
13
26
|
"main": "./dist/index.js",
|
|
@@ -25,30 +38,30 @@
|
|
|
25
38
|
"CHANGELOG.md"
|
|
26
39
|
],
|
|
27
40
|
"peerDependencies": {
|
|
28
|
-
"@dudousxd/nestjs-authz": ">=0.
|
|
41
|
+
"@dudousxd/nestjs-authz": ">=0.9.0 <0.10.0",
|
|
42
|
+
"@nestjs/authorization": "^0.1.3",
|
|
43
|
+
"@nestjs/common": "^12.0.0",
|
|
44
|
+
"@nestjs/core": "^12.0.0",
|
|
45
|
+
"@nestjs/testing": "^12.0.0",
|
|
29
46
|
"reflect-metadata": ">=0.1.13"
|
|
30
47
|
},
|
|
31
48
|
"devDependencies": {
|
|
32
|
-
"@
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"@
|
|
49
|
+
"@nestjs/authorization": "0.1.3",
|
|
50
|
+
"@nestjs/common": "12.0.1",
|
|
51
|
+
"@nestjs/core": "12.0.1",
|
|
52
|
+
"@nestjs/testing": "12.0.1",
|
|
53
|
+
"@types/node": "22.20.5",
|
|
54
|
+
"reflect-metadata": "0.2.2",
|
|
55
|
+
"rxjs": "7.8.2",
|
|
56
|
+
"typescript": "5.9.3",
|
|
57
|
+
"vitest": "3.2.7",
|
|
58
|
+
"@dudousxd/nestjs-authz": "^0.9.1"
|
|
37
59
|
},
|
|
38
60
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
61
|
+
"node": ">=22"
|
|
40
62
|
},
|
|
41
|
-
"keywords": [
|
|
42
|
-
"nestjs",
|
|
43
|
-
"authorization",
|
|
44
|
-
"authz",
|
|
45
|
-
"testing",
|
|
46
|
-
"gate",
|
|
47
|
-
"policy",
|
|
48
|
-
"fixtures"
|
|
49
|
-
],
|
|
50
63
|
"scripts": {
|
|
51
|
-
"build": "tsc -p tsconfig.json",
|
|
64
|
+
"build": "tsc -p tsconfig.json --incremental false",
|
|
52
65
|
"test": "vitest run --passWithNoTests",
|
|
53
66
|
"test:watch": "vitest",
|
|
54
67
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|