@zap-studio/permit 0.1.0 → 0.1.2

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,5 +1,19 @@
1
1
  # @zap-studio/permit
2
2
 
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 2de8183: Add a reusable synchronous Standard Schema validator helper in `@zap-studio/validation` and refactor `@zap-studio/permit` to use it for resource schema validation in `createPolicy`.
8
+ - Updated dependencies [2de8183]
9
+ - @zap-studio/validation@0.2.0
10
+
11
+ ## 0.1.1
12
+
13
+ ### Patch Changes
14
+
15
+ - 907d903: Add runtime resource validation and fail-closed behavior, including denying access when merges are called with no policies.
16
+
3
17
  ## 0.1.0
4
18
 
5
19
  ### Minor Changes
@@ -0,0 +1,15 @@
1
+ //#region src/errors.ts
2
+ /**
3
+ * Represents an error that occurs during policy evaluation or enforcement.
4
+ * Use this error to indicate issues related to policy logic, configuration, or execution.
5
+ */
6
+ var PolicyError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "PolicyError";
10
+ }
11
+ };
12
+
13
+ //#endregion
14
+ export { PolicyError as t };
15
+ //# sourceMappingURL=errors-CgD70cJR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-CgD70cJR.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Represents an error that occurs during policy evaluation or enforcement.\n * Use this error to indicate issues related to policy logic, configuration, or execution.\n */\nexport class PolicyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PolicyError\";\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,cAAb,cAAiC,MAAM;CACrC,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO"}
package/dist/errors.mjs CHANGED
@@ -1,15 +1,3 @@
1
- //#region src/errors.ts
2
- /**
3
- * Represents an error that occurs during policy evaluation or enforcement.
4
- * Use this error to indicate issues related to policy logic, configuration, or execution.
5
- */
6
- var PolicyError = class extends Error {
7
- constructor(message) {
8
- super(message);
9
- this.name = "PolicyError";
10
- }
11
- };
1
+ import { t as PolicyError } from "./errors-CgD70cJR.mjs";
12
2
 
13
- //#endregion
14
- export { PolicyError };
15
- //# sourceMappingURL=errors.mjs.map
3
+ export { PolicyError };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as PermitConfig, d as Resources, f as Role, i as Context, l as Policy, n as Actions, p as RoleHierarchy, r as ConditionFn, u as PolicyFn } from "./types-Bd4zClp0.mjs";
1
+ import { c as PermitConfig, d as Resources, f as Role, i as Context, l as Policy, n as Actions, p as RoleHierarchy, r as ConditionFn, u as PolicyFn } from "./types-BgG1Aq6K.mjs";
2
2
 
3
3
  //#region src/index.d.ts
4
4
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;AAiCA;;;;;;;AAwBA;;;;;;;AAwBgB,iBAhDA,KAgDA,CACG,iBAhDA,OAgDA,EAIM,gBAAA,MAAA,GAAA,MAAA,EAAU,YAAA,OAAA,CAAS,CAAA,CAAA,EAjDvC,QAiDuC,CAjD9B,QAiD8B,EAjDpB,OAiDoB,EAjDX,SAiDW,CAAA;;;;;;;AAuB5C;;;;;;;;;;AAMG,iBA1Da,IA0Db,CAsBH,iBA/EmB,OA+EH,EACG,gBAAA,MAAA,GAAA,MAAA,EAIU,YAAA,OAAA,CAAU,CAAA,CAAA,EAjFlC,QAiFkC,CAjFzB,QAiFyB,EAjFf,OAiFe,EAjFN,SAiFM,CAAA;;;;;;;;AAoBvC;;;;;;;;;AAMG,iBAvFa,IAuFb,CAAA,iBAtFgB,OAsFhB,EAgBH,gBAAgB,MAAA,GAAA,MAAA,EAAqB,YAAA,OAAA,CAAyB,CAAA,SAAA,EAlGjD,WAkGiD,CAlGrC,QAkGqC,EAlG3B,OAkG2B,EAlGlB,SAkGkB,CAAA,CAAA,EAjG3D,QAiG2D,CAjGlD,QAiGkD,EAjGxC,OAiGwC,EAjG/B,SAiG+B,CAAA;;;;;;;AAwB9D;;;;;;;;;AA6CA;;AACqC,iBAjJrB,GAiJqB,CAAO,iBAhJzB,OAgJyB,EAGpC,gBAAA,MAAA,GAAA,MAAA,EAAmB,YAAA,OAAA,CAAU,CAAA,GAAA,UAAA,EA/IpB,WA+IoB,CA/IR,QA+IQ,EA/IE,OA+IF,EA/IW,SA+IX,CAAA,EAAA,CAAA,EA9IlC,WA8IkC,CA9ItB,QA8IsB,EA9IZ,OA8IY,EA9IH,SA8IG,CAAA;;;;AAErC;;;;;;;;;;;;;;AAuFgB,iBAjNA,EAiNA,CACG,iBAjNA,OAiNA,EACE,gBAAA,MAAA,GAAA,MAAA,EAAY,YAAA,OAAA,CACN,CAAA,GAAA,UAAA,EA/MV,WA+MU,CA/ME,QA+MF,EA/MY,OA+MZ,EA/MqB,SA+MrB,CAAA,EAAA,CAAA,EA9MxB,WA8MwB,CA9MZ,QA8MY,EA9MF,OA8ME,EA9MO,SA8MP,CAAA;;;;;;;;;;;;;AAiG3B;;AAEqB,iBA9RL,GA8RK,CAAY,iBA7Rd,OA6Rc,EACN,gBAAA,MAAA,GAAA,MAAA,EAAR,YAAA,OAAA,CAA8B,CAAA,SAAA,EA1RpC,WA0RoC,CA1RxB,QA0RwB,EA1Rd,OA0Rc,EA1RL,SA0RK,CAAA,CAAA,EAzR9C,WAyR8C,CAzRlC,QAyRkC,EAzRxB,OAyRwB,EAzRf,SAyRe,CAAA;;;;;;;;;;;AAqCjD;;AAEqB,iBAhTL,GAgTK,CAAY,iBAhTI,OAgTJ,EACN,UAAA,MAjTmC,QAiTnC,CAAR,CAAA,GAAA,EAhTZ,CAgTY,EAAA,KAAA,EA/SV,QA+SU,CA/SD,CA+SC,CAAA,CAAA,EA9ShB,WA8SgB,CA9SJ,QA8SI,CAAA;;;;;;;;;;;;;;;;;;iBAzRH,oCAAoC,OAAO,aAClD,oBACI,cAAc,SACxB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0CS,yBACG;QAAkB,OAAO;+DAGpC,OAAO,YAAY,UAAU,SAAS;iBAE9B,yBACG;QAAkB,QAAQ;uEAG7B,OAAO,YAEf,kBACK,cAAc,SACxB,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+ElB,8BACG,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ,qBAEvC,aAAa,UAAU,YAAY,YAC1C,OAAO,UAAU,YAAY;;;;;;;;;;;;;;iBA8FhB,+BACG,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ,0BAElC,OAAO,UAAU,YAAY,cACzC,OAAO,UAAU,YAAY;;;;;;;;;;;;;;iBAkChB,kCACG,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ,0BAElC,OAAO,UAAU,YAAY,cACzC,OAAO,UAAU,YAAY"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ import { t as PolicyError } from "./errors-CgD70cJR.mjs";
2
+ import { createSyncStandardValidator } from "@zap-studio/validation";
3
+
1
4
  //#region src/index.ts
