@classytic/arc 2.11.4 → 2.13.1

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 (166) hide show
  1. package/README.md +16 -12
  2. package/dist/{BaseController-swXruJ2_.mjs → BaseController-DX_T-bDB.mjs} +388 -423
  3. package/dist/EventTransport-CT_52aWU.d.mts +34 -0
  4. package/dist/EventTransport-DLWoUMHy.mjs +103 -0
  5. package/dist/{ResourceRegistry-DkAeAuTX.mjs → ResourceRegistry-CTERg_2x.mjs} +139 -66
  6. package/dist/audit/index.d.mts +2 -2
  7. package/dist/audit/index.mjs +1 -1
  8. package/dist/auth/audit.d.mts +199 -0
  9. package/dist/auth/audit.mjs +288 -0
  10. package/dist/auth/index.d.mts +3 -3
  11. package/dist/auth/index.mjs +117 -191
  12. package/dist/{betterAuthOpenApi-DwxtK3uG.mjs → betterAuthOpenApi--M_i87dQ.mjs} +1 -1
  13. package/dist/buildHandler-olo-gt94.mjs +610 -0
  14. package/dist/cache/index.mjs +3 -3
  15. package/dist/cli/commands/describe.d.mts +89 -13
  16. package/dist/cli/commands/describe.mjs +56 -2
  17. package/dist/cli/commands/docs.mjs +2 -2
  18. package/dist/cli/commands/generate.mjs +147 -48
  19. package/dist/cli/commands/init.d.mts +13 -0
  20. package/dist/cli/commands/init.mjs +130 -87
  21. package/dist/cli/commands/introspect.mjs +8 -1
  22. package/dist/context/index.mjs +1 -1
  23. package/dist/core/index.d.mts +3 -3
  24. package/dist/core/index.mjs +5 -5
  25. package/dist/core-D72ia0EH.mjs +1399 -0
  26. package/dist/{createActionRouter-CIKOcNA7.mjs → createActionRouter-CEvzKcy8.mjs} +7 -20
  27. package/dist/createAggregationRouter-CyecOxnO.mjs +114 -0
  28. package/dist/{createApp-C9bRrqlX.mjs → createApp-XX2-N0Yd.mjs} +28 -22
  29. package/dist/{defineEvent-D1Ky9M1D.mjs → defineEvent-D5h7EvAx.mjs} +1 -1
  30. package/dist/docs/index.d.mts +1 -1
  31. package/dist/docs/index.mjs +2 -2
  32. package/dist/{elevation-DOFoxoDs.mjs → elevation-DgoeTyfX.mjs} +1 -1
  33. package/dist/errorHandler-Bk-AGhkU.mjs +174 -0
  34. package/dist/errorHandler-DFr45ZG4.d.mts +45 -0
  35. package/dist/errors-j4aJm1Wg.mjs +184 -0
  36. package/dist/{eventPlugin-Cts2-Tfj.mjs → eventPlugin-CaKTYkYM.mjs} +28 -4
  37. package/dist/{eventPlugin-DDJoNEPL.d.mts → eventPlugin-qXpqTebY.d.mts} +24 -1
  38. package/dist/events/index.d.mts +6 -6
  39. package/dist/events/index.mjs +11 -35
  40. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  41. package/dist/events/transports/redis.d.mts +1 -1
  42. package/dist/factory/index.d.mts +2 -2
  43. package/dist/factory/index.mjs +2 -2
  44. package/dist/{fields-BRjxOAFp.d.mts → fields-COhcH3fk.d.mts} +23 -2
  45. package/dist/hooks/index.d.mts +1 -1
  46. package/dist/hooks/index.mjs +1 -1
  47. package/dist/idempotency/index.d.mts +1 -1
  48. package/dist/idempotency/index.mjs +1 -20
  49. package/dist/idempotency/redis.mjs +1 -1
  50. package/dist/{index-rHjXmJar.d.mts → index-BTqLEvhu.d.mts} +163 -3
  51. package/dist/{index-CXXRbnf8.d.mts → index-BtW7qYwa.d.mts} +660 -326
  52. package/dist/{index-m8mOOlFW.d.mts → index-Ds61mrJE.d.mts} +50 -4
  53. package/dist/{index-D9t1KNaB.d.mts → index-Dz5IKsrE.d.mts} +360 -219
  54. package/dist/index.d.mts +6 -7
  55. package/dist/index.mjs +9 -10
  56. package/dist/integrations/event-gateway.d.mts +1 -1
  57. package/dist/integrations/event-gateway.mjs +1 -1
  58. package/dist/integrations/index.d.mts +1 -1
  59. package/dist/integrations/mcp/index.d.mts +2 -2
  60. package/dist/integrations/mcp/index.mjs +1 -1
  61. package/dist/integrations/mcp/testing.d.mts +1 -1
  62. package/dist/integrations/mcp/testing.mjs +1 -1
  63. package/dist/integrations/streamline.d.mts +60 -11
  64. package/dist/integrations/streamline.mjs +75 -85
  65. package/dist/integrations/websocket.mjs +2 -8
  66. package/dist/middleware/index.d.mts +1 -1
  67. package/dist/middleware/index.mjs +2 -2
  68. package/dist/migrations/index.d.mts +23 -3
  69. package/dist/migrations/index.mjs +0 -7
  70. package/dist/{multipartBody-CvTR1Un6.mjs → multipartBody-BOvVSVCD.mjs} +11 -8
  71. package/dist/{openapi-D7G1V7ex.mjs → openapi-CiOMVW1p.mjs} +143 -13
  72. package/dist/org/index.d.mts +2 -2
  73. package/dist/org/index.mjs +1 -1
  74. package/dist/permissions/index.d.mts +3 -3
  75. package/dist/permissions/index.mjs +3 -3
  76. package/dist/{permissions-gd_aUWrR.mjs → permissions-ohQyv50e.mjs} +404 -176
  77. package/dist/{pipe-DVoIheVC.mjs → pipe-Zr0KXjQe.mjs} +1 -1
  78. package/dist/pipeline/index.d.mts +1 -1
  79. package/dist/pipeline/index.mjs +1 -1
  80. package/dist/plugins/index.d.mts +16 -31
  81. package/dist/plugins/index.mjs +33 -13
  82. package/dist/plugins/response-cache.mjs +1 -1
  83. package/dist/plugins/tracing-entry.mjs +1 -1
  84. package/dist/presets/filesUpload.d.mts +4 -4
  85. package/dist/presets/filesUpload.mjs +6 -9
  86. package/dist/presets/index.d.mts +1 -1
  87. package/dist/presets/index.mjs +1 -1
  88. package/dist/presets/multiTenant.d.mts +1 -1
  89. package/dist/presets/multiTenant.mjs +2 -2
  90. package/dist/presets/search.d.mts +2 -2
  91. package/dist/presets/search.mjs +6 -8
  92. package/dist/{presets-Z7P5w4gF.mjs → presets-BbkjdPeH.mjs} +6 -28
  93. package/dist/{queryCachePlugin-Bq6bO6vc.mjs → queryCachePlugin-m1XsgAIJ.mjs} +3 -3
  94. package/dist/{redis-stream-xTGxB2bm.d.mts → redis-stream-D6HzR1Z_.d.mts} +1 -1
  95. package/dist/registry/index.d.mts +1 -1
  96. package/dist/registry/index.mjs +2 -2
  97. package/dist/{replyHelpers-ByllIXXV.mjs → replyHelpers-CK-FNO8E.mjs} +3 -21
  98. package/dist/{resourceToTools-CxNmI6xF.mjs → resourceToTools-C5coh64w.mjs} +224 -71
  99. package/dist/{routerShared-BqLRb5l7.mjs → routerShared-D6_fEGHh.mjs} +40 -36
  100. package/dist/{schemaIR-Dy2p4MxS.mjs → schemaIR-7Vl611Qs.mjs} +1 -1
  101. package/dist/schemas/index.d.mts +100 -30
  102. package/dist/schemas/index.mjs +86 -29
  103. package/dist/scim/index.d.mts +264 -0
  104. package/dist/scim/index.mjs +963 -0
  105. package/dist/scope/index.d.mts +3 -3
  106. package/dist/scope/index.mjs +4 -4
  107. package/dist/{sse-V7aXc3bW.mjs → sse-Bz-5ZeTt.mjs} +1 -1
  108. package/dist/{store-helpers-Cp4uKC1U.mjs → store-helpers-BkIN9-vu.mjs} +1 -1
  109. package/dist/testing/index.d.mts +2 -8
  110. package/dist/testing/index.mjs +16 -24
  111. package/dist/types/index.d.mts +4 -4
  112. package/dist/{types-D7KpfiL1.d.mts → types-BvqwCCSx.d.mts} +73 -25
  113. package/dist/{types-DDyTPc6y.d.mts → types-CTYvcwHe.d.mts} +195 -1
  114. package/dist/{types-AOD8fxIw.mjs → types-C_s5moIu.mjs} +117 -1
  115. package/dist/{types-BQ9TJQNy.d.mts → types-DQHFc8PM.d.mts} +1 -1
  116. package/dist/utils/index.d.mts +2 -2
  117. package/dist/utils/index.mjs +5 -5
  118. package/dist/{utils-CcYTj09l.mjs → utils-_h9B3c57.mjs} +1269 -1334
  119. package/dist/{versioning-DsglKfM_.d.mts → versioning-DTTvc80y.d.mts} +1 -1
  120. package/package.json +24 -34
  121. package/skills/arc/SKILL.md +147 -51
  122. package/skills/arc/references/agent-auth.md +238 -0
  123. package/skills/arc/references/api-reference.md +187 -0
  124. package/skills/arc/references/auth.md +354 -7
  125. package/skills/arc/references/enterprise-auth.md +94 -0
  126. package/skills/arc/references/events.md +8 -6
  127. package/skills/arc/references/mcp.md +2 -2
  128. package/skills/arc/references/multi-tenancy.md +11 -2
  129. package/skills/arc/references/production.md +10 -9
  130. package/skills/arc/references/scim.md +247 -0
  131. package/skills/arc/references/testing.md +1 -1
  132. package/skills/arc-code-review/SKILL.md +141 -0
  133. package/skills/arc-code-review/references/anti-patterns.md +911 -0
  134. package/skills/arc-code-review/references/arc-cheatsheet.md +380 -0
  135. package/skills/arc-code-review/references/migration-recipes.md +700 -0
  136. package/skills/arc-code-review/references/mongokit-migration.md +386 -0
  137. package/skills/arc-code-review/references/scaffolding.md +230 -0
  138. package/skills/arc-code-review/references/severity.md +127 -0
  139. package/dist/EventTransport-BFQjw9pB.mjs +0 -133
  140. package/dist/EventTransport-CYNUXdCJ.d.mts +0 -293
  141. package/dist/adapters/index.d.mts +0 -3
  142. package/dist/adapters/index.mjs +0 -2
  143. package/dist/adapters-DUUiiimH.mjs +0 -964
  144. package/dist/auth/mongoose.d.mts +0 -191
  145. package/dist/auth/mongoose.mjs +0 -73
  146. package/dist/core-CbcQRIch.mjs +0 -1054
  147. package/dist/errorHandler-BQm8ZxTK.mjs +0 -173
  148. package/dist/errorHandler-DEWmGWPz.d.mts +0 -114
  149. package/dist/errors-D5c-5BJL.mjs +0 -232
  150. package/dist/index-Rg8axYPz.d.mts +0 -370
  151. /package/dist/{HookSystem-CGsMd6oK.mjs → HookSystem-Iiebom92.mjs} +0 -0
  152. /package/dist/{actionPermissions-sUUKDhtP.mjs → actionPermissions-CyUkQu6O.mjs} +0 -0
  153. /package/dist/{caching-CheW3m-S.mjs → caching-SM8gghN6.mjs} +0 -0
  154. /package/dist/{constants-BhY1OHoH.mjs → constants-Cxde4rpC.mjs} +0 -0
  155. /package/dist/{elevation-BQQXZ_VR.d.mts → elevation-BXOWoGCF.d.mts} +0 -0
  156. /package/dist/{keys-CARyUjiR.mjs → keys-CGcCbNyu.mjs} +0 -0
  157. /package/dist/{loadResources-CPpkyKfM.mjs → loadResources-DBMQg_Aj.mjs} +0 -0
  158. /package/dist/{memory-DikHSvWa.mjs → memory-UBydS5ku.mjs} +0 -0
  159. /package/dist/{metrics-Csh4nsvv.mjs → metrics-Qnvwc-LQ.mjs} +0 -0
  160. /package/dist/{pluralize-CWP6MB39.mjs → pluralize-DQgqgifU.mjs} +0 -0
  161. /package/dist/{registry-D63ee7fl.mjs → registry-I-ogLgL9.mjs} +0 -0
  162. /package/dist/{requestContext-C5XeK3VA.mjs → requestContext-SSaaTgW8.mjs} +0 -0
  163. /package/dist/{schemaConverter-B0oKLuqI.mjs → schemaConverter-De34B1ZG.mjs} +0 -0
  164. /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-BzkXkvVv.mjs} +0 -0
  165. /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
  166. /package/dist/{versioning-CGPjkqAg.mjs → versioning-BUrT5aP4.mjs} +0 -0
