@classytic/arc 2.28.0 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/{BaseController-B65zbz9m.mjs → BaseController-B5DwufbB.mjs} +12 -10
  2. package/dist/EventTransport-C6YA8xY9.mjs +103 -0
  3. package/dist/{HookSystem-C1vB_Gc9.mjs → HookSystem-WkIougo6.mjs} +15 -35
  4. package/dist/applyPermissionResult-Cf6zUCvb.mjs +257 -0
  5. package/dist/audit/index.d.mts +2 -2
  6. package/dist/audit/index.mjs +1 -1
  7. package/dist/auth/index.d.mts +2 -2
  8. package/dist/auth/index.mjs +1 -1
  9. package/dist/{buildHandler-Dtwd8fdz.mjs → buildHandler-BYS3Vkfz.mjs} +32 -6
  10. package/dist/cleanup/index.d.mts +2 -2
  11. package/dist/cleanup/index.mjs +1 -1
  12. package/dist/cli/commands/describe.d.mts +1 -1
  13. package/dist/cli/commands/docs.mjs +1 -1
  14. package/dist/core/index.d.mts +2 -2
  15. package/dist/core/index.mjs +5 -4
  16. package/dist/{core-BGa8TXJR.mjs → core-poalJLvp.mjs} +2 -48
  17. package/dist/{createActionRouter-C-N7z2EL.mjs → createActionRouter-BxuywVRB.mjs} +2 -2
  18. package/dist/{createAggregationRouter-wzwFa_dW.mjs → createAggregationRouter-BiY-lGdY.mjs} +7 -20
  19. package/dist/{createApp-Du5G54TM.mjs → createApp-CXrKFciH.mjs} +24 -3
  20. package/dist/defineAggregation-ufWjUwml.mjs +47 -0
  21. package/dist/{defineResource-2zvVVpxg.mjs → defineResource-CxWqDCAy.mjs} +35 -42
  22. package/dist/docs/index.d.mts +1 -1
  23. package/dist/docs/index.mjs +1 -1
  24. package/dist/{eventPlugin-DrQ_FesK.mjs → eventPlugin-D2NNR4py.mjs} +2 -103
  25. package/dist/events/index.mjs +4 -339
  26. package/dist/factory/index.d.mts +4 -4
  27. package/dist/factory/index.mjs +1 -1
  28. package/dist/{fastify-augmentation-OzYGIiU1.d.mts → fastify-augmentation-0WhPnUH7.d.mts} +3 -3
  29. package/dist/{applyPermissionResult-BnyVE9eD.mjs → fields-CQ_7KQWw.mjs} +1 -107
  30. package/dist/hooks/index.d.mts +1 -1
  31. package/dist/hooks/index.mjs +1 -1
  32. package/dist/idempotency/index.mjs +1 -1
  33. package/dist/{index-CZY4EGga.d.mts → index-CCzVR-N3.d.mts} +1 -1
  34. package/dist/{index-BMnbzvtO.d.mts → index-Ch16aAah.d.mts} +201 -21
  35. package/dist/{index-CjVg-Epg.d.mts → index-Cx7bro4R.d.mts} +4 -4
  36. package/dist/{index-DPRH0Cme.d.mts → index-Q5gzEIWY.d.mts} +97 -66
  37. package/dist/index.d.mts +6 -6
  38. package/dist/index.mjs +8 -7
  39. package/dist/integrations/index.d.mts +1 -1
  40. package/dist/integrations/mcp/index.d.mts +6 -6
  41. package/dist/integrations/mcp/index.mjs +1 -1
  42. package/dist/integrations/mcp/testing.d.mts +1 -1
  43. package/dist/integrations/mcp/testing.mjs +1 -1
  44. package/dist/{mcpPlugin-Cs6nRGCt.mjs → mcpPlugin-nRzm5g2s.mjs} +36 -43
  45. package/dist/middleware/index.d.mts +1 -1
  46. package/dist/migrations/index.d.mts +0 -3
  47. package/dist/migrations/index.mjs +0 -3
  48. package/dist/{openapi-BLV4OMXb.mjs → openapi-B8Vkbxbn.mjs} +1 -1
  49. package/dist/permissions/index.d.mts +3 -3
  50. package/dist/permissions/index.mjs +4 -3
  51. package/dist/{permissions-BrtiBKGs.mjs → permissions-Bfu_8y1U.mjs} +318 -289
  52. package/dist/pipeline/index.d.mts +1 -1
  53. package/dist/{pipeline-BGRnV-To.mjs → pipeline-BJnpkY9i.mjs} +4 -18
  54. package/dist/plugins/index.d.mts +4 -4
  55. package/dist/plugins/index.mjs +2 -2
  56. package/dist/plugins/tracing-entry.mjs +1 -1
  57. package/dist/{preloadResources-CM4uUaFv.d.mts → preloadResources-DCfa2egz.d.mts} +3 -3
  58. package/dist/presets/filesUpload.d.mts +2 -2
  59. package/dist/presets/filesUpload.mjs +1 -1
  60. package/dist/presets/index.d.mts +14 -2
  61. package/dist/presets/index.mjs +1 -1
  62. package/dist/presets/multiTenant.d.mts +1 -1
  63. package/dist/presets/search.d.mts +2 -2
  64. package/dist/presets/search.mjs +1 -1
  65. package/dist/{presets-DwFDVKP0.mjs → presets-CJOEXLuk.mjs} +25 -9
  66. package/dist/projection-UBDgfMwu.mjs +17 -0
  67. package/dist/{realtime-DEuDu9io.mjs → realtime-B9nmIY5V.mjs} +2 -1
  68. package/dist/{registerRoute-DTp18Ah_.mjs → registerRoute-JkxIEBJq.mjs} +3 -1
  69. package/dist/registry/index.d.mts +2 -2
  70. package/dist/registry/index.mjs +1 -1
  71. package/dist/{registry-9km_w1aP.mjs → registry-BYXUUg21.mjs} +1 -1
  72. package/dist/repositoryFilter-Dz8k3S4P.mjs +44 -0
  73. package/dist/subscribe-helpers-DfzsivM6.mjs +339 -0
  74. package/dist/testing/index.d.mts +34 -6
  75. package/dist/testing/index.mjs +314 -211
  76. package/dist/types/index.d.mts +3 -3
  77. package/dist/{types-QFGc3Cjv.d.mts → types-Bmmn-fO0.d.mts} +1 -1
  78. package/dist/{types-Dmw-6Qeo.d.mts → types-ChvvVNP8.d.mts} +93 -88
  79. package/dist/{types-Cu6vVJmh.d.mts → types-D_n8L6VY.d.mts} +32 -1
  80. package/dist/usage/index.d.mts +1 -1
  81. package/dist/usage/index.mjs +0 -0
  82. package/dist/utils/index.d.mts +2 -5
  83. package/package.json +539 -539