2
5
  /**
3
6
  * Returns a policy function that always allows the action.
@@ -219,11 +222,42 @@ function hasRole(role, hierarchy) {
219
222
  * ```
220
223
  */
221
224
  function createPolicy(config) {
222
- const { rules } = config;
225
+ const { rules, resources, actions } = config;
226
+ const validators = /* @__PURE__ */ new Map();
227
+ const getValidatedResource = (resourceType, resource) => {
228
+ const validator = validators.get(resourceType);
229
+ if (!validator) return null;
230
+ try {
231
+ const result = validator(resource);
232
+ if (result.issues) return null;
233
+ return result.value;
234
+ } catch (error) {
235
+ console.warn(`Resource validation failed for ${String(resourceType)}: ${String(error)}`);
236
+ return null;
237
+ }
238
+ };
239
+ for (const key of Object.keys(resources)) {
240
+ const schema = resources[key];
241
+ if (!schema) throw new PolicyError(`Missing schema for resource: ${String(key)}`);
242
+ const validator = createSyncStandardValidator(schema);
243
+ validators.set(key, (input) => validator(input));
244
+ }
223
245
  return { can(context, action, resourceType, resource) {
224
- const policyFn = rules[resourceType][action];
246
+ const allowedActions = actions[resourceType];
247
+ if (!allowedActions) return false;
248
+ if (!allowedActions.includes(action)) return false;
249
+ const validatedResource = getValidatedResource(resourceType, resource);
250
+ if (!validatedResource) return false;
251
+ const resourceRules = rules[resourceType];
252
+ if (!resourceRules) return false;
253
+ const policyFn = resourceRules[action];
225
254
  if (!policyFn) return false;
226
- return policyFn(context, action, resource) === "allow";
255
+ try {
256
+ return policyFn(context, action, validatedResource) === "allow";
257
+ } catch (error) {
258
+ console.warn(`Policy evaluation error for ${String(resourceType)}.${String(action)}: ${String(error)}`);
259
+ return false;
260
+ }
227
261
  } };
228
262
  }
229
263
  /**
@@ -241,6 +275,7 @@ function createPolicy(config) {
241
275
  */
