@zap-studio/permit 0.3.3 → 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.
package/CHANGELOG.md CHANGED
@@ -1,98 +1,112 @@
1
- ## @zap-studio/permit@0.3.3
1
+ # Changelog
2
2
 
3
- ### Migrate to ultracite lint/format
3
+ All notable changes to this project will be documented in this file.
4
4
 
5
- Internal formatting and lint cleanup only. No public API or behavior change.
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- # @zap-studio/permit
7
+ ## [1.0.0]
8
8
 
9
- ## 0.3.2
9
+ ### Changed
10
10
 
11
- ### Dependencies
11
+ - Clarified tree-shakeable design in the package description and README (no code change).
12
12
 
13
- - Updated dependency `@zap-studio/validation` to `0.3.4`.
13
+ ### Removed
14
14
 
15
- ## 0.3.1
15
+ `assertNever` is no longer exported — it had no internal consumers and existed only as a generic exhaustiveness-check convenience. Use your own `never`-typed helper if you relied on it.
16
16
 
17
17
  ### Fixed
18
18
 
19
- - d10d8c4: Removed the TanStack Intent-specific authoring helper from the package surface.
20
- - d9ba7d1: Reworked the packaged permit skill content.
19
+ Neither merge strategy short-circuits anymore. All policies now run concurrently via `Promise.allSettled`, and every policy is invoked regardless of outcome, for both `mergePoliciesSome` (allow) and `mergePoliciesEvery` (deny). A rejecting policy no longer sinks the whole check — it's treated as a deny (`false`) and logged with `console.warn`, consistent with how `createPolicy` handles internal validation/evaluation errors.
20
+
21
+ ## [0.3.4]
22
+
23
+ ### Added
24
+
25
+ The package root now re-exports the full public API, so everything can be imported from `@zap-studio/permit` directly, including `PolicyError`, `assertNever`, and all public types. All exports are side-effect free and tree-shakeable; granular subpath imports keep working.
26
+
27
+ - The implementation moved out of the entrypoint into two new subpaths: `./conditions` (`allow`, `deny`, `when`, `and`, `or`, `not`, `has`, `hasRole`, `collectInheritedRoles`) and `./policy` (`createPolicy`, `mergePolicies`, `mergePoliciesAny`).
28
+
29
+ ## [0.3.3]
30
+
31
+ ### Changed
32
+
33
+ Internal formatting and lint cleanup only. No public API or behavior change.
34
+
35
+ ## [0.3.2]
36
+
37
+ ### Changed
38
+
39
+ - Updated dependency `@zap-studio/validation` to `0.3.4`.
40
+
41
+ ## [0.3.1]
21
42
 
22
43
  ### Changed
23
44
 
24
45
  - 5fa58b1: Reduced policy evaluation complexity by extracting permission parsing and merge strategy helpers without changing the `policy.can(...)` API.
25
46
  - abfda4b: Cleaned up public option typings by removing redundant `| undefined` unions from package types.
26
47
  - 9f31f87: Switched the package build to ESNext-aligned output and updated package tooling and publish metadata.
48
+ - d9ba7d1: Reworked the packaged permit skill content.
49
+ - Updated dependency `@zap-studio/validation` to `0.3.3`.
27
50
 
28
- ### Dependencies
51
+ ### Removed
29
52
 
30
- - Updated dependency `@zap-studio/validation` to `0.3.3`.
53
+ - d10d8c4: Removed the TanStack Intent-specific authoring helper from the package surface.
31
54
 
32
- ## 0.3.0
55
+ ## [0.3.0]
33
56
 
34
- ### Minor Changes
57
+ ### Changed
35
58
 
36
- - fe60f55: Change `policy.can()` to use a single permission string plus the resource object.
59
+ - fe60f55: **Breaking:** Changed `policy.can()` to use a single permission string plus the resource object.
37
60
 
38
61
  `policy.can(ctx, "read", "post", post)` is replaced by `policy.can(ctx, "post:read", post)`.
39
62
 
40
63
  This is a breaking API change in the `0.x` line. Docs and examples now use the new permission-string format consistently.
41
64
 
42
- ## 0.2.2
65
+ ## [0.2.2]
43
66
 