@@ -1,10 +1,68 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
2
+ import { arcLog } from "./logger/index.mjs";
2
3
  import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
3
- import { C as isService, S as isOrgInScope, a as getDPoPJkt, b as isElevated, d as getScopeContext, f as getScopeContextMap, g as getUserId, m as getTeamId, o as getMandate, p as getServiceScopes, u as getRequestScope, v as hasOrgAccess, x as isMember } from "./types-CSyCxl2V.mjs";
4
+ import { C as isService, S as isOrgInScope, a as getDPoPJkt, b as isElevated, d as getScopeContext, f as getScopeContextMap, g as getUserId, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, u as getRequestScope, v as hasOrgAccess, x as isMember } from "./types-CSyCxl2V.mjs";
5
+ import { a as normalizeToDecision, o as conjoinPolicyFilters } from "./applyPermissionResult-Cf6zUCvb.mjs";
4
6
  import { t as MemoryCacheStore } from "./memory-CmnzXE5Z.mjs";
5
7
  import { randomUUID } from "node:crypto";
8
+ //#region src/permissions/context.ts
9
+ /**
10
+ * PermissionContext accessors (arc 2.30 — transport-neutral evaluation).
11
+ *
12
+ * The goal of the standardized authorization core is that a `PermissionCheck`
13
+ * reads FACTS off its {@link PermissionContext}, never off a raw
14
+ * `FastifyRequest`. That makes evaluation:
15
+ * - pure (a combinator can hand a child a NEW context with an updated scope
16
+ * instead of mutating `request.scope` — see `algebra.ts`),
17
+ * - transport-neutral (MCP / jobs / websockets build a context directly, no
18
+ * `fakeRequest`),
19
+ * - unit-testable (no Fastify object required).
20
+ *
21
+ * `scopeOf` is the single seam every primitive uses to read the request scope.
22
+ * It prefers the context's first-class `scope` (set by combinators + adapters)
23
+ * and falls back to the raw request during migration (the `ctx.request` shim
24
+ * removed in the final phase). Once `scope` is populated everywhere, the
25
+ * fallback is dead and drops out.
26
+ */
27
+ /**
28
+ * Resolve the request scope for a permission check. Prefers the context's
29
+ * first-class `scope`; falls back to the raw HTTP `request.scope` when present,
30
+ * else `PUBLIC_SCOPE`. Primitives MUST use this instead of touching
31
+ * `ctx.request` so scope threads through combinators and non-HTTP transports
32
+ * (MCP, jobs) — which have no request — resolve identity correctly.
33
+ */
34
+ function scopeOf(ctx) {
35
+ if (ctx.scope) return ctx.scope;
36
+ return ctx.request ? getRequestScope(ctx.request) : PUBLIC_SCOPE;
37
+ }
38
+ //#endregion
6
39
  //#region src/permissions/core.ts
7
40
  /**
41
+ * Build an `allow` {@link AuthorizationDecision}, optionally attaching a row-level
42
+ * `policy` and/or a `scope` to install. The idiomatic "grant" return for arc
43
+ * 2.30 checks (parallels Cedar's `permit`).
44
+ *
45
+ * @example
46
+ * return allow(); // simple grant
47
+ * return allow({ policy: { ownerId: userId } }); // grant + row policy
48
+ */
49
+ function allow(extra) {
50
+ return extra ? {
51
+ effect: "allow",
52
+ ...extra
53
+ } : { effect: "allow" };
54
+ }
55
+ /**
56
+ * Build a `deny` {@link AuthorizationDecision} with an optional human-readable
57
+ * reason (parallels Cedar's `forbid`). The idiomatic "refuse" return.
58
+ */
59
+ function deny(reason) {
60
+ return reason ? {
61
+ effect: "deny",
62
+ reason
63
+ } : { effect: "deny" };
64
+ }
65
+ /**
8
66
  * Normalize a `string | [readonly string[]]` rest-args tuple into a single
9
67
  * `readonly string[]`. Lets a permission helper accept BOTH variadic and
10
68
  * array call shapes from one overload signature.
@@ -45,10 +103,7 @@ function allowPublic() {
45
103
  */
