|
1 | 1 | // shoutout https://lucas-barake.github.io/building-a-composable-policy-system/
|
2 | 2 |
|
3 | 3 | import { type Brand, Context, Data, Effect, type Option, Schema } from "effect";
|
4 |
| - |
| 4 | +import type { NonEmptyReadonlyArray } from "effect/Array"; |
5 | 5 | import { CurrentUser } from "./Authentication.ts";
|
6 | 6 |
|
7 |
| -export type Policy<E = never, R = never> = Brand.Branded< |
8 |
| - Effect.Effect<void, PolicyDeniedError | E, CurrentUser | R>, |
9 |
| - "Private" |
| 7 | +export type Policy<E = never, R = never> = Effect.Effect< |
| 8 | + void, |
| 9 | + PolicyDeniedError | E, |
| 10 | + CurrentUser | R |
10 | 11 | >;
|
11 | 12 |
|
12 |
| -export type PublicPolicy<E = never, R = never> = Brand.Branded< |
13 |
| - Effect.Effect<void, PolicyDeniedError | E, R>, |
14 |
| - "Public" |
| 13 | +export type PublicPolicy<E = never, R = never> = Effect.Effect< |
| 14 | + void, |
| 15 | + PolicyDeniedError | E, |
| 16 | + R |
15 | 17 | >;
|
16 | 18 |
|
17 | 19 | export class PolicyDeniedError extends Schema.TaggedError<PolicyDeniedError>()(
|
@@ -73,3 +75,23 @@ export const withPublicPolicy =
|
73 | 75 | <E, R>(policy: PublicPolicy<E, R>) =>
|
74 | 76 | <A, E2, R2>(self: Effect.Effect<A, E2, R2>) =>
|
75 | 77 | Effect.zipRight(policy, self);
|
| 78 | + |
| 79 | +/** |
| 80 | + * Composes multiple policies with AND semantics - all policies must pass. |
| 81 | + * Returns a new policy that succeeds only if all the given policies succeed. |
| 82 | + */ |
| 83 | +export const all = <E, R>( |
| 84 | + ...policies: NonEmptyReadonlyArray<Policy<E, R>> |
| 85 | +): Policy<E, R> => |
| 86 | + Effect.all(policies, { |
| 87 | + concurrency: 1, |
| 88 | + discard: true, |
| 89 | + }); |
| 90 | + |
| 91 | +/** |
| 92 | + * Composes multiple policies with OR semantics - at least one policy must pass. |
| 93 | + * Returns a new policy that succeeds if any of the given policies succeed. |
| 94 | + */ |
| 95 | +export const any = <E, R>( |
| 96 | + ...policies: NonEmptyReadonlyArray<Policy<E, R>> |
| 97 | +): Policy<E, R> => Effect.firstSuccessOf(policies); |
0 commit comments