@@ -1,5 +1,5 @@
1
- import { _ as isAuthenticated, a as getClientId, b as isOrgInScope, c as getOrgRoles, d as getScopeContextMap, f as getServiceScopes, g as hasOrgAccess, h as getUserRoles, i as getAncestorOrgIds, l as getRequestScope, m as getUserId, n as PUBLIC_SCOPE, o as getOrgContext, p as getTeamId, r as RequestScope, s as getOrgId, t as AUTHENTICATED_SCOPE, u as getScopeContext, v as isElevated, x as isService, y as isMember } from "../types-DDyTPc6y.mjs";
2
- import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-BQQXZ_VR.mjs";
1
+ import { C as isOrgInScope, D as requireTeamId, E as requireOrgId, O as requireUserId, S as isMember, T as requireClientId, _ as getUserId, a as getAncestorOrgIds, b as isAuthenticated, c as getMandate, d as getOrgRoles, f as getRequestScope, g as getTeamId, h as getServiceScopes, i as RequestScope, l as getOrgContext, m as getScopeContextMap, n as Mandate, o as getClientId, p as getScopeContext, r as PUBLIC_SCOPE, s as getDPoPJkt, t as AUTHENTICATED_SCOPE, u as getOrgId, v as getUserRoles, w as isService, x as isElevated, y as hasOrgAccess } from "../types-CTYvcwHe.mjs";
2
+ import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-BXOWoGCF.mjs";
3
3
  import { FastifyReply, FastifyRequest } from "fastify";