46
104
  function requireAuth() {
47
105
  const check = (ctx) => {
48
- if (!ctx.user) return {
49
- granted: false,
50
- reason: "Authentication required"
51
- };
106
+ if (!ctx.user) return deny("Authentication required");
52
107
  return true;
53
108
  };
54
109
  return check;
@@ -69,22 +124,16 @@ function requireRoles(rolesOrFirst, optionsOrSecond, ...rest) {
69
124
  }
70
125
  const includeOrgRoles = options?.includeOrgRoles ?? true;
71
126
  const check = (ctx) => {
72
- if (!ctx.user) return {
73
- granted: false,
74
- reason: "Authentication required"
75
- };
127
+ if (!ctx.user) return deny("Authentication required");
76
128
  const userRoles = getUserRoles(ctx.user);
77
129
  if (options?.bypassRoles?.some((r) => userRoles.includes(r))) return true;
78
130
  if (roles.some((r) => userRoles.includes(r))) return true;
79
131
  if (includeOrgRoles) {
80
- const scope = getRequestScope(ctx.request);
132
+ const scope = scopeOf(ctx);
81
133
  if (isElevated(scope)) return true;
82
134
  if (isMember(scope) && roles.some((r) => scope.orgRoles.includes(r))) return true;
83
135
  }
84
- return {
85
- granted: false,
86
- reason: `Required roles: ${roles.join(", ")}`
87
- };
136
+ return deny(`Required roles: ${roles.join(", ")}`);
88
137
  };
89
138
  check._roles = roles;
90
139
  return check;
@@ -97,19 +146,13 @@ function requireRoles(rolesOrFirst, optionsOrSecond, ...rest) {
97
146
  function roles(...args) {
98
147
  const roleList = normalizeVariadicOrArray(args);
99
148
  const check = (ctx) => {
100
- if (!ctx.user) return {
101
- granted: false,
102
- reason: "Authentication required"
103
- };
149
+ if (!ctx.user) return deny("Authentication required");
104
150
  const userRoles = getUserRoles(ctx.user);
105
151
  if (roleList.some((r) => userRoles.includes(r))) return true;
106
- const scope = getRequestScope(ctx.request);
152
+ const scope = scopeOf(ctx);
107
153
  if (isElevated(scope)) return true;
108
154
  if (isMember(scope) && roleList.some((r) => scope.orgRoles.includes(r))) return true;
109
- return {
110
- granted: false,
111
- reason: `Required roles: ${roleList.join(", ")}`
112
- };
155
+ return deny(`Required roles: ${roleList.join(", ")}`);
113
156
  };
114
157
  check._roles = roleList;
115
158
  return check;
@@ -128,31 +171,29 @@ function roles(...args) {
128
171
  */
129
172
  function requireOwnership(ownerField = "userId", options) {
130
173
  return (ctx) => {
131
- if (!ctx.user) return {
132
- granted: false,
133
- reason: "Authentication required"
134
- };
174
+ if (!ctx.user) return deny("Authentication required");
135
175
  const userRoles = getUserRoles(ctx.user);
136
176
  if (options?.bypassRoles?.some((r) => userRoles.includes(r))) return true;
137
- const userId = getUserId(getRequestScope(ctx.request)) ?? ctx.user.id ?? ctx.user._id;
138
- if (!userId) return {
139
- granted: false,
140
- reason: "User identity missing (no id or _id)"
141
- };
142
- return {
143
- granted: true,
144
- filters: { [ownerField]: userId }
145
- };
177
+ const userId = getUserId(scopeOf(ctx)) ?? ctx.user.id ?? ctx.user._id;
178
+ if (!userId) return deny("User identity missing (no id or _id)");
179
+ return allow({ policy: { [ownerField]: userId } });
146
180
  };
147
181
  }
148
182
  /**
149
183
  * Combine multiple checks — ALL must pass (AND logic).
150
184
  *
151
- * Each child runs against the **accumulated** state of previous children:
152
- * - `filters` from earlier children merge into the next child's `_policyFilters`
153
- * - `scope` from earlier children installs on the request before the next child runs
185
+ * Evaluation is PURE — nothing is written to the request. Each child runs
186
+ * against the accumulated state of previous children, threaded through a fresh
187
+ * child context:
188
+ * - `policy` from earlier children is CONJOINED (logical AND) — a later child
189
+ * can add or narrow restrictions but never silently replaces an earlier
190
+ * child's constraint on the same key (conflicting values are preserved under
191
+ * `$and`; see `conjoinPolicyFilters`)
192
+ * - `scope` installed by an earlier child is visible to the next through
193
+ * `scopeOf(ctx)`, and never downgrades an already-authoritative scope
154
194
  *
155
- * The final result carries both merged `filters` and merged `scope`.
195
+ * The returned decision carries the merged `policy` + `scope`; the enforcement
196
+ * point (`applyAuthorizationDecision`) applies them to the request once.
156
197
  *
157
198
  * @example
158
199
  * ```typescript
@@ -163,50 +204,37 @@ function requireOwnership(ownerField = "userId", options) {
163
204
  * ```
164
205
  */
165
206
  function allOf(...checks) {
166
- return async (ctx) => {
207
+ const check = async (ctx) => {
167
208
  let mergedFilters = {};
168
209
  let installedScope;
169
- const sink = ctx.request;
170
- const originalFilters = sink._policyFilters;
171
- const originalScope = sink.scope;
172
- try {
173
- for (const check of checks) {
174
- const result = await check(ctx);
175
- const normalized = typeof result === "boolean" ? { granted: result } : result;
176
- if (!normalized.granted) {
177
- sink._policyFilters = originalFilters;
178
- sink.scope = originalScope;
179
- return normalized;
180
- }
181
- if (normalized.filters) {
182
- mergedFilters = {
183
- ...mergedFilters,
184
- ...normalized.filters
185
- };
186
- sink._policyFilters = {
187
- ...sink._policyFilters ?? {},
188
- ...normalized.filters
189
- };
190
- }
191
- if (normalized.scope) {
192
- const current = sink.scope;
193
- if (!current || current.kind === "public") {
194
- sink.scope = normalized.scope;
195
- installedScope = normalized.scope;
196
- } else if (!installedScope) installedScope = normalized.scope;
197
- }
198
- }
199
- } catch (err) {
200
- sink._policyFilters = originalFilters;
201
- sink.scope = originalScope;
202
- throw err;
210
+ let threaded = ctx.scope;
211
+ let childCtx = ctx;
212
+ for (const inner of checks) {
213
+ const decision = normalizeToDecision(await inner(childCtx));
214
+ if (decision.effect !== "allow") return deny(decision.reason);
215
+ if (decision.policy) mergedFilters = conjoinPolicyFilters(mergedFilters, decision.policy);
216
+ if (decision.scope && (!threaded || threaded.kind === "public")) {
217
+ threaded = decision.scope;
218
+ installedScope = decision.scope;
219
+ } else if (decision.scope && !installedScope) installedScope = decision.scope;
220
+ if (threaded !== childCtx.scope) childCtx = {
221
+ ...childCtx,
222
+ scope: threaded
223
+ };
203
224
  }
204
- return {
205
- granted: true,
206
- filters: Object.keys(mergedFilters).length > 0 ? mergedFilters : void 0,
207
- scope: installedScope
208
- };
225
+ return allow({
226
+ ...Object.keys(mergedFilters).length > 0 ? { policy: mergedFilters } : {},
227
+ ...installedScope ? { scope: installedScope } : {}
228
+ });
209
229
  };
230
+ const roleBranches = checks.filter((c) => (c._roles?.length ?? 0) > 0 || (c._orgRoles?.length ?? 0) > 0);
231
+ if (checks.length > 0 && checks.every((c) => c._isPublic)) check._isPublic = true;
232
+ else if (roleBranches.length === 1) {
233
+ const [rb] = roleBranches;
234
+ if (rb?._roles?.length) check._roles = [...rb._roles];
235
+ if (rb?._orgRoles?.length) check._orgRoles = [...rb._orgRoles];
236
+ }
237
+ return check;
210
238
  }
211
239
  /**
212
240
  * Combine multiple checks — ANY must pass (OR logic).
@@ -219,19 +247,30 @@ function allOf(...checks) {
219
247
  * ```
220
248
  */
221
249
  function anyOf(...checks) {
222
- return async (ctx) => {
250
+ const check = async (ctx) => {
223
251
  const reasons = [];
224
- for (const check of checks) {
225
- const result = await check(ctx);
226
- const normalized = typeof result === "boolean" ? { granted: result } : result;
227
- if (normalized.granted) return normalized;
228
- if (normalized.reason) reasons.push(normalized.reason);
252
+ for (const inner of checks) {
253
+ const decision = normalizeToDecision(await inner(ctx));
254
+ if (decision.effect === "allow") return allow({
255
+ ...decision.policy ? { policy: decision.policy } : {},
256
+ ...decision.scope ? { scope: decision.scope } : {}
257
+ });
258
+ if (decision.reason) reasons.push(decision.reason);
229
259
  }
230
- return {
231
- granted: false,
232
- reason: reasons.join("; ")
233
- };
260
+ return deny(reasons.join("; "));
234
261
  };
262
+ if (checks.some((c) => c._isPublic)) check._isPublic = true;
263
+ else if (checks.every((c) => (c._roles?.length ?? 0) > 0 || (c._orgRoles?.length ?? 0) > 0)) {
264
+ const roles = /* @__PURE__ */ new Set();
265
+ const orgRoles = /* @__PURE__ */ new Set();
266
+ for (const c of checks) {
267
+ for (const r of c._roles ?? []) roles.add(r);
268
+ for (const r of c._orgRoles ?? []) orgRoles.add(r);
269
+ }
270
+ if (roles.size > 0) check._roles = [...roles];
271
+ if (orgRoles.size > 0) check._orgRoles = [...orgRoles];
272
+ }
273
+ return check;
235
274
  }
236
275
  /**
237
276
  * Invert a permission check. Grants when the wrapped check denies, denies
@@ -251,11 +290,7 @@ function anyOf(...checks) {
251
290
  */
252
291
  function not(check, reason = "Access denied") {
253
292
  return async (ctx) => {
254
- const result = await check(ctx);
255
- return (typeof result === "boolean" ? { granted: result } : result).granted ? {
256
- granted: false,
257
- reason
258
- } : true;
293
+ return normalizeToDecision(await check(ctx)).effect === "allow" ? deny(reason) : true;
259
294
  };
260
295
  }
261
296
  /**
@@ -267,10 +302,7 @@ function not(check, reason = "Access denied") {
267
302
  * ```
268
303
  */
269
304
  function denyAll(reason = "Access denied") {
270
- return () => ({
271
- granted: false,
272
- reason
273
- });
305
+ return () => deny(reason);
274
306
  }
275
307
  /**
276
308
  * Dynamic permission based on a condition function.
@@ -284,11 +316,7 @@ function denyAll(reason = "Access denied") {
284
316
  */
285
317
  function when(condition) {
286
318
  return async (ctx) => {
287
- const result = await condition(ctx);
288
- return {
289
- granted: result,
290
- reason: result ? void 0 : "Condition not met"
291
- };
319
+ return await condition(ctx) ? allow() : deny("Condition not met");
292
320
  };
293
321
  }
294
322
  //#endregion
@@ -349,16 +377,10 @@ const DEFAULT_TTL_GRACE_MS = 3e4;
349
377
  */
350
378
  function requireDPoP() {
351
379
  const check = (ctx) => {
352
- const scope = getRequestScope(ctx.request);
380
+ const scope = scopeOf(ctx);
353
381
  if (isElevated(scope)) return true;
354
- if (!isService(scope)) return {
355
- granted: false,
356
- reason: "DPoP-bound service identity required. Configure your authenticate callback to set scope.dpopJkt after verifying the DPoP proof header (RFC 9449)."
357
- };
358
- if (!getDPoPJkt(scope)) return {
359
- granted: false,
360
- reason: "Sender-constrained credential required (DPoP). Inbound token is bearer; replay-resistance is mandatory on this endpoint."
361
- };
382
+ if (!isService(scope)) return deny("DPoP-bound service identity required. Configure your authenticate callback to set scope.dpopJkt after verifying the DPoP proof header (RFC 9449).");
383
+ if (!getDPoPJkt(scope)) return deny("Sender-constrained credential required (DPoP). Inbound token is bearer; replay-resistance is mandatory on this endpoint.");
362
384
  return true;
363
385
  };
364
386
  check._dpopRequired = true;
@@ -404,38 +426,20 @@ function requireMandate(capability, opts = {}) {
404
426
  const ttlGrace = opts.ttlGraceMs ?? DEFAULT_TTL_GRACE_MS;
405
427
  const noElevatedBypass = opts.noElevatedBypass === true;
406
428
  const check = (ctx) => {
407
- const scope = getRequestScope(ctx.request);
429
+ const scope = scopeOf(ctx);
408
430
  if (isElevated(scope) && !noElevatedBypass) return true;
409
431
  const mandate = getMandate(scope);
410
- if (!mandate) return {
411
- granted: false,
412
- reason: `Capability mandate required (${capability}). Configure your authenticate callback to populate request.scope.mandate after verifying the mandate JWT/VC.`
413
- };
414
- if (mandate.capability !== capability) return {
415
- granted: false,
416
- reason: `Mandate authorizes "${mandate.capability}", not "${capability}"`
417
- };
418
- if (mandate.expiresAt !== void 0 && Date.now() > mandate.expiresAt + ttlGrace) return {
419
- granted: false,
420
- reason: `Mandate expired at ${new Date(mandate.expiresAt).toISOString()}`
421
- };
432
+ if (!mandate) return deny(`Capability mandate required (${capability}). Configure your authenticate callback to populate request.scope.mandate after verifying the mandate JWT/VC.`);
433
+ if (mandate.capability !== capability) return deny(`Mandate authorizes "${mandate.capability}", not "${capability}"`);
434
+ if (mandate.expiresAt !== void 0 && Date.now() > mandate.expiresAt + ttlGrace) return deny(`Mandate expired at ${new Date(mandate.expiresAt).toISOString()}`);
422
435
  if (opts.audience) {
423
436
  const required = typeof opts.audience === "function" ? opts.audience(ctx) : opts.audience;
424
- if (required && mandate.audience && mandate.audience !== required) return {
425
- granted: false,
426
- reason: `Mandate is bound to "${mandate.audience}", not "${required}"`
427
- };
428
- if (required && !mandate.audience) return {
429
- granted: false,
430
- reason: `Mandate must be bound to "${required}" (no audience claim)`
431
- };
437
+ if (required && mandate.audience && mandate.audience !== required) return deny(`Mandate is bound to "${mandate.audience}", not "${required}"`);
438
+ if (required && !mandate.audience) return deny(`Mandate must be bound to "${required}" (no audience claim)`);
432
439
  }
433
440
  if (opts.validateAmount) {
434
441
  const result = opts.validateAmount(ctx, mandate);
435
- if (result !== true) return {
436
- granted: false,
437
- reason: typeof result === "string" ? result : `Action exceeds mandate cap ${mandate.cap}${mandate.currency ? ` ${mandate.currency}` : ""}`
438
- };
442
+ if (result !== true) return deny(typeof result === "string" ? result : `Action exceeds mandate cap ${mandate.cap}${mandate.currency ? ` ${mandate.currency}` : ""}`);
439
443
  }
440
444
  return true;
441
445
  };
@@ -482,30 +486,18 @@ function requireAgentScope(opts) {
482
486
  const dpopCheck = needsDPoP ? requireDPoP() : null;
483
487
  const requiredScopes = scopes && scopes.length > 0 ? [...scopes] : null;
484
488
  const check = async (ctx) => {
485
- const scope = getRequestScope(ctx.request);
489
+ const scope = scopeOf(ctx);
486
490
  if (isElevated(scope) && !mandateOpts.noElevatedBypass) return true;
487
- if (!isService(scope)) return {
488
- granted: false,
489
- reason: "Service identity required (machine principal). Agent flows must authenticate as a service, not a logged-in user."
490
- };
491
+ if (!isService(scope)) return deny("Service identity required (machine principal). Agent flows must authenticate as a service, not a logged-in user.");
491
492
  if (requiredScopes) {
492
493
  const granted = scope.scopes ?? [];
493
- if (!requiredScopes.some((s) => granted.includes(s))) return {
494
- granted: false,
495
- reason: `Service identity is missing required OAuth scope(s): ${requiredScopes.join(", ")}`
496
- };
494
+ if (!requiredScopes.some((s) => granted.includes(s))) return deny(`Service identity is missing required OAuth scope(s): ${requiredScopes.join(", ")}`);
497
495
  }
498
496
  const mandateResult = await mandateCheck(ctx);
499
- if (mandateResult !== true) return typeof mandateResult === "object" ? mandateResult : {
500
- granted: false,
501
- reason: "Mandate check failed"
502
- };
497
+ if (mandateResult !== true) return typeof mandateResult === "object" ? mandateResult : deny("Mandate check failed");
503
498
  if (dpopCheck) {
504
499
  const dpopResult = await dpopCheck(ctx);
505
- if (dpopResult !== true) return typeof dpopResult === "object" ? dpopResult : {
506
- granted: false,
507
- reason: "DPoP binding required"
508
- };
500
+ if (dpopResult !== true) return typeof dpopResult === "object" ? dpopResult : deny("DPoP binding required");
509
501
  }
510
502
  return true;
511
503
  };
@@ -521,9 +513,9 @@ function requireAgentScope(opts) {
521
513
  /**
522
514
  * Permission Scope — checks bound to RequestScope.
523
515
  *
524
- * All read `request.scope` populated by an auth adapter (Better Auth bridge,
525
- * JWT custom auth, or an upstream permission check returning
526
- * `PermissionResult.scope`).
516
+ * All read the request scope via `scopeOf(ctx)` - populated by an auth adapter
517
+ * (Better Auth bridge, JWT custom auth) or threaded from an upstream check's
518
+ * `AuthorizationDecision.scope`.
527
519
  */
528
520
  /**
529
521
  * Require an org-bound caller. Grants for `member`, `service`, and
@@ -544,15 +536,9 @@ function requireAgentScope(opts) {
544
536
  */
545
537
  function requireOrgMembership() {
546
538
  const check = (ctx) => {
547
- if (hasOrgAccess(getRequestScope(ctx.request))) return true;
548
- if (!ctx.user) return {
549
- granted: false,
550
- reason: "Authentication required"
551
- };
552
- return {
553
- granted: false,
554
- reason: "Organization membership required"
555
- };
539
+ if (hasOrgAccess(scopeOf(ctx))) return true;
540
+ if (!ctx.user) return deny("Authentication required");
541
+ return deny("Organization membership required");
556
542
  };
557
543
  check._orgPermission = "membership";
558
544
  return check;
@@ -579,25 +565,13 @@ function requireOrgMembership() {
579
565
  function requireOrgRole(...args) {
580
566
  const roles = normalizeVariadicOrArray(args);
581
567
  const check = (ctx) => {
582
- const scope = getRequestScope(ctx.request);
568
+ const scope = scopeOf(ctx);
583
569
  if (isElevated(scope)) return true;
584
- if (isService(scope)) return {
585
- granted: false,
586
- reason: "Service scopes (API keys) cannot satisfy requireOrgRole. Use requireServiceScope(...) for machine identities, or compose with anyOf(requireOrgRole(...), requireServiceScope(...)) to accept both."
587
- };
588
- if (!ctx.user) return {
589
- granted: false,
590
- reason: "Authentication required"
591
- };
592
- if (!isMember(scope)) return {
593
- granted: false,
594
- reason: "Organization membership required"
595
- };
570
+ if (isService(scope)) return deny("Service scopes (API keys) cannot satisfy requireOrgRole. Use requireServiceScope(...) for machine identities, or compose with anyOf(requireOrgRole(...), requireServiceScope(...)) to accept both.");
571
+ if (!ctx.user) return deny("Authentication required");
572
+ if (!isMember(scope)) return deny("Organization membership required");
596
573
  if (roles.some((r) => scope.orgRoles.includes(r))) return true;
597
- return {
598
- granted: false,
599
- reason: `Required org roles: ${roles.join(", ")}`
600
- };
574
+ return deny(`Required org roles: ${roles.join(", ")}`);
601
575
  };
602
576
  check._orgRoles = roles;
603
577
  return check;
@@ -629,18 +603,12 @@ function requireServiceScope(...args) {
629
603
  const required = normalizeVariadicOrArray(args);
630
604
  if (required.length === 0) throw new Error("requireServiceScope() requires at least one scope string (e.g. requireServiceScope('jobs:write'))");
631
605
  const check = (ctx) => {
632
- const scope = getRequestScope(ctx.request);
606
+ const scope = scopeOf(ctx);
633
607
  if (isElevated(scope)) return true;
634
- if (!isService(scope)) return {
635
- granted: false,
636
- reason: "Service identity required (API key). For human users, use requireOrgRole(...) or compose with anyOf(requireOrgRole(...), requireServiceScope(...))."
637
- };
608
+ if (!isService(scope)) return deny("Service identity required (API key). For human users, use requireOrgRole(...) or compose with anyOf(requireOrgRole(...), requireServiceScope(...)).");
638
609
  const granted = getServiceScopes(scope);
639
610
  if (required.some((r) => granted.includes(r))) return true;
640
- return {
641
- granted: false,
642
- reason: `Required service scopes: ${required.join(", ")} (granted: ${granted.length > 0 ? granted.join(", ") : "none"})`
643
- };
611
+ return deny(`Required service scopes: ${required.join(", ")} (granted: ${granted.length > 0 ? granted.join(", ") : "none"})`);
644
612
  };
645
613
  check._serviceScopes = required;
646
614
  return check;
@@ -678,23 +646,14 @@ function requireScopeContext(keyOrMap, value) {
678
646
  const requiredKeys = Object.keys(required);
679
647
  if (requiredKeys.length === 0) throw new Error("requireScopeContext() requires at least one key (e.g. requireScopeContext('branchId'))");
680
648
  const check = (ctx) => {
681
- const scope = getRequestScope(ctx.request);
649
+ const scope = scopeOf(ctx);
682
650
  if (isElevated(scope)) return true;
683
- if (!getScopeContextMap(scope)) return {
684
- granted: false,
685
- reason: "Scope context required (member, service, or elevated scope). Populate request.scope.context in your auth function."
686
- };
651
+ if (!getScopeContextMap(scope)) return deny("Scope context required (member, service, or elevated scope). Populate request.scope.context in your auth function.");
687
652
  for (const key of requiredKeys) {
688
653
  const expected = required[key];
689
654
  const actual = getScopeContext(scope, key);
690
- if (actual === void 0) return {
691
- granted: false,
692
- reason: `Required scope context key "${key}" is missing`
693
- };
694
- if (expected !== void 0 && actual !== expected) return {
695
- granted: false,
696
- reason: `Required scope context "${key}" must equal "${expected}" (got "${actual}")`
697
- };
655
+ if (actual === void 0) return deny(`Required scope context key "${key}" is missing`);
656
+ if (expected !== void 0 && actual !== expected) return deny(`Required scope context "${key}" must equal "${expected}" (got "${actual}")`);
698
657
  }
699
658
  return true;
700
659
  };
@@ -711,8 +670,8 @@ function requireScopeContext(keyOrMap, value) {
711
670
  *
712
671
  * **Two call shapes:**
713
672
  * ```typescript
714
- * requireOrgInScope('acme-holding') // static
715
- * requireOrgInScope((ctx) => ctx.request.params.orgId) // dynamic
673
+ * requireOrgInScope('acme-holding') // static
674
+ * requireOrgInScope((ctx) => ctx.params?.orgId) // dynamic — from ctx facts, not the raw request
716
675
  * ```
717
676
  *
718
677
  * `elevated` scope grants unconditionally (cross-org bypass).
@@ -720,9 +679,9 @@ function requireScopeContext(keyOrMap, value) {
720
679
  * @example
721
680
  * ```typescript
722
681
  * permissions: {
723
- * list: requireOrgInScope((ctx) => ctx.request.params.orgId),
682
+ * list: requireOrgInScope((ctx) => ctx.params?.orgId),
724
683
  * create: allOf(
725
- * requireOrgInScope((ctx) => ctx.request.body?.organizationId),
684
+ * requireOrgInScope((ctx) => (ctx.data as { organizationId?: string })?.organizationId),
726
685
  * requireOrgRole('admin'),
727
686
  * ),
728
687
  * }
@@ -731,18 +690,12 @@ function requireScopeContext(keyOrMap, value) {
731
690
  function requireOrgInScope(target) {
732
691
  if (target === void 0 || target === null) throw new Error("requireOrgInScope() requires a target org id (string) or an extractor function");
733
692
  const check = (ctx) => {
734
- const scope = getRequestScope(ctx.request);
693
+ const scope = scopeOf(ctx);
735
694
  if (isElevated(scope)) return true;
736
695
  const targetOrgId = typeof target === "function" ? target(ctx) : target;
737
- if (!targetOrgId) return {
738
- granted: false,
739
- reason: "requireOrgInScope: target org id could not be resolved from the request"
740
- };
696
+ if (!targetOrgId) return deny("requireOrgInScope: target org id could not be resolved from the request");
741
697
  if (isOrgInScope(scope, targetOrgId)) return true;
742
- return {
743
- granted: false,
744
- reason: `Target organization "${targetOrgId}" is not in the caller's org hierarchy`
745
- };
698
+ return deny(`Target organization "${targetOrgId}" is not in the caller's org hierarchy`);
746
699
  };
747
700
  check._orgInScopeTarget = target;
748
701
  return check;
@@ -762,20 +715,11 @@ function requireOrgInScope(target) {
762
715
  */
763
716
  function requireTeamMembership() {
764
717
  const check = (ctx) => {
765
- if (!ctx.user) return {
766
- granted: false,
767
- reason: "Authentication required"
768
- };
769
- const scope = getRequestScope(ctx.request);
718
+ if (!ctx.user) return deny("Authentication required");
719
+ const scope = scopeOf(ctx);
770
720
  if (isElevated(scope)) return true;
771
- if (!isMember(scope)) return {
772
- granted: false,
773
- reason: "Organization membership required"
774
- };
775
- if (!getTeamId(scope)) return {
776
- granted: false,
777
- reason: "No active team"
778
- };
721
+ if (!isMember(scope)) return deny("Organization membership required");
722
+ if (!getTeamId(scope)) return deny("No active team");
779
723
  return true;
780
724
  };
781
725
  check._teamPermission = "membership";
@@ -832,21 +776,12 @@ function createOrgPermissions(config) {
832
776
  return {
833
777
  can(permissions) {
834
778
  return (ctx) => {
835
- if (!ctx.user) return {
836
- granted: false,
837
- reason: "Authentication required"
838
- };
839
- const scope = getRequestScope(ctx.request);
779
+ if (!ctx.user) return deny("Authentication required");
780
+ const scope = scopeOf(ctx);
840
781
  if (isElevated(scope)) return true;
841
- if (!isMember(scope)) return {
842
- granted: false,
843
- reason: "Organization membership required"
844
- };
782
+ if (!isMember(scope)) return deny("Organization membership required");
845
783
  if (hasPermissions(scope.orgRoles, permissions)) return true;
846
- return {
847
- granted: false,
848
- reason: `Missing permissions: ${Object.entries(permissions).map(([r, a]) => `${r}:[${a.join(",")}]`).join(", ")}`
849
- };
784
+ return deny(`Missing permissions: ${Object.entries(permissions).map(([r, a]) => `${r}:[${a.join(",")}]`).join(", ")}`);
850
785
  };
851
786
  },
852
787
  requireRole(...roles) {
@@ -873,7 +808,7 @@ function createOrgPermissions(config) {
873
808
  * - Cross-node invalidation via the event bus
874
809
  */
875
810
  function createDynamicPermissionMatrix(config) {
876
- const logger = config.logger ?? console;
811
+ const logger = config.logger ?? arcLog("permissions:dynamic");
877
812
  const configuredTtlSeconds = config.cache?.ttlSeconds ?? 0;
878
813
  const hasExternalStore = !!config.cacheStore;
879
814
  const cacheTtlSeconds = configuredTtlSeconds > 0 ? configuredTtlSeconds : hasExternalStore ? 300 : 0;
@@ -944,34 +879,19 @@ function createDynamicPermissionMatrix(config) {
944
879
  }
945
880
  function can(required) {
946
881
  return async (ctx) => {
947
- if (!ctx.user) return {
948
- granted: false,
949
- reason: "Authentication required"
950
- };
951
- const scope = getRequestScope(ctx.request);
882
+ if (!ctx.user) return deny("Authentication required");
883
+ const scope = scopeOf(ctx);
952
884
  if (isElevated(scope)) return true;
953
- if (!isMember(scope)) return {
954
- granted: false,
955
- reason: "Organization membership required"
956
- };
885
+ if (!isMember(scope)) return deny("Organization membership required");
957
886
  const orgRoles = scope.orgRoles;
958
- if (orgRoles.length === 0) return {
959
- granted: false,
960
- reason: "Not a member of this organization"
961
- };
887
+ if (orgRoles.length === 0) return deny("Not a member of this organization");
962
888
  let matrix;
963
889
  try {
964
890
  matrix = await resolveMatrix(ctx, scope.organizationId, orgRoles);
965
891
  } catch (error) {
966
- return {
967
- granted: false,
968
- reason: `Permission matrix resolution failed: ${error instanceof Error ? error.message : String(error)}`
969
- };
892
+ return deny(`Permission matrix resolution failed: ${error instanceof Error ? error.message : String(error)}`);
970
893
  }
971
- for (const [resource, actions] of Object.entries(required)) for (const action of actions) if (!orgRoles.some((role) => roleAllows(matrix, role, resource, action))) return {
972
- granted: false,
973
- reason: `Missing permission: ${resource}:${action}`
974
- };
894
+ for (const [resource, actions] of Object.entries(required)) for (const action of actions) if (!orgRoles.some((role) => roleAllows(matrix, role, resource, action))) return deny(`Missing permission: ${resource}:${action}`);
975
895
  return true;
976
896
  };
977
897
  }
@@ -1052,8 +972,132 @@ function createDynamicPermissionMatrix(config) {
1052
972
  };
1053
973
  }
1054
974
  //#endregion
975
+ //#region src/permissions/explain.ts
976
+ /**
977
+ * Extract a check's static requirement from its `PermissionCheckMeta`. Public
978
+ * wins; then the union of platform + org roles a role gate declares; otherwise
979
+ * "authenticated" (auth-required / custom / request-time checks).
980
+ */
981
+ function describePermission(check) {
982
+ if (check._isPublic) return { kind: "public" };
983
+ const roles = [...check._roles ?? [], ...check._orgRoles ?? []];
984
+ if (roles.length > 0) return {
985
+ kind: "roles",
986
+ roles
987
+ };
988
+ return { kind: "authenticated" };
989
+ }
990
+ /**
991
+ * Explain whether a principal (by roles) passes a permission check, from static
992
+ * metadata alone — for permission-matrix UIs, docs, and audits. Omit `principal`
993
+ * to get the requirement-only view (roles → `conditional`).
994
+ *
995
+ * @example
996
+ * explainAccess(requireRoles("admin"), { roles: ["editor"] })
997
+ * // → { decision: "deny", reason: "requires one of: admin", requirement: { kind: "roles", roles: ["admin"] } }
998
+ */
999
+ function explainAccess(check, principal) {
1000
+ const requirement = describePermission(check);
1001
+ if (requirement.kind === "public") return {
1002
+ decision: "allow",
1003
+ reason: "public — no authentication required",
1004
+ requirement
1005
+ };
1006
+ if (requirement.kind === "authenticated") return {
1007
+ decision: "conditional",
1008
+ reason: "requires authentication; decided per request",
1009
+ requirement
1010
+ };
1011
+ if (!principal) return {
1012
+ decision: "conditional",
1013
+ reason: `requires one of: ${requirement.roles.join(", ")}`,
1014
+ requirement
1015
+ };
1016
+ const held = /* @__PURE__ */ new Set([...principal.roles ?? [], ...principal.orgRoles ?? []]);
1017
+ const matched = requirement.roles.filter((r) => held.has(r));
1018
+ return matched.length > 0 ? {
1019
+ decision: "allow",
1020
+ reason: `granted via role(s): ${matched.join(", ")}`,
1021
+ requirement
1022
+ } : {
1023
+ decision: "deny",
1024
+ reason: `requires one of: ${requirement.roles.join(", ")}`,
1025
+ requirement
1026
+ };
1027
+ }
1028
+ /**
1029
+ * Introspect a whole permission map (`{ list, get, create, ... }` or any
1030
+ * action→check record) into `action → requirement`. The reusable core of a
1031
+ * `/permissions/matrix` endpoint or an access audit — framework-owned, so a host
1032
+ * never hand-rolls `(check as any)._roles` peeking. Undefined slots (a CRUD op
1033
+ * with no gate = public-by-omission) surface as `public` so the inventory is
1034
+ * honest about what's exposed.
1035
+ */
1036
+ function describePermissionMap(permissions) {
1037
+ const out = {};
1038
+ for (const [action, check] of Object.entries(permissions)) out[action] = check ? describePermission(check) : { kind: "public" };
1039
+ return out;
1040
+ }
1041
+ /**
1042
+ * The public attack surface of a permission map — the actions reachable with NO
1043
+ * authentication (explicit `allowPublic()` or public-by-omission). Empty is the
1044
+ * healthy default for a protected resource; a non-empty list is what a
1045
+ * "what's exposed?" audit report should show.
1046
+ */
1047
+ function collectPublicSurface(permissions) {
1048
+ return Object.entries(describePermissionMap(permissions)).filter(([, req]) => req.kind === "public").map(([action]) => action);
1049
+ }
1050
+ //#endregion
1055
1051
  //#region src/permissions/grants.ts
1056
1052
  /**
1053
+ * Per-record grants — the `requireGrant` combinator + mode lattice.
1054
+ *
1055
+ * The one ACL idea arc's permission vocabulary lacked (design verdict:
1056
+ * `designs/record-sharing.md`): subject × record × mode grants, Puter-style,
1057
+ * WITHOUT Puter's mistakes. Arc ships only the GATE — grant storage is a
1058
+ * host `defineResource` (tenant-scoped, `audit: true`, any kit via
1059
+ * repo-core's `RepositoryLike`), and the `resolve` callback is structural,
1060
+ * so core stays DB-agnostic and storage-free by construction.
1061
+ *
1062
+ * What this design deliberately avoids (verified against Puter's source):
1063
+ * - string-typed permissions (`fs:<uuid>:read` VARCHARs that silently
1064
+ * never match when malformed) → typed {@link GrantMode} union
1065
+ * - per-item ACL checks on list responses (their readdir returns
1066
+ * unfiltered children; the UI stats each item — N+1) → resolutions
1067
+ * return a row `policy` that composes into THE list query via the
1068
+ * decision's `policy` → `_policyFilters` (one paginated, correct query)
1069
+ * - un-revocable capability tokens → share links should reference a
1070
+ * grant ROW (`subjectType: 'link'`); delete the row, the link dies
1071
+ *
1072
+ * @example
1073
+ * ```ts
1074
+ * // Host owns the grants table — it's just a resource:
1075
+ * // { resource, resourceId, subjectType: 'user'|'team'|'link',
1076
+ * // subjectId, mode, grantedBy, expiresAt? }
1077
+ * import { anyOf, requireGrant, requireOwnership } from '@classytic/arc/permissions';
1078
+ *
1079
+ * defineResource({
1080
+ * name: 'document',
1081
+ * permissions: {
1082
+ * get: anyOf(requireOwnership('ownerId'), requireGrant({ mode: 'read', resolve })),
1083
+ * update: anyOf(requireOwnership('ownerId'), requireGrant({ mode: 'write', resolve })),
1084
+ * list: requireGrant({
1085
+ * mode: 'list',
1086
+ * // List-shaped resolution: return filters — owner OR granted ids.
1087
+ * resolve: async (ctx) => ({
1088
+ * filters: {
1089
+ * $or: [
1090
+ * { ownerId: ctx.user?.id },
1091
+ * { _id: { $in: await grants.grantedIds(ctx) } },
1092
+ * ],
1093
+ * },
1094
+ * }),
1095
+ * }),
1096
+ * },
1097
+ * });
1098
+ * ```
1099
+ */
1100
+ /**
1057
1101
  * The grant-mode lattice, weakest → strongest. Holding a mode satisfies
1058
1102
  * every mode at or below it (`write` ⊇ `read` ⊇ `list` ⊇ `see`); `manage`
1059
1103
  * (grant/revoke rights) satisfies everything. Same semantics as Puter's
@@ -1095,36 +1139,21 @@ function requireGrant(options) {
1095
1139
  try {
1096
1140
  resolution = await resolve(ctx);
1097
1141
  } catch (err) {
1098
- ctx.request.log?.warn?.({
1142
+ ctx.request?.log?.warn?.({
1099
1143
  err,
1100
1144
  resource: ctx.resource,
1101
1145
  action: ctx.action
1102
1146
  }, "requireGrant: resolve threw — denying (fail-closed)");
1103
- return {
1104
- granted: false,
1105
- reason: "Grant lookup failed"
1106
- };
1147
+ return deny("Grant lookup failed");
1107
1148
  }
1108
1149
  if (typeof resolution === "boolean") return resolution;
1109
1150
  const { mode: held, filters, reason } = resolution;
1110
1151
  if (held !== void 0) {
1111
- if (!modeSatisfies(held, required)) return {
1112
- granted: false,
1113
- ...reason ? { reason } : {}
1114
- };
1115
- return {
1116
- granted: true,
1117
- ...filters ? { filters } : {}
1118
- };
1152
+ if (!modeSatisfies(held, required)) return deny(reason);
1153
+ return allow(filters ? { policy: filters } : void 0);
1119
1154
  }
1120
- if (filters !== void 0) return {
1121
- granted: true,
1122
- filters
1123
- };
1124
- return {
1125
- granted: false,
1126
- ...reason ? { reason } : {}
1127
- };
1155
+ if (filters !== void 0) return allow({ policy: filters });
1156
+ return deny(reason);
1128
1157
  };
1129
1158
  }
1130
1159
  //#endregion
@@ -1343,4 +1372,4 @@ function readOnly(overrides) {
1343
1372
  }, overrides);
1344
1373
  }
1345
1374
  //#endregion
1346
- export { requireAuth as A, requireDPoP as C, anyOf as D, allowPublic as E, requireRoles as M, roles as N, denyAll as O, when as P, requireAgentScope as S, allOf as T, requireOrgMembership as _, permissionMatrix as a, requireServiceScope as b, publicReadAdminWrite as c, GRANT_MODES as d, modeSatisfies as f, requireOrgInScope as g, createOrgPermissions as h, ownerWithAdminBypass as i, requireOwnership as j, not as k, readOnly as l, createDynamicPermissionMatrix as m, authenticated as n, presets_exports as o, requireGrant as p, fullPublic as r, publicRead as s, adminOnly as t, createRoleHierarchy as u, requireOrgRole as v, requireMandate as w, requireTeamMembership as x, requireScopeContext as y };
1375
+ export { allow as A, when as B, requireScopeContext as C, requireDPoP as D, requireAgentScope as E, not as F, requireAuth as I, requireOwnership as L, anyOf as M, deny as N, requireMandate as O, denyAll as P, requireRoles as R, requireOrgRole as S, requireTeamMembership as T, scopeOf as V, explainAccess as _, permissionMatrix as a, requireOrgInScope as b, publicReadAdminWrite as c, GRANT_MODES as d, modeSatisfies as f, describePermissionMap as g, describePermission as h, ownerWithAdminBypass as i, allowPublic as j, allOf as k, readOnly as l, collectPublicSurface as m, authenticated as n, presets_exports as o, requireGrant as p, fullPublic as r, publicRead as s, adminOnly as t, createRoleHierarchy as u, createDynamicPermissionMatrix as v, requireServiceScope as w, requireOrgMembership as x, createOrgPermissions as y, roles as z };