242
276
  function mergePolicies(...policies) {
243
277
  return { can(context, action, resourceType, resource) {
278
+ if (!policies.length) return false;
244
279
  for (const policy of policies) if (!policy.can(context, action, resourceType, resource)) return false;
245
280
  return true;
246
281
  } };
@@ -260,6 +295,7 @@ function mergePolicies(...policies) {
260
295
  */
261
296
  function mergePoliciesAny(...policies) {
262
297
  return { can(context, action, resourceType, resource) {
298
+ if (!policies.length) return false;
263
299
  return policies.some((policy) => policy.can(context, action, resourceType, resource));
264
300
  } };
265
301
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\n Actions,\n ConditionFn,\n Context,\n InferAction,\n InferResource,\n PermitConfig,\n Policy,\n PolicyFn,\n Resources,\n Role,\n RoleHierarchy,\n} from \"./types\";\n\n/**\n * Returns a policy function that always allows the action.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * read: allow(), // Always allow reading posts\n * },\n * },\n * });\n * ```\n */\nexport function allow<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(): PolicyFn<TContext, TAction, TResource> {\n return () => \"allow\";\n}\n\n/**\n * Returns a policy function that always denies the action.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * delete: deny(), // Never allow deleting posts\n * },\n * },\n * });\n * ```\n */\nexport function deny<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(): PolicyFn<TContext, TAction, TResource> {\n return () => \"deny\";\n}\n\n/**\n * Returns a policy function that allows or denies based on a condition.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * },\n * },\n * });\n * ```\n */\nexport function when<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n condition: ConditionFn<TContext, TAction, TResource>\n): PolicyFn<TContext, TAction, TResource> {\n return (context, action, resource) =>\n condition(context, action, resource) ? \"allow\" : \"deny\";\n}\n\n/**\n * Returns a condition function that returns `true` if all conditions are met.\n *\n * @example\n * ```ts\n * const isOwnerAndPublished = and(\n * (ctx, action, resource) => ctx.user.id === resource.authorId,\n * (ctx, action, resource) => resource.status === \"published\"\n * );\n *\n * rules: {\n * post: {\n * delete: when(isOwnerAndPublished),\n * },\n * }\n * ```\n */\nexport function and<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n ...conditions: ConditionFn<TContext, TAction, TResource>[]\n): ConditionFn<TContext, TAction, TResource> {\n return (context, action, resource) =>\n conditions.every((condition) => condition(context, action, resource));\n}\n\n/**\n * Returns a condition function that returns `true` if any condition is met.\n *\n * @example\n * ```ts\n * const isOwnerOrAdmin = or(\n * (ctx, action, resource) => ctx.user.id === resource.authorId,\n * (ctx, action, resource) => ctx.user.role === \"admin\"\n * );\n *\n * rules: {\n * post: {\n * write: when(isOwnerOrAdmin),\n * },\n * }\n * ```\n */\nexport function or<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n ...conditions: ConditionFn<TContext, TAction, TResource>[]\n): ConditionFn<TContext, TAction, TResource> {\n return (context, action, resource) =>\n conditions.some((condition) => condition(context, action, resource));\n}\n\n/**\n * Returns a condition function that negates another condition.\n *\n * @example\n * ```ts\n * const isNotOwner = not((ctx, action, resource) => ctx.user.id === resource.authorId);\n *\n * rules: {\n * post: {\n * like: when(isNotOwner), // Can only like posts you don't own\n * },\n * }\n * ```\n */\nexport function not<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n condition: ConditionFn<TContext, TAction, TResource>\n): ConditionFn<TContext, TAction, TResource> {\n return (context, action, resource) => !condition(context, action, resource);\n}\n\n/**\n * Returns a condition function that checks if a context property equals a value.\n *\n * @example\n * ```ts\n * rules: {\n * post: {\n * write: when(has(\"role\", \"admin\")), // Only admins can write\n * },\n * }\n * ```\n */\nexport function has<TContext extends Context, K extends keyof TContext>(\n key: K,\n value: TContext[K]\n): ConditionFn<TContext> {\n return (context) => context[key] === value;\n}\n\n/**\n * Collects all roles including inherited ones from a role hierarchy.\n *\n * @example\n * ```ts\n * type Role = \"guest\" | \"user\" | \"admin\";\n *\n * const hierarchy: RoleHierarchy<Role> = {\n * guest: [],\n * user: [\"guest\"],\n * admin: [\"user\"],\n * };\n *\n * collectInheritedRoles([\"admin\"], hierarchy);\n * // Returns: Set { \"admin\", \"user\", \"guest\" }\n * ```\n */\nexport function collectInheritedRoles<TRole extends Role = Role>(\n roles: TRole[],\n hierarchy: RoleHierarchy<TRole>\n): Set<TRole> {\n const inherited = new Set<TRole>();\n\n function add(role: TRole) {\n if (!inherited.has(role)) {\n inherited.add(role);\n const parents = hierarchy[role] ?? [];\n parents.forEach(add); // recursively add parent roles\n }\n }\n\n roles.forEach(add);\n return inherited;\n}\n\n/**\n * Returns a condition function that checks if the user has a specific role.\n * Supports role hierarchy for inherited permissions.\n *\n * @example\n * ```ts\n * // Without hierarchy\n * rules: {\n * post: {\n * delete: when(hasRole(\"admin\")),\n * },\n * }\n *\n * // With hierarchy\n * const hierarchy = {\n * guest: [],\n * user: [\"guest\"],\n * admin: [\"user\"],\n * };\n *\n * rules: {\n * post: {\n * read: when(hasRole(\"guest\", hierarchy)), // Admins and users can also read\n * },\n * }\n * ```\n */\nexport function hasRole<\n TContext extends Context & { role: Role | Role[] },\n TAction extends string = string,\n TResource = unknown,\n>(role: Role): ConditionFn<TContext, TAction, TResource>;\n\nexport function hasRole<\n TContext extends Context & { role: TRole | TRole[] },\n TAction extends string = string,\n TResource = unknown,\n TRole extends Role = Role,\n>(\n role: TRole,\n hierarchy: RoleHierarchy<TRole>\n): ConditionFn<TContext, TAction, TResource>;\n\nexport function hasRole<\n TContext extends Context & { role: Role | Role[] },\n TAction extends string = string,\n TResource = unknown,\n>(\n role: Role,\n hierarchy?: RoleHierarchy<Role>\n): ConditionFn<TContext, TAction, TResource> {\n return (context) => {\n const userRoles = Array.isArray(context.role)\n ? context.role\n : [context.role];\n\n if (!hierarchy) {\n return userRoles.includes(role);\n }\n\n const inherited = collectInheritedRoles(userRoles, hierarchy);\n return inherited.has(role);\n };\n}\n\n/**\n * Creates a type-safe policy from resource schemas, actions, and rules.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { createPolicy, allow, deny, when } from \"@zap-studio/permit\";\n * import type { Resources, Actions } from \"@zap-studio/permit/types\";\n *\n * // Define resource schemas\n * const resources = {\n * post: z.object({\n * id: z.string(),\n * authorId: z.string(),\n * visibility: z.enum([\"public\", \"private\"]),\n * }),\n * comment: z.object({\n * id: z.string(),\n * postId: z.string(),\n * authorId: z.string(),\n * }),\n * } satisfies Resources;\n *\n * // Define actions per resource\n * const actions = {\n * post: [\"read\", \"write\", \"delete\"],\n * comment: [\"read\", \"write\"],\n * } as const satisfies Actions<typeof resources>;\n *\n * // Define context type\n * type AppContext = { user: { id: string; role: string } };\n *\n * // Create the policy\n * const policy = createPolicy<AppContext>({\n * resources,\n * actions,\n * rules: {\n * post: {\n * read: when((ctx, action, resource) => resource.visibility === \"public\"),\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * delete: deny(),\n * },\n * comment: {\n * read: allow(),\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * },\n * },\n * });\n *\n * // Check permissions\n * const post = { id: \"1\", authorId: \"user-1\", visibility: \"public\" as const };\n * policy.can(ctx, \"read\", \"post\", post); // true\n * policy.can(ctx, \"write\", \"post\", post); // depends on ctx.user.id\n * ```\n */\nexport function createPolicy<\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n config: PermitConfig<TContext, TResources, TActions>\n): Policy<TContext, TResources, TActions> {\n const { rules } = config;\n\n return {\n can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n action: InferAction<TActions, K>,\n resourceType: K,\n resource: InferResource<TResources, K>\n ): boolean {\n const resourceRules = rules[resourceType];\n const policyFn = resourceRules[action];\n\n if (!policyFn) {\n return false;\n }\n\n return policyFn(context, action, resource) === \"allow\";\n },\n };\n}\n\n/**\n * Merges multiple policies into one using \"deny-overrides\" strategy.\n * If any policy denies, the merged policy denies. All must allow for the result to allow.\n *\n * @example\n * ```ts\n * const basePolicy = createPolicy({ ... });\n * const adminPolicy = createPolicy({ ... });\n *\n * const merged = mergePolicies(basePolicy, adminPolicy);\n * // Both policies must allow for the action to be permitted\n * ```\n */\nexport function mergePolicies<\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n ...policies: Policy<TContext, TResources, TActions>[]\n): Policy<TContext, TResources, TActions> {\n return {\n can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n action: InferAction<TActions, K>,\n resourceType: K,\n resource: InferResource<TResources, K>\n ): boolean {\n for (const policy of policies) {\n if (!policy.can(context, action, resourceType, resource)) {\n return false;\n }\n }\n return true;\n },\n };\n}\n\n/**\n * Merges multiple policies into one using \"allow-overrides\" strategy.\n * If any policy allows, the merged policy allows. All must deny for the result to deny.\n *\n * @example\n * ```ts\n * const guestPolicy = createPolicy({ ... });\n * const memberPolicy = createPolicy({ ... });\n *\n * const merged = mergePoliciesAny(guestPolicy, memberPolicy);\n * // If either policy allows, the action is permitted\n * ```\n */\nexport function mergePoliciesAny<\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n ...policies: Policy<TContext, TResources, TActions>[]\n): Policy<TContext, TResources, TActions> {\n return {\n can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n action: InferAction<TActions, K>,\n resourceType: K,\n resource: InferResource<TResources, K>\n ): boolean {\n return policies.some((policy) =>\n policy.can(context, action, resourceType, resource)\n );\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,SAAgB,QAI4B;AAC1C,cAAa;;;;;;;;;;;;;;;;;;AAmBf,SAAgB,OAI4B;AAC1C,cAAa;;;;;;;;;;;;;;;;;;AAmBf,SAAgB,KAKd,WACwC;AACxC,SAAQ,SAAS,QAAQ,aACvB,UAAU,SAAS,QAAQ,SAAS,GAAG,UAAU;;;;;;;;;;;;;;;;;;;AAoBrD,SAAgB,IAKd,GAAG,YACwC;AAC3C,SAAQ,SAAS,QAAQ,aACvB,WAAW,OAAO,cAAc,UAAU,SAAS,QAAQ,SAAS,CAAC;;;;;;;;;;;;;;;;;;;AAoBzE,SAAgB,GAKd,GAAG,YACwC;AAC3C,SAAQ,SAAS,QAAQ,aACvB,WAAW,MAAM,cAAc,UAAU,SAAS,QAAQ,SAAS,CAAC;;;;;;;;;;;;;;;;AAiBxE,SAAgB,IAKd,WAC2C;AAC3C,SAAQ,SAAS,QAAQ,aAAa,CAAC,UAAU,SAAS,QAAQ,SAAS;;;;;;;;;;;;;;AAe7E,SAAgB,IACd,KACA,OACuB;AACvB,SAAQ,YAAY,QAAQ,SAAS;;;;;;;;;;;;;;;;;;;AAoBvC,SAAgB,sBACd,OACA,WACY;CACZ,MAAM,4BAAY,IAAI,KAAY;CAElC,SAAS,IAAI,MAAa;AACxB,MAAI,CAAC,UAAU,IAAI,KAAK,EAAE;AACxB,aAAU,IAAI,KAAK;AAEnB,IADgB,UAAU,SAAS,EAAE,EAC7B,QAAQ,IAAI;;;AAIxB,OAAM,QAAQ,IAAI;AAClB,QAAO;;AA8CT,SAAgB,QAKd,MACA,WAC2C;AAC3C,SAAQ,YAAY;EAClB,MAAM,YAAY,MAAM,QAAQ,QAAQ,KAAK,GACzC,QAAQ,OACR,CAAC,QAAQ,KAAK;AAElB,MAAI,CAAC,UACH,QAAO,UAAU,SAAS,KAAK;AAIjC,SADkB,sBAAsB,WAAW,UAAU,CAC5C,IAAI,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2D9B,SAAgB,aAKd,QACwC;CACxC,MAAM,EAAE,UAAU;AAElB,QAAO,EACL,IACE,SACA,QACA,cACA,UACS;EAET,MAAM,WADgB,MAAM,cACG;AAE/B,MAAI,CAAC,SACH,QAAO;AAGT,SAAO,SAAS,SAAS,QAAQ,SAAS,KAAK;IAElD;;;;;;;;;;;;;;;AAgBH,SAAgB,cAKd,GAAG,UACqC;AACxC,QAAO,EACL,IACE,SACA,QACA,cACA,UACS;AACT,OAAK,MAAM,UAAU,SACnB,KAAI,CAAC,OAAO,IAAI,SAAS,QAAQ,cAAc,SAAS,CACtD,QAAO;AAGX,SAAO;IAEV;;;;;;;;;;;;;;;AAgBH,SAAgB,iBAKd,GAAG,UACqC;AACxC,QAAO,EACL,IACE,SACA,QACA,cACA,UACS;AACT,SAAO,SAAS,MAAM,WACpB,OAAO,IAAI,SAAS,QAAQ,cAAc,SAAS,CACpD;IAEJ"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { createSyncStandardValidator } from \"@zap-studio/validation\";\nimport { PolicyError } from \"./errors\";\nimport type {\n Actions,\n ConditionFn,\n Context,\n InferAction,\n InferResource,\n PermitConfig,\n Policy,\n PolicyFn,\n Resources,\n Role,\n RoleHierarchy,\n} from \"./types\";\n\n/**\n * Returns a policy function that always allows the action.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * read: allow(), // Always allow reading posts\n * },\n * },\n * });\n * ```\n */\nexport function allow<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(): PolicyFn<TContext, TAction, TResource> {\n return () => \"allow\";\n}\n\n/**\n * Returns a policy function that always denies the action.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * delete: deny(), // Never allow deleting posts\n * },\n * },\n * });\n * ```\n */\nexport function deny<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(): PolicyFn<TContext, TAction, TResource> {\n return () => \"deny\";\n}\n\n/**\n * Returns a policy function that allows or denies based on a condition.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * },\n * },\n * });\n * ```\n */\nexport function when<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n condition: ConditionFn<TContext, TAction, TResource>\n): PolicyFn<TContext, TAction, TResource> {\n return (context, action, resource) =>\n condition(context, action, resource) ? \"allow\" : \"deny\";\n}\n\n/**\n * Returns a condition function that returns `true` if all conditions are met.\n *\n * @example\n * ```ts\n * const isOwnerAndPublished = and(\n * (ctx, action, resource) => ctx.user.id === resource.authorId,\n * (ctx, action, resource) => resource.status === \"published\"\n * );\n *\n * rules: {\n * post: {\n * delete: when(isOwnerAndPublished),\n * },\n * }\n * ```\n */\nexport function and<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n ...conditions: ConditionFn<TContext, TAction, TResource>[]\n): ConditionFn<TContext, TAction, TResource> {\n return (context, action, resource) =>\n conditions.every((condition) => condition(context, action, resource));\n}\n\n/**\n * Returns a condition function that returns `true` if any condition is met.\n *\n * @example\n * ```ts\n * const isOwnerOrAdmin = or(\n * (ctx, action, resource) => ctx.user.id === resource.authorId,\n * (ctx, action, resource) => ctx.user.role === \"admin\"\n * );\n *\n * rules: {\n * post: {\n * write: when(isOwnerOrAdmin),\n * },\n * }\n * ```\n */\nexport function or<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n ...conditions: ConditionFn<TContext, TAction, TResource>[]\n): ConditionFn<TContext, TAction, TResource> {\n return (context, action, resource) =>\n conditions.some((condition) => condition(context, action, resource));\n}\n\n/**\n * Returns a condition function that negates another condition.\n *\n * @example\n * ```ts\n * const isNotOwner = not((ctx, action, resource) => ctx.user.id === resource.authorId);\n *\n * rules: {\n * post: {\n * like: when(isNotOwner), // Can only like posts you don't own\n * },\n * }\n * ```\n */\nexport function not<\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n>(\n condition: ConditionFn<TContext, TAction, TResource>\n): ConditionFn<TContext, TAction, TResource> {\n return (context, action, resource) => !condition(context, action, resource);\n}\n\n/**\n * Returns a condition function that checks if a context property equals a value.\n *\n * @example\n * ```ts\n * rules: {\n * post: {\n * write: when(has(\"role\", \"admin\")), // Only admins can write\n * },\n * }\n * ```\n */\nexport function has<TContext extends Context, K extends keyof TContext>(\n key: K,\n value: TContext[K]\n): ConditionFn<TContext> {\n return (context) => context[key] === value;\n}\n\n/**\n * Collects all roles including inherited ones from a role hierarchy.\n *\n * @example\n * ```ts\n * type Role = \"guest\" | \"user\" | \"admin\";\n *\n * const hierarchy: RoleHierarchy<Role> = {\n * guest: [],\n * user: [\"guest\"],\n * admin: [\"user\"],\n * };\n *\n * collectInheritedRoles([\"admin\"], hierarchy);\n * // Returns: Set { \"admin\", \"user\", \"guest\" }\n * ```\n */\nexport function collectInheritedRoles<TRole extends Role = Role>(\n roles: TRole[],\n hierarchy: RoleHierarchy<TRole>\n): Set<TRole> {\n const inherited = new Set<TRole>();\n\n function add(role: TRole) {\n if (!inherited.has(role)) {\n inherited.add(role);\n const parents = hierarchy[role] ?? [];\n parents.forEach(add); // recursively add parent roles\n }\n }\n\n roles.forEach(add);\n return inherited;\n}\n\n/**\n * Returns a condition function that checks if the user has a specific role.\n * Supports role hierarchy for inherited permissions.\n *\n * @example\n * ```ts\n * // Without hierarchy\n * rules: {\n * post: {\n * delete: when(hasRole(\"admin\")),\n * },\n * }\n *\n * // With hierarchy\n * const hierarchy = {\n * guest: [],\n * user: [\"guest\"],\n * admin: [\"user\"],\n * };\n *\n * rules: {\n * post: {\n * read: when(hasRole(\"guest\", hierarchy)), // Admins and users can also read\n * },\n * }\n * ```\n */\nexport function hasRole<\n TContext extends Context & { role: Role | Role[] },\n TAction extends string = string,\n TResource = unknown,\n>(role: Role): ConditionFn<TContext, TAction, TResource>;\n\nexport function hasRole<\n TContext extends Context & { role: TRole | TRole[] },\n TAction extends string = string,\n TResource = unknown,\n TRole extends Role = Role,\n>(\n role: TRole,\n hierarchy: RoleHierarchy<TRole>\n): ConditionFn<TContext, TAction, TResource>;\n\nexport function hasRole<\n TContext extends Context & { role: Role | Role[] },\n TAction extends string = string,\n TResource = unknown,\n>(\n role: Role,\n hierarchy?: RoleHierarchy<Role>\n): ConditionFn<TContext, TAction, TResource> {\n return (context) => {\n const userRoles = Array.isArray(context.role)\n ? context.role\n : [context.role];\n\n if (!hierarchy) {\n return userRoles.includes(role);\n }\n\n const inherited = collectInheritedRoles(userRoles, hierarchy);\n return inherited.has(role);\n };\n}\n\n/**\n * Creates a type-safe policy from resource schemas, actions, and rules.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { createPolicy, allow, deny, when } from \"@zap-studio/permit\";\n * import type { Resources, Actions } from \"@zap-studio/permit/types\";\n *\n * // Define resource schemas\n * const resources = {\n * post: z.object({\n * id: z.string(),\n * authorId: z.string(),\n * visibility: z.enum([\"public\", \"private\"]),\n * }),\n * comment: z.object({\n * id: z.string(),\n * postId: z.string(),\n * authorId: z.string(),\n * }),\n * } satisfies Resources;\n *\n * // Define actions per resource\n * const actions = {\n * post: [\"read\", \"write\", \"delete\"],\n * comment: [\"read\", \"write\"],\n * } as const satisfies Actions<typeof resources>;\n *\n * // Define context type\n * type AppContext = { user: { id: string; role: string } };\n *\n * // Create the policy\n * const policy = createPolicy<AppContext>({\n * resources,\n * actions,\n * rules: {\n * post: {\n * read: when((ctx, action, resource) => resource.visibility === \"public\"),\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * delete: deny(),\n * },\n * comment: {\n * read: allow(),\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * },\n * },\n * });\n *\n * // Check permissions\n * const post = { id: \"1\", authorId: \"user-1\", visibility: \"public\" as const };\n * policy.can(ctx, \"read\", \"post\", post); // true\n * policy.can(ctx, \"write\", \"post\", post); // depends on ctx.user.id\n * ```\n */\nexport function createPolicy<\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n config: PermitConfig<TContext, TResources, TActions>\n): Policy<TContext, TResources, TActions> {\n const { rules, resources, actions } = config;\n const validators = new Map<\n keyof TResources,\n (input: unknown) => StandardSchemaV1.Result<unknown>\n >();\n\n const getValidatedResource = <K extends keyof TResources>(\n resourceType: K,\n resource: InferResource<TResources, K>\n ): InferResource<TResources, K> | null => {\n const validator = validators.get(resourceType);\n if (!validator) {\n return null;\n }\n try {\n const result = validator(resource);\n if (result.issues) {\n return null;\n }\n return result.value as InferResource<TResources, K>;\n } catch (error) {\n console.warn(\n `Resource validation failed for ${String(resourceType)}: ${String(error)}`\n );\n return null;\n }\n };\n\n for (const key of Object.keys(resources) as Array<keyof TResources>) {\n const schema = resources[key];\n if (!schema) {\n throw new PolicyError(`Missing schema for resource: ${String(key)}`);\n }\n const validator = createSyncStandardValidator(schema);\n validators.set(key, (input: unknown) => validator(input));\n }\n\n return {\n can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n action: InferAction<TActions, K>,\n resourceType: K,\n resource: InferResource<TResources, K>\n ): boolean {\n const allowedActions = actions[resourceType];\n if (!allowedActions) {\n return false;\n }\n if (!allowedActions.includes(action)) {\n return false;\n }\n\n const validatedResource = getValidatedResource(resourceType, resource);\n if (!validatedResource) {\n return false;\n }\n\n const resourceRules = rules[resourceType];\n if (!resourceRules) {\n return false;\n }\n\n const policyFn = resourceRules[action];\n\n if (!policyFn) {\n return false;\n }\n\n try {\n return policyFn(context, action, validatedResource) === \"allow\";\n } catch (error) {\n console.warn(\n `Policy evaluation error for ${String(resourceType)}.${String(action)}: ${String(error)}`\n );\n return false;\n }\n },\n };\n}\n\n/**\n * Merges multiple policies into one using \"deny-overrides\" strategy.\n * If any policy denies, the merged policy denies. All must allow for the result to allow.\n *\n * @example\n * ```ts\n * const basePolicy = createPolicy({ ... });\n * const adminPolicy = createPolicy({ ... });\n *\n * const merged = mergePolicies(basePolicy, adminPolicy);\n * // Both policies must allow for the action to be permitted\n * ```\n */\nexport function mergePolicies<\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n ...policies: Policy<TContext, TResources, TActions>[]\n): Policy<TContext, TResources, TActions> {\n return {\n can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n action: InferAction<TActions, K>,\n resourceType: K,\n resource: InferResource<TResources, K>\n ): boolean {\n if (!policies.length) {\n return false;\n }\n for (const policy of policies) {\n if (!policy.can(context, action, resourceType, resource)) {\n return false;\n }\n }\n return true;\n },\n };\n}\n\n/**\n * Merges multiple policies into one using \"allow-overrides\" strategy.\n * If any policy allows, the merged policy allows. All must deny for the result to deny.\n *\n * @example\n * ```ts\n * const guestPolicy = createPolicy({ ... });\n * const memberPolicy = createPolicy({ ... });\n *\n * const merged = mergePoliciesAny(guestPolicy, memberPolicy);\n * // If either policy allows, the action is permitted\n * ```\n */\nexport function mergePoliciesAny<\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n ...policies: Policy<TContext, TResources, TActions>[]\n): Policy<TContext, TResources, TActions> {\n return {\n can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n action: InferAction<TActions, K>,\n resourceType: K,\n resource: InferResource<TResources, K>\n ): boolean {\n if (!policies.length) {\n return false;\n }\n return policies.some((policy) =>\n policy.can(context, action, resourceType, resource)\n );\n },\n };\n}\n"],"mappings":"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"}
@@ -131,4 +131,4 @@ type Policy<TContext extends Context, TResources extends Resources = Resources,
131
131
  };