44
- ### Patch Changes
67
+ ### Changed
45
68
 
46
69
  - e26293e: Updated dependencies.
47
70
  - @zap-studio/validation@0.3.2
48
71
 
49
- ## 0.2.1
72
+ ## [0.2.1]
50
73
 
51
- ### Patch Changes
74
+ ### Changed
52
75
 
53
76
  - 5ea3d3b: Updated dependencies.
54
77
  - @zap-studio/validation@0.3.1
55
78
 
56
- ## 0.2.0
79
+ ## [0.2.0]
57
80
 
58
81
  ### Changed
59
82
 
60
83
  - f0f503e: Made policy evaluation asynchronous by default.
61
-
62
- ### Breaking Changes
63
-
64
- - f0f503e: `policy.can(...)` now returns `Promise<boolean>`.
65
- - f0f503e: `createPolicy()` now uses async-safe Standard Schema validation for resource schemas (including async resource schemas); this is not a separate `Policy` schema API.
66
- - f0f503e: The `Policy` interface changed so `can()` is async, and `mergePolicies`/`mergePoliciesAny` are async accordingly.
84
+ - f0f503e: **Breaking:** `policy.can(...)` now returns `Promise<boolean>`.
85
+ - f0f503e: **Breaking:** `createPolicy()` now uses async-safe Standard Schema validation for resource schemas (including async resource schemas); this is not a separate `Policy` schema API.
86
+ - f0f503e: **Breaking:** The `Policy` interface changed so `can()` is async, and `mergePolicies`/`mergePoliciesAny` are async accordingly.
67
87
  - Action required: callers must `await policy.can(...)` and handle `mergePolicies`/`mergePoliciesAny` as async operations; also account for async-safe resource schema validation in `createPolicy()`.
68
-
69
- ### Dependencies
70
-
71
88
  - f75b984: Updated dependency `@zap-studio/validation` to `0.3.0`.
72
89
 
73
- ## 0.1.3
90
+ ## [0.1.3]
74
91
 
75
- ### Dependencies
92
+ ### Changed
76
93
 
77
94
  - e4542bb: Updated dependency `@zap-studio/validation` to `0.2.1`.
78
95
 
79
- ## 0.1.2
96
+ ## [0.1.2]
80
97
 
81
98
  ### Changed
82
99
 
83
100
  - 2de8183: Adopted shared synchronous Standard Schema validator utilities from `@zap-studio/validation` for resource schema validation in `createPolicy`.
84
-
85
- ### Dependencies
86
-
87
101
  - 2de8183: Updated dependency `@zap-studio/validation` to `0.2.0`.
88
102
 
89
- ## 0.1.1
103
+ ## [0.1.1]
90
104
 
91
105
  ### Fixed
92
106
 
93
107
  - 907d903: Added runtime resource validation and fail-closed behavior, including deny-by-default when merges are invoked with no policies.
94
108
 
95
- ## 0.1.0
109
+ ## [0.1.0]
96
110
 
97
111
  ### Added
98
112
 
package/README.md CHANGED
@@ -2,137 +2,93 @@
2
2
 