4
4
 
5
5
  //#region src/scope/rateLimitKey.d.ts
@@ -30,4 +30,4 @@ interface ResolveOrgFromHeaderOptions {
30
30
  */
31
31
  declare function resolveOrgFromHeader(options: ResolveOrgFromHeaderOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
32
32
  //#endregion
33
- export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, resolveOrgFromHeader };
33
+ export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, type Mandate, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
@@ -1,6 +1,6 @@
1
- import { _ as isElevated, a as getOrgContext, b as isService, c as getRequestScope, d as getServiceScopes, f as getTeamId, g as isAuthenticated, h as hasOrgAccess, i as getClientId, l as getScopeContext, m as getUserRoles, n as PUBLIC_SCOPE, o as getOrgId, p as getUserId, r as getAncestorOrgIds, s as getOrgRoles, t as AUTHENTICATED_SCOPE, u as getScopeContextMap, v as isMember, y as isOrgInScope } from "../types-AOD8fxIw.mjs";
2
- import { n as normalizeRoles } from "../types-DV9WDfeg.mjs";
3
- import { n as elevation_default, t as elevationPlugin } from "../elevation-DOFoxoDs.mjs";
1
+ import { C as requireClientId, E as requireUserId, S as isService, T as requireTeamId, _ as hasOrgAccess, a as getDPoPJkt, b as isMember, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserRoles, h as getUserId, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as isAuthenticated, w as requireOrgId, x as isOrgInScope, y as isElevated } from "../types-C_s5moIu.mjs";
2
+ import { n as normalizeRoles } from "../types-D57iXYb8.mjs";
3
+ import { n as elevation_default, t as elevationPlugin } from "../elevation-DgoeTyfX.mjs";
4
4
  //#region src/scope/rateLimitKey.ts
5
5
  function createTenantKeyGenerator(opts) {
6
6
  if (opts?.strategy) return opts.strategy;
@@ -76,4 +76,4 @@ function resolveOrgFromHeader(options) {
76
76
  };
77
77
  }
78
78
  //#endregion
79
- export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, resolveOrgFromHeader };
79
+ export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
2
  import { arcLog } from "./logger/index.mjs";