132
132
  //#endregion
133
133
  export { Decision as a, PermitConfig as c, Resources as d, Role as f, Context as i, Policy as l, Rules as m, Actions as n, InferAction as o, RoleHierarchy as p, ConditionFn as r, InferResource as s, ActionPolicyMap as t, PolicyFn as u };
134
- //# sourceMappingURL=types-Bd4zClp0.d.mts.map
134
+ //# sourceMappingURL=types-BgG1Aq6K.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-Bd4zClp0.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;AAOA;AAMA;AAKA;AAiBY,KA5BA,QAAA,GA4BA,OAAA,GAAA,MAAA;;;;;AAA2C,KAtB3C,OAsB2C,CAAA,WAAA,OAAA,CAAA,GAtBb,QAsBa;;AAiBvD;;AAEE,KApCU,IAoCV,CAAA,cAAA,MAAA,GAAA,MAAA,CAAA,GApCgD,KAoChD;;;AAiBF;AAOA;;;;;;;AAQA;;;;;AAGa,KAtDD,aAsDC,CAAA,cAtD2B,IAsD3B,GAtDkC,IAsDlC,CAAA,GAtD0C,MAsD1C,CAtDiD,KAsDjD,EAtDwD,KAsDxD,EAAA,CAAA;AAKb;;;;;;;AASA;;;;;;AASA;;AAKQ,KAjEI,SAiEJ,CAAA,qBAAA,MAAA,GAAA,MAAA,CAAA,GAjEsD,MAiEtD,CAhEN,YAgEM,EA/DN,gBA+DM,CAAA;;;;;;AAOR;;;;;;;;;AAKiC,KA1DrB,OA0DqB,CAAA,mBA1DM,SA0DN,CAAA,GAAA,QAAA,MAzDnB,UAyDmB,GAAA,SAAA,MAAA,EAAA,EAAA;;;;AAE7B,KArDQ,aAqDR,CACc,mBArDG,SAqDH,EAAY,qBAAA,MApDD,UAoDC,CAA1B,GAnDA,gBAAA,CAAiB,WAmDjB,CAnD6B,UAmD7B,CAnDwC,YAmDxC,CAAA,CAAA;;;AAkBJ;AACmB,KAjEP,WAiEO,CACE,iBAjEF,MAiEE,CAAA,MAAA,EAAA,SAAA,MAAA,EAAA,CAAA,EAAY,YAAA,MAhEf,QAgEe,CACN,GAhEvB,QAgEuB,CAhEd,GAgEc,CAAA,CAAA,MAAA,CAAA;;;;AAEd,KA7DD,QA6DC,CACF,iBA7DQ,OA6DR,EACI,gBAAA,MAAA,GAAA,MAAA,EAAU,YAAA,OAAA,CAAY,GAAA,CAAA,OAAA,EA3DvB,QA2DuB,EAAA,MAAA,EA3DL,OA2DK,EAAA,QAAA,EA3Dc,SA2Dd,EAAA,GA3D4B,QA2D5B;;;AAkBrC;AACmB,KAzEP,WAyEO,CACE,iBAzEF,OAyEE,EAAY,gBAAA,MAAA,GAAA,MAAA,EACN,YAAA,OAAA,CAAR,GAAA,CAAA,OAAA,EAvEL,QAuEK,EAAA,MAAA,EAvEa,OAuEb,EAAA,QAAA,EAvEgC,SAuEhC,EAAA,GAAA,OAAA;;;;AAKsB,KAvE7B,eAuE6B,CAC5B,iBAvEM,OAuEN,EACW,gBAAA,MAAA,GAAA,MAAA,EAAU,YAAA,OAAA,CAAtB,GAAA,QApEJ,OAoEI,IApEO,QAoEP,CApEgB,QAoEhB,EApE0B,CAoE1B,EApE6B,SAoE7B,CAAA,EAAA;;;;;AAEE,KA/DF,KA+DE,kBA9DK,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ,6BAEnC,mBAAmB,WAAW,gBACxC,UACA,YAAY,UAAU,IACtB,cAAc,YAAY;;;;;;;;;;;;;;;KAkBlB,8BACO,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ;aAEpC;WACF;SACF,MAAM,UAAU,YAAY;;;;;;;;;;;;;;;;;KAkBzB,wBACO,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ;;;;wBAK3B,mBAAmB,mBAC5B,kBACD,YAAY,UAAU,oBAChB,eACJ,cAAc,YAAY"}
1
+ {"version":3,"file":"types-BgG1Aq6K.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;AAOA;AAMA;AAKA;AAiBY,KA5BA,QAAA,GA4BA,OAAA,GAAA,MAAA;;;;;AAA2C,KAtB3C,OAsB2C,CAAA,WAAA,OAAA,CAAA,GAtBb,QAsBa;;AAiBvD;;AAEE,KApCU,IAoCV,CAAA,cAAA,MAAA,GAAA,MAAA,CAAA,GApCgD,KAoChD;;;AAiBF;AAOA;;;;;;;AAQA;;;;;AAGa,KAtDD,aAsDC,CAAA,cAtD2B,IAsD3B,GAtDkC,IAsDlC,CAAA,GAtD0C,MAsD1C,CAtDiD,KAsDjD,EAtDwD,KAsDxD,EAAA,CAAA;AAKb;;;;;;;AASA;;;;;;AASA;;AAKQ,KAjEI,SAiEJ,CAAA,qBAAA,MAAA,GAAA,MAAA,CAAA,GAjEsD,MAiEtD,CAhEN,YAgEM,EA/DN,gBA+DM,CAAA;;;;;;AAOR;;;;;;;;;AAKiC,KA1DrB,OA0DqB,CAAA,mBA1DM,SA0DN,CAAA,GAAA,QAAA,MAzDnB,UAyDmB,GAAA,SAAA,MAAA,EAAA,EAAA;;;;AAE7B,KArDQ,aAqDR,CACc,mBArDG,SAqDH,EAAY,qBAAA,MApDD,UAoDC,CAA1B,GAnDA,gBAAA,CAAiB,WAmDjB,CAnD6B,UAmD7B,CAnDwC,YAmDxC,CAAA,CAAA;;;AAkBJ;AACmB,KAjEP,WAiEO,CACE,iBAjEF,MAiEE,CAAA,MAAA,EAAA,SAAA,MAAA,EAAA,CAAA,EAAY,YAAA,MAhEf,QAgEe,CACN,GAhEvB,QAgEuB,CAhEd,GAgEc,CAAA,CAAA,MAAA,CAAA;;;;AAEd,KA7DD,QA6DC,CACF,iBA7DQ,OA6DR,EACI,gBAAA,MAAA,GAAA,MAAA,EAAU,YAAA,OAAA,CAAY,GAAA,CAAA,OAAA,EA3DvB,QA2DuB,EAAA,MAAA,EA3DL,OA2DK,EAAA,QAAA,EA3Dc,SA2Dd,EAAA,GA3D4B,QA2D5B;;;AAkBrC;AACmB,KAzEP,WAyEO,CACE,iBAzEF,OAyEE,EAAY,gBAAA,MAAA,GAAA,MAAA,EACN,YAAA,OAAA,CAAR,GAAA,CAAA,OAAA,EAvEL,QAuEK,EAAA,MAAA,EAvEa,OAuEb,EAAA,QAAA,EAvEgC,SAuEhC,EAAA,GAAA,OAAA;;;;AAKsB,KAvE7B,eAuE6B,CAC5B,iBAvEM,OAuEN,EACW,gBAAA,MAAA,GAAA,MAAA,EAAU,YAAA,OAAA,CAAtB,GAAA,QApEJ,OAoEI,IApEO,QAoEP,CApEgB,QAoEhB,EApE0B,CAoE1B,EApE6B,SAoE7B,CAAA,EAAA;;;;;AAEE,KA/DF,KA+DE,kBA9DK,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ,6BAEnC,mBAAmB,WAAW,gBACxC,UACA,YAAY,UAAU,IACtB,cAAc,YAAY;;;;;;;;;;;;;;;KAkBlB,8BACO,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ;aAEpC;WACF;SACF,MAAM,UAAU,YAAY;;;;;;;;;;;;;;;;;KAkBzB,wBACO,4BACE,YAAY,4BACd,QAAQ,cAAc,QAAQ;;;;wBAK3B,mBAAmB,mBAC5B,kBACD,YAAY,UAAU,oBAChB,eACJ,cAAc,YAAY"}
package/dist/types.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as Decision, c as PermitConfig, d as Resources, f as Role, i as Context, l as Policy, m as Rules, n as Actions, o as InferAction, p as RoleHierarchy, r as ConditionFn, s as InferResource, t as ActionPolicyMap, u as PolicyFn } from "./types-Bd4zClp0.mjs";
1
+ import { a as Decision, c as PermitConfig, d as Resources, f as Role, i as Context, l as Policy, m as Rules, n as Actions, o as InferAction, p as RoleHierarchy, r as ConditionFn, s as InferResource, t as ActionPolicyMap, u as PolicyFn } from "./types-BgG1Aq6K.mjs";
2
2
  export { ActionPolicyMap, Actions, ConditionFn, Context, Decision, InferAction, InferResource, PermitConfig, Policy, PolicyFn, Resources, Role, RoleHierarchy, Rules };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-studio/permit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -34,7 +34,8 @@
34
34
  "README.md"
35
35
  ],
36
36
  "dependencies": {
37
- "@standard-schema/spec": "^1.0.0"
37
+ "@standard-schema/spec": "^1.1.0",
38
+ "@zap-studio/validation": "0.2.0"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/node": "^25.0.2",
@@ -42,9 +43,9 @@
42
43
  "tsdown": "^0.18.0",
43
44
  "typescript": "^5.9.3",
44
45
  "vitest": "^4.0.15",
46
+ "@zap-studio/vitest-config": "0.0.0",
45
47
  "@zap-studio/tsdown-config": "0.0.0",
46
- "@zap-studio/typescript-config": "0.0.0",
47
- "@zap-studio/vitest-config": "0.0.0"
48
+ "@zap-studio/typescript-config": "0.0.0"
48
49
  },
49
50
  "exports": {
50
51
  ".": "./dist/index.mjs",
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Represents an error that occurs during policy evaluation or enforcement.\n * Use this error to indicate issues related to policy logic, configuration, or execution.\n */\nexport class PolicyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PolicyError\";\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,cAAb,cAAiC,MAAM;CACrC,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO"}