3
3
  A type-safe, declarative authorization library for TypeScript with [Standard Schema](https://standardschema.dev/) support.
4
4
 
5
- ## Features
6
-
7
- - Full type safety with TypeScript
8
- - Standard Schema support (Zod, Valibot, ArkType, etc.)
9
- - Declarative policy definitions
10
- - Role hierarchy support
11
- - Composable conditions (`and`, `or`, `not`)
12
- - Policy merging strategies
5
+ Full documentation: [zapstudio.dev/permit](https://www.zapstudio.dev/permit)
13
6
 
14
7
  ## Installation
15
8
 
16
9
  ```bash
17
- pnpm add @zap-studio/permit
18
- # or
19
10
  npm install @zap-studio/permit
20
11
  ```
21
12
 
13
+ You also need a schema library that implements [Standard Schema](https://standardschema.dev/), such as Zod, Valibot, or ArkType.
14
+
15
+ ## Features
16
+
17
+ - **Full type safety** — actions, resources, and permissions are inferred from your schemas and `satisfies` declarations.
18
+ - **Standard Schema support** via `Resources` — works with Zod, Valibot, ArkType, or any compatible library.
19
+ - **Declarative policies** through `createPolicy(...)` with `allow()`, `deny()`, and `when(condition)`.
20
+ - **Role hierarchy support** via `hasRole(role, hierarchy?)`, with inheritance resolved by `collectInheritedRoles`.
21
+ - **Composable conditions** via `and`, `or`, and `not`.
22
+ - **Policy merging strategies** via `mergePoliciesAnd` and `mergePoliciesOr`.
23
+ - **Structured errors** with `PolicyError` for invalid configuration or evaluation failures.
24
+ - **Tree-shakeable** — policies and conditions are plain functions; unused exports are dropped by any modern bundler.
25
+
22
26
  ## Quick Start
23
27
 
24
28
  ```ts
25
29
  import { z } from "zod";
26
30
  import { createPolicy, allow, deny, when } from "@zap-studio/permit";
27
- import type { Resources, Actions } from "@zap-studio/permit/types";
31
+ import type { Resources, Actions } from "@zap-studio/permit";
28
32
 
29
- // 1. Define your resource schemas
30
33
  const resources = {
31
- post: z.object({
32
- id: z.string(),
33
- authorId: z.string(),
34
- visibility: z.enum(["public", "private"]),
35
- }),
36
- comment: z.object({
37
- id: z.string(),
38
- postId: z.string(),
39
- authorId: z.string(),
40
- }),
34
+ post: z.object({ id: z.string(), authorId: z.string() }),
41
35
  } satisfies Resources;
42
36
 
43
- // 2. Define actions per resource
44
37
  const actions = {
45
38
  post: ["read", "write", "delete"],
46
- comment: ["read", "write"],
47
39
  } as const satisfies Actions<typeof resources>;
48
40
 
49
- // 3. Define your context type
50
- type AppContext = {
51
- user: { id: string; role: "guest" | "user" | "admin" };
52
- };
41
+ type AppContext = { user: { id: string } };
53
42
 
54
- // 4. Create your policy
55
43
  const policy = createPolicy<AppContext>({
56
44
  resources,
57
45
  actions,
58
46
  rules: {
59
47
  post: {
60
- read: when((ctx, action, resource) => resource.visibility === "public"),
61
- write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
62
- delete: deny(),
63
- },
64
- comment: {
65
48
  read: allow(),
66
49
  write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
50
+ delete: deny(),
67
51
  },
68
52
  },
69
53
  });
70
54
 
71
- // 5. Check permissions
72
- const ctx: AppContext = { user: { id: "user-1", role: "user" } };
73
- const post = { id: "1", authorId: "user-1", visibility: "public" as const };
55
+ const ctx: AppContext = { user: { id: "user-1" } };
56
+ const post = { id: "1", authorId: "user-1" };
74
57
 
75
- await policy.can(ctx, "post:read", post); // true
76
- await policy.can(ctx, "post:write", post); // true (user is author)
77
- await policy.can(ctx, "post:delete", post); // false (always denied)
58
+ await policy.can(ctx, "post:write", post); // true, inferred as boolean
78
59
  ```
79
60
 
80
- ## API Reference
81
-
82
- ### Policy Builders
61
+ ## Declarative Policies
83
62
 
84
- #### `allow()`
85
-
86
- Returns a policy function that always allows the action.
63
+ Through `createPolicy(...)` with `allow()`, `deny()`, and `when(condition)`.
87
64
 
88
65
  ```ts
89
66
  rules: {
90
67
  post: {
91
- read: allow(), // Anyone can read
68
+ read: allow(),
69
+ delete: deny(),
70
+ write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
92
71
  },
93
72
  }
94
73
  ```
95
74
 
96
- #### `deny()`
75
+ ## Role Hierarchy Support
97
76
 
98
- Returns a policy function that always denies the action.
77
+ Via `hasRole(role, hierarchy?)`, with inheritance resolved by `collectInheritedRoles`.
99
78
 
100
79
  ```ts
101
- rules: {
102
- post: {
103
- delete: deny(), // No one can delete
104
- },
105
- }
106
- ```
107
-
108
- #### `when(condition)`
109
-
110
- Returns a policy function that allows or denies based on a condition.
80
+ const hierarchy = { guest: [], user: ["guest"], admin: ["user"] };
111
81
 
112
- ```ts
113
82
  rules: {
114
83
  post: {
115
- write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
84
+ read: when(hasRole("guest", hierarchy)), // admins and users inherit guest access
116
85
  },
117
86
  }
118
87
  ```
119
88
 
120
- ### Condition Combinators
89
+ ## Composable Conditions
121
90
 
122
- #### `and(...conditions)`
123
-
124
- Returns a condition that is true only if all conditions are true.
125
-
126
- ```ts
127
- const isOwnerAndPublished = and(
128
- (ctx, action, resource) => ctx.user.id === resource.authorId,
129
- (ctx, action, resource) => resource.status === "published"
130
- );
131
- ```
132
-
133
- #### `or(...conditions)`
134
-
135
- Returns a condition that is true if any condition is true.
91
+ Via `and`, `or`, and `not`.
136
92
 
137
93
  ```ts
138
94
  const isOwnerOrAdmin = or(
@@ -141,130 +97,52 @@ const isOwnerOrAdmin = or(
141
97
  );
142
98
  ```
143
99
 
144
- #### `not(condition)`
145
-
146
- Returns a condition that negates another condition.
147
-
148
- ```ts
149
- const isNotOwner = not(
150
- (ctx, action, resource) => ctx.user.id === resource.authorId
151
- );
152
- ```
153
-
154
- ### Context Helpers
155
-
156
- #### `has(key, value)`
157
-
158
- Checks if a context property equals a specific value.
159
-
160
- ```ts
161
- rules: {
162
- post: {
163
- write: when(has("role", "admin")),
164
- },
165
- }
166
- ```
167
-
168
- #### `hasRole(role, hierarchy?)`
169
-
170
- Checks if the user has a specific role, with optional hierarchy support.
171
-
172
- ```ts
173
- const hierarchy = {
174
- guest: [],
175
- user: ["guest"],
176
- admin: ["user"],
177
- };
178
-
179
- rules: {
180
- post: {
181
- read: when(hasRole("guest", hierarchy)), // Admins and users inherit guest permissions
182
- },
183
- }
184
- ```
185
-
186
- ### Policy Merging
187
-
188
- #### `mergePolicies(...policies)`
100
+ ## Policy Merging Strategies
189
101
 
190
- Merges policies with "deny-overrides" strategy. All policies must allow for the action to be permitted.
102
+ Via `mergePoliciesAnd` and `mergePoliciesOr`.
191
103
 
192
104
  ```ts
193
- const merged = mergePolicies(basePolicy, restrictivePolicy);
105
+ const merged = mergePoliciesAnd(basePolicy, restrictivePolicy);
194
106
  ```
195
107
 
196
- #### `mergePoliciesAny(...policies)`
197
-
198
- Merges policies with "allow-overrides" strategy. Any policy allowing is sufficient.
199
-
200
- ```ts
201
- const merged = mergePoliciesAny(guestPolicy, memberPolicy);
202
- ```
203
-
204
- ## Type Helpers
205
-
206
- ### `Resources`
108
+ ## Standard Schema Support
207
109
 
208
- Type helper for defining resource schemas with `satisfies`.
110
+ Works with Zod, Valibot, ArkType, or any compatible library.
209
111
 
210
112
  ```ts
113
+ // Zod, Valibot, ArkType, or any Standard Schema-compatible library
211
114
  const resources = {
212
115
  post: z.object({ id: z.string() }),
213
116
  } satisfies Resources;
214
117
  ```
215
118
 
216
- ### `Actions<TResources>`
217
-
218
- Type helper for defining actions with `satisfies`. Ensures action keys match resource keys.
219
-
220
- ```ts
221
- const actions = {
222
- post: ["read", "write"],
223
- } as const satisfies Actions<typeof resources>;
224
- ```
225
-
226
- ### `InferResource<TResources, K>`
119
+ ## Structured Errors
227
120
 
228
- Infers the TypeScript type for a specific resource.
121
+ `PolicyError` for invalid configuration or evaluation failures.
229
122
 
230
123
  ```ts
231
- type Post = InferResource<typeof resources, "post">;
232
- // { id: string }
233
- ```
124
+ import { PolicyError } from "@zap-studio/permit";
234
125
 
235
- ### `InferAction<TActions, K>`
236
-
237
- Infers the action union type for a specific resource.
238
-
239
- ```ts
240
- type PostAction = InferAction<typeof actions, "post">;
241
- // "read" | "write"
126
+ try {
127
+ const policy = createPolicy(config);
128
+ await policy.can(ctx, "post:read", post);
129
+ } catch (error) {
130
+ if (error instanceof PolicyError) console.error(error.message);
131
+ }
242
132
  ```
243
133
 
244
- ## Standard Schema Support
245
-
246
- This library uses [Standard Schema](https://standardschema.dev/) for resource validation, which means it works with any compatible schema library:
134
+ ## Runtime Support
247
135
 
248
- - [Zod](https://zod.dev/)
249
- - [Valibot](https://valibot.dev/)
250
- - [ArkType](https://arktype.io/)
136
+ | Runtime | Minimum version |
137
+ | ------------------ | ------------------------------------------------ |
138
+ | Node.js | 18.0.0 |
139
+ | Bun | 1.0.0 |
140
+ | Deno | 1.42 |
141
+ | Cloudflare Workers | Any current release |
142
+ | Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
251
143
 
252
- ```ts
253
- // With Zod
254
- import { z } from "zod";
255
- const resources = {
256
- post: z.object({ id: z.string() }),
257
- } satisfies Resources;
144
+ The package ships standard ESM only and uses no runtime-specific APIs. Deno 1.42 is the first release that can install packages from JSR (`deno add jsr:@zap-studio/permit`).
258
145
 
259
- // With Valibot
260
- import * as v from "valibot";
261
- const resources = {
262
- post: v.object({ id: v.string() }),
263
- } satisfies Resources;
146
+ ## License
264
147
 
265
- // With ArkType
266
- import { type } from "arktype";
267
- const resources = {
268
- post: type({ id: "string" }),
269
- } satisfies Resources;
270
- ```
148
+ MIT
@@ -1,5 +1,5 @@
1
- import { Actions, ConditionFn, Context, PermitConfig, Policy, PolicyFn, Resources, Role, RoleHierarchy } from "./types.mjs";
2
- //#region src/index.d.ts
1
+ import { ConditionFn, Context, HasRoleFn, PolicyFn, Role, RoleHierarchy } from "./types.js";
2
+ //#region src/conditions.d.ts
3
3
  /**
4
4
  * Returns a policy function that always allows the action.
5
5
  *
@@ -133,17 +133,6 @@ declare const has: <TContext extends Context, K extends keyof TContext>(key: K,
133
133
  * ```
134
134
  */
135
135
  declare const collectInheritedRoles: <TRole extends Role = Role>(roles: TRole[], hierarchy: RoleHierarchy<TRole>) => Set<TRole>;
136
- /**
137
- * Call signatures for {@link hasRole}, preserving the with/without hierarchy overloads.
138
- */
139
- interface HasRoleFn {
140
- <TContext extends {
141
- role: Role | Role[];
142
- }, TAction extends string = string, TResource = unknown>(role: Role): ConditionFn<TContext, TAction, TResource>;
143
- <TContext extends {
144
- role: TRole | TRole[];
145
- }, TAction extends string = string, TResource = unknown, TRole extends Role = Role>(role: TRole, hierarchy: RoleHierarchy<TRole>): ConditionFn<TContext, TAction, TResource>;
146
- }
147
136
  /**
148
137
  * Returns a condition function that checks if the user has a specific role.
149
138
  * Supports role hierarchy for inherited permissions.
@@ -172,90 +161,6 @@ interface HasRoleFn {
172
161
  * ```
173
162
  */
174
163
  declare const hasRole: HasRoleFn;
175
- /**
176
- * Creates a type-safe policy from resource schemas, actions, and rules.
177
- *
178
- * @example
179
- * ```ts
180
- * import { z } from "zod";
181
- * import { createPolicy, allow, deny, when } from "@zap-studio/permit";
182
- * import type { Resources, Actions } from "@zap-studio/permit/types";
183
- *
184
- * // Define resource schemas
185
- * const resources = {
186
- * post: z.object({
187
- * id: z.string(),
188
- * authorId: z.string(),
189
- * visibility: z.enum(["public", "private"]),
190
- * }),
191
- * comment: z.object({
192
- * id: z.string(),
193
- * postId: z.string(),
194
- * authorId: z.string(),
195
- * }),
196
- * } satisfies Resources;
197
- *
198
- * // Define actions per resource
199
- * const actions = {
200
- * post: ["read", "write", "delete"],
201
- * comment: ["read", "write"],
202
- * } as const satisfies Actions<typeof resources>;
203
- *
204
- * // Define context type
205
- * type AppContext = { user: { id: string; role: string } };
206
- *
207
- * // Create the policy
208
- * const policy = createPolicy<AppContext>({
209
- * resources,
210
- * actions,
211
- * rules: {
212
- * post: {
213
- * read: when((ctx, action, resource) => resource.visibility === "public"),
214
- * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
215
- * delete: deny(),
216
- * },
217
- * comment: {
218
- * read: allow(),
219
- * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
220
- * },
221
- * },
222
- * });
223
- *
224
- * // Check permissions
225
- * const post = { id: "1", authorId: "user-1", visibility: "public" as const };
226
- * await policy.can(ctx, "post:read", post); // true
227
- * await policy.can(ctx, "post:write", post); // depends on ctx.user.id
228
- * ```
229
- */
230
- declare const createPolicy: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(config: PermitConfig<TContext, TResources, TActions>) => Policy<TContext, TResources, TActions>;
231
- /**
232
- * Merges multiple policies into one using "deny-overrides" strategy.
233
- * If any policy denies, the merged policy denies. All must allow for the result to allow.
234
- *
235
- * @example
236
- * ```ts
237
- * const basePolicy = createPolicy({ ... });
238
- * const adminPolicy = createPolicy({ ... });
239
- *
240
- * const merged = mergePolicies(basePolicy, adminPolicy);
241
- * // Both policies must allow for the action to be permitted
242
- * ```
243
- */
244
- declare const mergePolicies: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
245
- /**
246
- * Merges multiple policies into one using "allow-overrides" strategy.
247
- * If any policy allows, the merged policy allows. All must deny for the result to deny.
248
- *
249
- * @example
250
- * ```ts
251
- * const guestPolicy = createPolicy({ ... });
252
- * const memberPolicy = createPolicy({ ... });
253
- *
254
- * const merged = mergePoliciesAny(guestPolicy, memberPolicy);
255
- * // If either policy allows, the action is permitted
256
- * ```
257
- */
258
- declare const mergePoliciesAny: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
259
164
  //#endregion
260
- export { allow, and, collectInheritedRoles, createPolicy, deny, has, hasRole, mergePolicies, mergePoliciesAny, not, or, when };
261
- //# sourceMappingURL=index.d.mts.map
165
+ export { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when };
166
+ //# sourceMappingURL=conditions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditions.d.ts","names":[],"sources":["../src/conditions.ts"],"mappings":";;;;;;;;;;;;;;;;;;cAgCa,QAET,iBAAiB,SACjB,iCACA,0BACG,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;cAoBtB,OAET,iBAAiB,SACjB,iCACA,0BACG,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;cAoBtB,OAET,iBAAiB,SACjB,iCACA,qBAEA,WAAW,YAAY,UAAU,SAAS,eACzC,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;;cAqBpB,MAET,iBAAiB,SACjB,iCACA,wBAEG,YAAY,YAAY,UAAU,SAAS,iBAC7C,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;;;;cAqBvB,KAET,iBAAiB,SACjB,iCACA,wBAEG,YAAY,YAAY,UAAU,SAAS,iBAC7C,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;cAkBvB,MAET,iBAAiB,SACjB,iCACA,qBAEA,WAAW,YAAY,UAAU,SAAS,eACzC,YAAY,UAAU,SAAS;;;;;;;;;;;;;cAgBvB,MACV,iBAAiB,SAAS,gBAAgB,UACzC,KAAK,GACL,OAAO,SAAS,OACf,YAAY;;;;;;;;;;;;;;;;;;cAqBJ,wBAAyB,cAAc,OAAO,MACzD,OAAO,SACP,WAAW,cAAc,WACxB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgDM,SAAS"}