3
- import { n as PUBLIC_SCOPE, o as getOrgId } from "./types-AOD8fxIw.mjs";
3
+ import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-C_s5moIu.mjs";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/plugins/sse.ts
6
6
  var sse_exports = /* @__PURE__ */ __exportAll({
@@ -1,4 +1,4 @@
1
- //#region src/adapters/store-helpers.ts
1
+ //#region src/utils/store-helpers.ts
2
2
  /**
3
3
  * Classify an error thrown by `getOne` / `getById` / `update` as a
4
4
  * "document not found" miss. Mongokit uses `status: 404`, Prisma uses
@@ -1,5 +1,5 @@
1
- import { B as ResourceDefinition, Pt as AnyRecord } from "../index-CXXRbnf8.mjs";
2
- import { d as ResourceLike, r as CreateAppOptions } from "../types-D7KpfiL1.mjs";
1
+ import { V as ResourceDefinition, Wt as AnyRecord } from "../index-BtW7qYwa.mjs";
2
+ import { d as ResourceLike, r as CreateAppOptions } from "../types-BvqwCCSx.mjs";
3
3
  import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
4
4
  import { FastifyInstance, FastifyServerOptions } from "fastify";
5
5
  import { Mock } from "vitest";
@@ -349,12 +349,6 @@ declare class HttpTestHarness<T = unknown> {
349
349
  private resource;
350
350
  private optionsOrGetter;
351
351
  private enabledRoutes;
352
- /**
353
- * Update verbs exercised by this harness instance. One entry for single-method
354
- * resources (`"PATCH"` or `"PUT"`), two for `updateMethod: "both"` so both
355
- * verbs are covered — the framework mounts both, and the harness should
356
- * probe both.
357
- */
358
352
  private updateMethods;
359
353
  constructor(resource: ResourceDefinition<unknown>, optionsOrGetter: OptionsOrGetter<T>);
360
354
  private getOptions;
@@ -1,4 +1,4 @@
1
- import { t as CRUD_OPERATIONS } from "../constants-BhY1OHoH.mjs";
1
+ import { t as CRUD_OPERATIONS } from "../constants-Cxde4rpC.mjs";
2
2
  import { runStorageContract } from "./storageContract.mjs";
3
3
  import Fastify from "fastify";
4
4
  import { afterAll, describe, expect, it, vi } from "vitest";
@@ -49,13 +49,13 @@ function expectArc(response) {
49
49
  },
50
50
  ok(status = 200) {
51
51
  expect(response.statusCode, `expected 2xx (${status}) but got ${response.statusCode}. Body: ${response.body.slice(0, 200)}`).toBe(status);
52
- expect(getBody().success).toBe(true);
52
+ getBody();
53
53
  return assertion;
54
54
  },
55
55
  failed(status) {
56
56
  if (status !== void 0) expect(response.statusCode).toBe(status);
57
57
  else expect(response.statusCode).toBeGreaterThanOrEqual(400);
58
- expect(getBody().success).toBe(false);
58
+ expect(getBody().code, "expected ErrorContract.code on failed response").toBeDefined();
59
59
  return assertion;
60
60
  },
61
61
  unauthorized() {
@@ -74,7 +74,8 @@ function expectArc(response) {
74
74
  return assertion.failed(409);
75
75
  },
76
76
  hasData() {
77
- expect(getBody().data, "expected body.data to be defined").toBeDefined();
77
+ const body = getBody();
78
+ expect(Object.keys(body).length > 0, "expected response body to be defined").toBe(true);
78
79
  return assertion;
79
80
  },
80
81
  hasStatus(status) {
@@ -82,22 +83,17 @@ function expectArc(response) {
82
83
  return assertion;
83
84
  },
84
85
  hidesField(field) {
85
- const data = getBody().data;
86
- expect(data, "expected body.data to be defined before field check").toBeDefined();
87
- expect(data, `expected field '${field}' to be hidden from response.data`).not.toHaveProperty(field);
86
+ expect(getBody(), `expected field '${field}' to be hidden from response`).not.toHaveProperty(field);
88
87
  return assertion;
89
88
  },
90
89
  showsField(field) {
91
- const data = getBody().data;
92
- expect(data, "expected body.data to be defined before field check").toBeDefined();
93
- expect(data, `expected field '${field}' on response.data`).toHaveProperty(field);
90
+ expect(getBody(), `expected field '${field}' on response`).toHaveProperty(field);
94
91
  return assertion;
95
92
  },
96
93
  paginated(expected) {
97
94
  const body = getBody();
98
- expect(body.success).toBe(true);
99
- const docs = body.docs ?? (Array.isArray(body.data) ? body.data : void 0);
100
- expect(Array.isArray(docs), "expected `docs[]` (or `data[]`) on paginated response").toBe(true);
95
+ const data = body.data;
96
+ expect(Array.isArray(data), "expected `data[]` array on paginated response").toBe(true);
101
97
  if (expected?.page !== void 0) expect(body.page).toBe(expected.page);
102
98
  if (expected?.limit !== void 0) expect(body.limit).toBe(expected.limit);
103
99
  if (expected?.total !== void 0) expect(body.total).toBe(expected.total);
@@ -107,8 +103,8 @@ function expectArc(response) {
107
103
  },
108
104
  hasError(matcher) {
109
105
  const body = getBody();
110
- const errorField = body.error ?? body.message;
111
- expect(errorField, "expected body.error or body.message to be set").toBeDefined();
106
+ const errorField = body.message ?? body.error;
107
+ expect(errorField, "expected body.message (or body.error) to be set").toBeDefined();
112
108
  if (typeof matcher === "string") expect(errorField).toBe(matcher);
113
109
  else expect(errorField).toMatch(matcher);
114
110
  return assertion;
@@ -520,12 +516,6 @@ var HttpTestHarness = class {
520
516
  resource;
521
517
  optionsOrGetter;
522
518
  enabledRoutes;
523
- /**
524
- * Update verbs exercised by this harness instance. One entry for single-method
525
- * resources (`"PATCH"` or `"PUT"`), two for `updateMethod: "both"` so both
526
- * verbs are covered — the framework mounts both, and the harness should
527
- * probe both.
528
- */
529
519
  updateMethods;
530
520
  constructor(resource, optionsOrGetter) {
531
521
  this.resource = resource;
@@ -588,7 +578,7 @@ var HttpTestHarness = class {
588
578
  expect(res.statusCode).toBe(200);
589
579
  const body = JSON.parse(res.body);
590
580
  expect(body.success).toBe(true);
591
- const list = body.data ?? body.docs;
581
+ const list = body.data ?? body.data;
592
582
  expect(Array.isArray(list)).toBe(true);
593
583
  });
594
584
  if (enabledRoutes.has("get")) {
@@ -792,7 +782,7 @@ function createMockRepository(overrides = {}) {
792
782
  return {
793
783
  getAll: vi.fn().mockResolvedValue({
794
784
  method: "offset",
795
- docs: [],
785
+ data: [],
796
786
  total: 0,
797
787
  page: 1,
798
788
  limit: 20,
@@ -822,6 +812,8 @@ function createMockRepository(overrides = {}) {
822
812
  acknowledged: true,
823
813
  deletedCount: 0
824
814
  }),
815
+ claim: vi.fn().mockResolvedValue(null),
816
+ claimVersion: vi.fn().mockResolvedValue(null),
825
817
  getBySlug: vi.fn().mockResolvedValue(null),
826
818
  getDeleted: vi.fn().mockResolvedValue([]),
827
819
  restore: vi.fn().mockResolvedValue(null),
@@ -1081,7 +1073,7 @@ function pickDefaultAuth(authMode, callerAuth) {
1081
1073
  };
1082
1074
  }
1083
1075
  async function createTestApp(options = {}) {
1084
- const { createApp } = await import("../createApp-C9bRrqlX.mjs").then((n) => n.r);
1076
+ const { createApp } = await import("../createApp-XX2-N0Yd.mjs").then((n) => n.r);
1085
1077
  const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
1086
1078
  let dbHandle;
1087
1079
  let dbUri;
@@ -1,5 +1,5 @@
1
- import { $ as OpenApiSchemas, A as RequestIdOptions, At as Authenticator, Bt as UserOrganization, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, Ft as ApiResponse, G as ActionsMap, H as ActionDefinition, I as MiddlewareHandler, It as ArcRequest, J as CrudRouteKey, K as ArcFieldRule, L as RequestWithExtras, Lt as JWTPayload, M as EventsDecorator, Mt as JwtContext, N as FastifyRequestExtras, Nt as TokenPair, O as HealthOptions, Ot as AuthHelpers, P as FastifyWithAuth, Pt as AnyRecord, Q as MiddlewareConfig, Rt as ObjectId, S as QueryParserInterface, T as CrudRouterOptions, U as ActionEntry, W as ActionHandlerFn, X as EventDefinition, Y as CrudSchemas, Z as FieldRule, _ as ControllerQueryOptions, _t as IControllerResponse, a as InferAdapterDoc, at as ResourceConfig, b as ParsedQuery, c as TypedController, ct as ResourcePermissions, d as PaginationResult, dt as RouteMethod, et as PresetFunction, f as IntrospectionData, ft as RouteSchemaOptions, g as ArcInternalMetadata, gt as IController, h as ResourceMetadata, ht as FastifyHandler, i as ValidationResult, it as ResourceCacheConfig, j as ArcDecorator, jt as AuthenticatorContext, k as IntrospectionPluginOptions, kt as AuthPluginOptions, l as TypedRepository, lt as RouteDefinition, m as RegistryStats, mt as ControllerLike, n as ConfigError, nt as PresetResult, o as InferDocType, ot as ResourceHookContext, p as RegistryEntry, pt as ControllerHandler, q as CrudController, r as ValidateOptions, rt as RateLimitConfig, s as InferResourceDoc, st as ResourceHooks, t as RouteHandlerMethod, tn as BaseControllerOptions, tt as PresetHook, u as TypedResourceConfig, ut as RouteMcpConfig, v as LookupOption, vt as IRequestContext, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, yt as RouteHandler, zt as UserLike } from "../index-CXXRbnf8.mjs";
2
- import { r as RequestScope } from "../types-DDyTPc6y.mjs";
3
- import { c as PermissionCheck, d as UserBase, l as PermissionContext, u as PermissionResult } from "../fields-BRjxOAFp.mjs";
4
- import { n as ElevationOptions, t as ElevationEvent } from "../elevation-BQQXZ_VR.mjs";
1
+ import { $ as OpenApiSchemas, A as RequestIdOptions, Bt as Authenticator, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, G as ActionsMap, Gt as ApiResponse, H as ActionDefinition, Ht as JwtContext, I as MiddlewareHandler, J as CrudRouteKey, Jt as ObjectId, K as ArcFieldRule, Kt as ArcRequest, L as RequestWithExtras, M as EventsDecorator, N as FastifyRequestExtras, O as HealthOptions, P as FastifyWithAuth, Q as MiddlewareConfig, Rt as AuthHelpers, S as QueryParserInterface, T as CrudRouterOptions, U as ActionEntry, Ut as TokenPair, Vt as AuthenticatorContext, W as ActionHandlerFn, Wt as AnyRecord, X as EventDefinition, Xt as UserOrganization, Y as CrudSchemas, Yt as UserLike, Z as FieldRule, _ as ControllerQueryOptions, _t as IControllerResponse, a as InferAdapterDoc, at as ResourceConfig, b as ParsedQuery, c as TypedController, ct as ResourcePermissions, d as PaginationResult, dt as RouteMethod, et as PresetFunction, f as IntrospectionData, ft as RouteSchemaOptions, g as ArcInternalMetadata, gt as IController, h as ResourceMetadata, ht as FastifyHandler, i as ValidationResult, it as ResourceCacheConfig, j as ArcDecorator, k as IntrospectionPluginOptions, l as TypedRepository, lt as RouteDefinition, m as RegistryStats, mt as ControllerLike, n as ConfigError, nt as PresetResult, o as InferDocType, on as BaseControllerOptions, ot as ResourceHookContext, p as RegistryEntry, pt as ControllerHandler, q as CrudController, qt as JWTPayload, r as ValidateOptions, rt as RateLimitConfig, s as InferResourceDoc, st as ResourceHooks, t as RouteHandlerMethod, tt as PresetHook, u as TypedResourceConfig, ut as RouteMcpConfig, v as LookupOption, vt as IRequestContext, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, yt as RouteHandler, zt as AuthPluginOptions } from "../index-BtW7qYwa.mjs";
2
+ import { i as RequestScope } from "../types-CTYvcwHe.mjs";
3
+ import { c as PermissionCheck, d as UserBase, l as PermissionContext, u as PermissionResult } from "../fields-COhcH3fk.mjs";
4
+ import { n as ElevationOptions, t as ElevationEvent } from "../elevation-BXOWoGCF.mjs";
5
5
  export { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ApiResponse, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResourceCacheConfig, ResourceConfig, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult };
@@ -1,12 +1,12 @@
1
- import { At as Authenticator } from "./index-CXXRbnf8.mjs";
2
1
  import { r as CacheStore } from "./interface-beEtJyWM.mjs";
3
- import { n as ElevationOptions } from "./elevation-BQQXZ_VR.mjs";
4
- import { o as EventTransport } from "./EventTransport-CYNUXdCJ.mjs";
2
+ import { Bt as Authenticator } from "./index-BtW7qYwa.mjs";
3
+ import { n as ElevationOptions } from "./elevation-BXOWoGCF.mjs";
4
+ import { a as EventTransport } from "./EventTransport-CT_52aWU.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "./externalPaths-BD5nw6St.mjs";
6
6
  import { r as QueryCachePluginOptions } from "./queryCachePlugin-CqMdLI2-.mjs";
7
- import { t as EventPluginOptions } from "./eventPlugin-DDJoNEPL.mjs";
8
- import { f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-DsglKfM_.mjs";
9
- import { t as ErrorHandlerOptions } from "./errorHandler-DEWmGWPz.mjs";
7
+ import { t as EventPluginOptions } from "./eventPlugin-qXpqTebY.mjs";
8
+ import { f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-DTTvc80y.mjs";
9
+ import { t as ErrorHandlerOptions } from "./errorHandler-DFr45ZG4.mjs";
10
10
  import { r as IdempotencyStore } from "./interface-DfLGcus7.mjs";
11
11
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
12
12
 
@@ -682,7 +682,20 @@ interface CreateAppOptions {
682
682
  keywords?: string[];
683
683
  };
684
684
  /**
685
- * Enable `reply.ok()`, `reply.fail()`, `reply.paginated()` response helpers.
685
+ * Enable `reply.sendList()` + `reply.stream()` response decorators.
686
+ *
687
+ * Arc emits raw data on success — HTTP status discriminates, no
688
+ * `{ success, data }` envelope — so single-doc handlers just
689
+ * `return doc` or `reply.send(doc)` and errors throw `ArcError`
690
+ * (the global handler serializes to `ErrorContract`). The two
691
+ * decorators below cover the cases that DO need framework support:
692
+ *
693
+ * - `sendList(input)` normalises any kit-shaped paginated/array
694
+ * result to the canonical wire shape via repo-core's
695
+ * `toCanonicalList` so the server and `@classytic/arc-next`
696
+ * typed client share one declaration.
697
+ * - `stream(source, options)` sets `Content-Type` /
698
+ * `Content-Disposition` headers for file downloads in one call.
686
699
  *
687
700
  * Default: `false` (opt-in).
688
701
  *
@@ -690,32 +703,64 @@ interface CreateAppOptions {
690
703
  * ```typescript
691
704
  * const app = await createApp({ replyHelpers: true });
692
705
  *
693
- * // Then in any handler:
694
- * return reply.ok({ name: 'MacBook' }); // 200 { success: true, data: { ... } }
695
- * return reply.ok(product, 201); // → 201 { success: true, data: { ... } }
696
- * return reply.fail('Not found', 404); // → 404 { success: false, error: '...' }
697
- * return reply.fail(['err1', 'err2'], 422); // → 422 { success: false, errors: [...] }
698
- * return reply.paginated({ docs, total, page, limit });
706
+ * // List endpoint kit-shaped pagination → canonical wire shape
707
+ * app.get('/orders', async (req, reply) => {
708
+ * const result = await orderRepo.getAll(req.query);
709
+ * return reply.sendList(result);
710
+ * });
711
+ *
712
+ * // File download
713
+ * app.get('/orders/export.csv', async (req, reply) => {
714
+ * return reply.stream(csvReadable, {
715
+ * contentType: 'text/csv',
716
+ * filename: 'orders.csv',
717
+ * });
718
+ * });
699
719
  * ```
700
720
  */
701
721
  replyHelpers?: boolean;
702
722
  /**
703
- * Auto-convert BigInt values to Number in all JSON responses.
704
- *
705
- * When `true`, Arc adds a `preSerialization` hook that converts BigInt values
706
- * to Number before JSON serialization. Without this, `JSON.stringify` throws
707
- * on BigInt values (e.g., from financial libraries like fin-io).
708
- *
709
- * Default: `false` (opt-in most apps don't use BigInt).
723
+ * Serialize `bigint` values in JSON responses.
724
+ *
725
+ * `JSON.stringify` throws on `bigint` by default. This option installs a
726
+ * `preSerialization` hook that converts them on the way out:
727
+ *
728
+ * - `false` (default) — no conversion. JSON serialization throws if a
729
+ * `bigint` reaches the wire. Safest when no codepath produces bigints.
730
+ * - `'string'` — **recommended for IDs / money / counters / ledgers.**
731
+ * Converts every `bigint` to a decimal string. Lossless: every digit
732
+ * survives the wire. Clients parse with `BigInt(value)` to reconstitute.
733
+ * - `'number'` — converts every `bigint` to `Number(value)`.
734
+ * **Lossy above 2^53 - 1 (`Number.MAX_SAFE_INTEGER` = 9007199254740991).**
735
+ * Use ONLY when you've audited the value range — e.g. small enums,
736
+ * bounded counters that physically can't exceed the safe range. For
737
+ * anything that could be an ID, monetary amount, or unbounded counter,
738
+ * `Number()` corruption silently rounds digits and there is no
739
+ * recovery. arc emits a one-shot startup warning when you opt into
740
+ * this mode.
741
+ * - `true` — back-compat alias for `'number'`. Will be removed in a
742
+ * future major. Migrate to `'string'` (lossless) or explicit
743
+ * `'number'` (acknowledges the precision risk).
744
+ *
745
+ * Default: `false` (opt-in — most apps don't use bigint).
710
746
  *
711
747
  * @example
712
748
  * ```typescript
749
+ * // Lossless — recommended path
750
+ * const app = await createApp({
751
+ * serializeBigInt: 'string',
752
+ * });
753
+ * // { totalSatoshis: "9007199254740999" }
754
+ *
755
+ * // Lossy — only when value range is bounded
713
756
  * const app = await createApp({
714
- * serializeBigInt: true,
757
+ * serializeBigInt: 'number',
758
+ * });
759
+ * // { totalSatoshis: 9007199254740999 } // precision lost above MAX_SAFE_INTEGER
715
760
  * });
716
761
  * ```
717
762
  */
718
- serializeBigInt?: boolean;
763
+ serializeBigInt?: boolean | "number" | "string";
719
764
  /**
720
765
  * Resources to register automatically. Accepts two shapes:
721
766
  *
@@ -733,7 +778,7 @@ interface CreateAppOptions {
733
778
  * Arc's lifecycle contract:
734
779
  * ```
735
780
  * 1. Arc core (security, auth, events)
736
- * 2. plugins() ← infra (DB, SSE, docs)
781
+ * 2. plugins() ← infra (DB, SSE, data)
737
782
  * 3. bootstrap[] ← domain init (engines, singletons)
738
783
  * 4. resources resolution ← (factory form: call it here)
739
784
  * 5. resources registered ← plugins mounted on Fastify
@@ -757,6 +802,9 @@ interface CreateAppOptions {
757
802
  *
758
803
  * @example Factory with async-booted engine
759
804
  * ```ts
805
+ * import { createApp, defineResource } from '@classytic/arc';
806
+ * import { createMongooseAdapter } from '@classytic/mongokit/adapter';
807
+ *
760
808
  * const app = await createApp({
761
809
  * bootstrap: [async () => { await ensureCatalogEngine(); }],
762
810
  * resources: async () => {
@@ -867,7 +915,7 @@ interface CreateAppOptions {
867
915
  * Custom plugin registration — runs after Arc core (security, auth, events)
868
916
  * but before `bootstrap` and `resources`.
869
917
  *
870
- * Use this for infrastructure setup: database connections, OpenAPI docs,
918
+ * Use this for infrastructure setup: database connections, OpenAPI data,
871
919
  * webhook plugins, SSE wiring, etc.
872
920
  */
873
921
  plugins?: (fastify: FastifyInstance) => Promise<void>;
@@ -882,7 +930,7 @@ interface CreateAppOptions {
882
930
  * Boot order:
883
931
  * ```
884
932
  * 1. Arc core (security, auth, events)
885
- * 2. plugins() ← infra (DB, SSE, docs)
933
+ * 2. plugins() ← infra (DB, SSE, data)
886
934
  * 3. bootstrap[] ← domain init (singletons, event handlers)
887
935
  * 4. resources[] ← auto-discovered routes
888
936
  * ```
@@ -19,6 +19,93 @@
19
19
  * const globalRoles = getUserRoles(scope);
20
20
  * ```
21
21
  */
22
+ /**
23
+ * A capability mandate — declarative, time-boxed, optionally cap-bounded
24
+ * authorization for a single high-value action by an AI agent or service.
25
+ *
26
+ * Models the **2025 agent-payments / agent-authorization conventions**:
27
+ * - Google + Anthropic + Stripe **AP2** (Agent Payments Protocol)
28
+ * - Stripe **x402 / Agentic Commerce**
29
+ * - **MCP authorization** (RFC 9728 + RFC 9700 + RFC 9449)
30
+ *
31
+ * Where OAuth `scopes` answer "what can this client EVER do?", a mandate
32
+ * answers "what is THIS REQUEST authorized to do RIGHT NOW?" — narrower in
33
+ * capability, time, and value.
34
+ *
35
+ * Arc does **not** parse mandate JWTs / Verifiable Credentials — your
36
+ * authenticate callback does (one `jose.jwtVerify()` or `did-jwt-vc.verify()`
37
+ * call) and populates this object. Arc's `requireMandate(capability, opts)`
38
+ * permission helper reads it and validates the action against the mandate's
39
+ * declared bounds.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Inbound: Authorization: Mandate eyJhbGc...
44
+ * authenticate: async (request) => {
45
+ * const proof = request.headers['authorization']?.replace(/^Mandate /, '');
46
+ * const claims = await verifyMandate(proof); // host's verifier
47
+ * request.scope = {
48
+ * kind: 'service',
49
+ * clientId: claims.iss,
50
+ * organizationId: claims.org,
51
+ * scopes: claims.scope?.split(' '),
52
+ * mandate: {
53
+ * id: claims.jti,
54
+ * capability: claims.cap, // 'payment.charge' / 'inbox.send' / ...
55
+ * cap: claims.amount, // numeric ceiling
56
+ * expiresAt: claims.exp * 1000,
57
+ * parent: claims.parent, // delegated mandate chain
58
+ * audience: claims.aud, // resource id mandate is bound to
59
+ * },
60
+ * dpopJkt: claims.cnf?.jkt, // RFC 7638 — sender-constrained
61
+ * };
62
+ * return { id: claims.iss };
63
+ * };
64
+ * ```
65
+ */
66
+ interface Mandate {
67
+ /** Mandate identifier — typically the JWT `jti` claim. */
68
+ id: string;
69
+ /**
70
+ * Capability string the mandate authorizes. Hierarchical-dotted convention:
71
+ * `payment.charge`, `payment.refund`, `inbox.send`, `data.export`, etc.
72
+ * Match in `requireMandate(capability)`; treated as opaque otherwise.
73
+ */
74
+ capability: string;
75
+ /**
76
+ * Optional numeric ceiling for the action — interpretation is per-capability:
77
+ * for `payment.*` the upper bound on amount (in the mandate's currency),
78
+ * for `data.export` the row count, for `inbox.send` the message count.
79
+ * Validated by the host via `validateAmount` in `requireMandate`'s opts.
80
+ */
81
+ cap?: number;
82
+ /**
83
+ * Currency code (ISO 4217) when `cap` represents a monetary amount.
84
+ * Required for AP2 / x402 payment mandates; ignored for non-monetary caps.
85
+ */
86
+ currency?: string;
87
+ /** Expiry as epoch ms. `requireMandate` enforces with optional grace window. */
88
+ expiresAt?: number;
89
+ /**
90
+ * Parent mandate id when this mandate was delegated from another. Lets the
91
+ * audit chain reconstruct the full delegation graph (root user authorization
92
+ * → agent platform mandate → per-action sub-mandate).
93
+ */
94
+ parent?: string;
95
+ /**
96
+ * Resource the mandate is bound to (e.g., `invoice:INV-7`, `order:ORD-42`).
97
+ * When set, `requireMandate` can verify the inbound request targets the
98
+ * same resource — prevents a payment-mandate for one invoice being replayed
99
+ * against another.
100
+ */
101
+ audience?: string;
102
+ /**
103
+ * Free-form claims the host's mandate verifier extracted (intent text,
104
+ * user-presented confirmation, risk score, etc.). Surfaced in audit rows;
105
+ * arc takes no position on the shape.
106
+ */
107
+ meta?: Readonly<Record<string, unknown>>;
108
+ }
22
109
  /**
23
110
  * Request scope — 5 kinds, no ambiguity.
24
111
  *
@@ -84,6 +171,38 @@ type RequestScope = {
84
171
  scopes?: readonly string[]; /** App-defined scope dimensions — see `member.context` for details. */
85
172
  context?: Readonly<Record<string, string>>; /** Parent organizations — see `member.ancestorOrgIds` for details. */
86
173
  ancestorOrgIds?: readonly string[];
174
+ /**
175
+ * Capability mandate — narrows what the caller may do beyond OAuth `scopes`.
176
+ *
177
+ * Where `scopes` says "this client can write orders", a mandate says
178
+ * "this specific request can charge up to $50 on behalf of user U for
179
+ * invoice INV-7, expires at T". Models the AP2 / Stripe x402 / Google
180
+ * Agent Payments Protocol pattern for AI-agent-led actions on protected
181
+ * resources.
182
+ *
183
+ * Arc does **not** parse or verify mandate JWTs / VCs — your authenticate
184
+ * function does (using `jose` / `did-jwt-vc` / your provider's SDK) and
185
+ * populates this field. Arc's `requireMandate(capability, opts)`
186
+ * permission helper reads it.
187
+ *
188
+ * Optional — omit when the request isn't mandate-bound (most M2M auth
189
+ * still works fine via OAuth `scopes`).
190
+ */
191
+ mandate?: Readonly<Mandate>;
192
+ /**
193
+ * DPoP key thumbprint (RFC 7638 JWK SHA-256 thumbprint) bound to this
194
+ * service identity. When present, the inbound token is sender-constrained
195
+ * — replaying it from a different client (different key) MUST fail.
196
+ *
197
+ * Arc does **not** verify the DPoP proof header — your authenticate
198
+ * function does (one `jose.dpop.verify()` call) and populates this
199
+ * field on success. Arc's `requireDPoP()` permission helper checks
200
+ * presence + binding.
201
+ *
202
+ * Pairs with RFC 9449 (OAuth DPoP) and is mandatory for high-value
203
+ * agent flows in MCP authorization (RFC 9728 + RFC 9700) and AP2.
204
+ */
205
+ dpopJkt?: string;
87
206
  } | {
88
207
  kind: "elevated";
89
208
  userId?: string;
@@ -131,6 +250,37 @@ declare function getClientId(scope: RequestScope): string | undefined;
131
250
  * Returns an empty array for any non-service kind.
132
251
  */
133
252
  declare function getServiceScopes(scope: RequestScope): readonly string[];
253
+ /**
254
+ * Get the capability mandate from a service scope (when present).
255
+ *
256
+ * Returns the `Mandate` populated by your authenticate function on
257
+ * mandate-bound requests (AP2 / x402 / MCP authorization), or `undefined`
258
+ * for any non-service scope or non-mandate-bound service request.
259
+ *
260
+ * Use directly when you need the full mandate for custom logic; use
261
+ * `requireMandate(capability)` for the canonical permission-check path.
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * import { getMandate } from '@classytic/arc/scope';
266
+ *
267
+ * const mandate = getMandate(request.scope);
268
+ * if (mandate?.audience !== `invoice:${request.params.id}`) {
269
+ * throw new ForbiddenError('Mandate is bound to a different resource');
270
+ * }
271
+ * ```
272
+ */
273
+ declare function getMandate(scope: RequestScope): Readonly<Mandate> | undefined;
274
+ /**
275
+ * Get the DPoP key thumbprint (RFC 7638) from a service scope.
276
+ *
277
+ * When non-empty, the inbound credential is sender-constrained — replaying
278
+ * it from a different keypair must fail. Set by your authenticate function
279
+ * after a successful `jose.dpop.verify()` (or equivalent) call. Read via
280
+ * `requireDPoP()` permission helper or directly when implementing custom
281
+ * binding checks.
282
+ */
283
+ declare function getDPoPJkt(scope: RequestScope): string | undefined;
134
284
  /** Get org roles from scope (empty array if not a member) */
135
285
  declare function getOrgRoles(scope: RequestScope): string[];
136
286
  /** Get team ID from scope (only available on member kind) */
@@ -214,6 +364,50 @@ declare function isOrgInScope(scope: RequestScope, targetOrgId: string): boolean
214
364
  * ```
215
365
  */
216
366
  declare function getUserId(scope: RequestScope): string | undefined;
367
+ /**
368
+ * Throwing variant of `getOrgId`. Returns the organization id when the
369
+ * scope carries one (`member` / `service` / `elevated`); throws
370
+ * `OrgRequiredError` (HTTP 403, code `ORG_SELECTION_REQUIRED`) otherwise.
371
+ *
372
+ * Use whenever a handler/service path REQUIRES an org id — `requireOrgId`
373
+ * eliminates the `if (!orgId) throw …` boilerplate that drifts across
374
+ * handlers, and ensures every "missing org" produces the same error shape.
375
+ *
376
+ * @param hint Optional context for the error message (e.g. route name) —
377
+ * surfaces in the response body and in logs, so the failing path is
378
+ * visible without grepping for the throw site.
379
+ * @example
380
+ * ```typescript
381
+ * import { requireOrgId } from '@classytic/arc/scope';
382
+ *
383
+ * const orgId = requireOrgId(req.scope, 'POST /orders');
384
+ * await orderRepo.create({ ...req.body, organizationId: orgId });
385
+ * ```
386
+ */
387
+ declare function requireOrgId(scope: RequestScope, hint?: string): string;
388
+ /**
389
+ * Throwing variant of `getUserId`. Returns the user id when the scope is
390
+ * `authenticated` / `member` / `elevated` and carries one; throws
391
+ * `UnauthorizedError` (HTTP 401) otherwise.
392
+ *
393
+ * @param hint Optional route/context tag for the error message.
394
+ */
395
+ declare function requireUserId(scope: RequestScope, hint?: string): string;
396
+ /**
397
+ * Throwing variant of `getClientId`. Returns the service-account `clientId`
398
+ * when the scope kind is `service`; throws `UnauthorizedError` (HTTP 401)
399
+ * otherwise. Use for routes that are service-account-only.
400
+ */
401
+ declare function requireClientId(scope: RequestScope, hint?: string): string;
402
+ /**
403
+ * Throwing variant of `getTeamId`. Returns the team id when the scope is
404
+ * `member` and carries one; throws `OrgRequiredError` (HTTP 403) otherwise.
405
+ *
406
+ * Uses the org-context error class (not a separate `TeamRequiredError`)
407
+ * because the failure mode is the same shape as missing-org: the request
408
+ * lacks a tenancy dimension the route requires.
409
+ */
410
+ declare function requireTeamId(scope: RequestScope, hint?: string): string;
217
411
  /**
218
412
  * Get global user roles from scope (available on authenticated and member).
219
413
  * These are user-level roles (e.g. superadmin, finance-admin) distinct from
@@ -283,4 +477,4 @@ declare const PUBLIC_SCOPE: Readonly<RequestScope>;
283
477
  /** Default authenticated scope — used when user is logged in but no org */
284
478
  declare const AUTHENTICATED_SCOPE: Readonly<RequestScope>;
285
479
  //#endregion
286
- export { isAuthenticated as _, getClientId as a, isOrgInScope as b, getOrgRoles as c, getScopeContextMap as d, getServiceScopes as f, hasOrgAccess as g, getUserRoles as h, getAncestorOrgIds as i, getRequestScope as l, getUserId as m, PUBLIC_SCOPE as n, getOrgContext as o, getTeamId as p, RequestScope as r, getOrgId as s, AUTHENTICATED_SCOPE as t, getScopeContext as u, isElevated as v, isService as x, isMember as y };
480
+ export { isOrgInScope as C, requireTeamId as D, requireOrgId as E, requireUserId as O, isMember as S, requireClientId as T, getUserId as _, getAncestorOrgIds as a, isAuthenticated as b, getMandate as c, getOrgRoles as d, getRequestScope as f, getTeamId as g, getServiceScopes as h, RequestScope as i, getOrgContext as l, getScopeContextMap as m, Mandate as n, getClientId as o, getScopeContext as p, PUBLIC_SCOPE as r, getDPoPJkt as s, AUTHENTICATED_SCOPE as t, getOrgId as u, getUserRoles as v, isService as w, isElevated as x, hasOrgAccess as y };