@classytic/arc 2.34.0 → 2.36.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 (182) hide show
  1. package/dist/{BaseController-CbkuhNI2.mjs → BaseController-CTJKm6Cb.mjs} +95 -14
  2. package/dist/{EventTransport-tq4CEB-q.mjs → EventTransport-D23Uvhg4.mjs} +1 -1
  3. package/dist/{HookSystem-DqXkMXkB.mjs → HookSystem-DgwxNZTx.mjs} +6 -4
  4. package/dist/{ResourceRegistry-DoALUlOO.mjs → ResourceRegistry-DNuYVbmv.mjs} +2 -2
  5. package/dist/{pipeline-q69DZzgG.mjs → actionPermissions-D97DniUQ.mjs} +30 -7
  6. package/dist/audit/index.d.mts +2 -2
  7. package/dist/audit/index.mjs +2 -2
  8. package/dist/auth/audit.mjs +1 -1
  9. package/dist/auth/index.d.mts +2 -2
  10. package/dist/auth/index.mjs +6 -6
  11. package/dist/auth/redis-session.mjs +1 -1
  12. package/dist/{authorizationDecision-CMMLWmuZ.mjs → authorizationDecision-EsiY3nUu.mjs} +2 -2
  13. package/dist/{base-Csjc2oF-.d.mts → base-C-NL2fTi.d.mts} +16 -1
  14. package/dist/{buildHandler-BrdYrG_1.mjs → buildHandler-Dohd2JuY.mjs} +4 -4
  15. package/dist/cache/index.mjs +4 -4
  16. package/dist/{caching-BcGmvnHh.mjs → caching-CP3GLJ7U.mjs} +2 -2
  17. package/dist/cleanup/index.d.mts +2 -2
  18. package/dist/cleanup/index.mjs +4 -4
  19. package/dist/cli/commands/describe.d.mts +1 -1
  20. package/dist/cli/commands/describe.mjs +3 -3
  21. package/dist/cli/commands/docs.mjs +3 -3
  22. package/dist/cli/commands/doctor.mjs +1 -1
  23. package/dist/cli/commands/generate.mjs +2 -2
  24. package/dist/cli/commands/init.mjs +2 -2
  25. package/dist/cli/commands/introspect.mjs +2 -2
  26. package/dist/cli/index.mjs +3 -3
  27. package/dist/{constants-BxCoxmiN.mjs → constants-DUoKAGYD.mjs} +1 -1
  28. package/dist/context/index.d.mts +16 -1
  29. package/dist/context/index.mjs +64 -4
  30. package/dist/core/index.d.mts +2 -2
  31. package/dist/core/index.mjs +7 -8
  32. package/dist/{core-BYKa8wHu.mjs → core-DTXsiNTJ.mjs} +3 -3
  33. package/dist/{createActionRouter-D6T0jWhS.mjs → createActionRouter-CZz6JO93.mjs} +27 -64
  34. package/dist/{createAggregationRouter-QLV2PrWP.mjs → createAggregationRouter-DKrGv_EC.mjs} +7 -7
  35. package/dist/defineAggregation-CpQfcM8R.mjs +25 -0
  36. package/dist/{defineResource-ZhuLonLm.mjs → defineResource-DWzjyxb2.mjs} +62 -66
  37. package/dist/discovery/index.mjs +1 -1
  38. package/dist/docs/index.d.mts +1 -1
  39. package/dist/docs/index.mjs +3 -3
  40. package/dist/{elevation-B0tlY8y1.mjs → elevation-D_cmo80j.mjs} +4 -4
  41. package/dist/encryption/index.mjs +3 -3
  42. package/dist/{errorHandler-C7xBqHsI.mjs → errorHandler-2RT0hLSI.mjs} +3 -3
  43. package/dist/{errors-Cd_yy7aW.mjs → errors-DDKni6Tz.mjs} +1 -1
  44. package/dist/{eventPlugin-DSNEZWfV.d.mts → eventPlugin-B47sfCJ8.d.mts} +1 -1
  45. package/dist/{eventPlugin-Dar8qRaj.mjs → eventPlugin-c3YwGzz9.mjs} +5 -5
  46. package/dist/events/index.d.mts +2 -2
  47. package/dist/events/index.mjs +5 -5
  48. package/dist/events/transports/redis-stream-entry.mjs +1 -1
  49. package/dist/events/transports/redis.mjs +1 -1
  50. package/dist/factory/index.d.mts +5 -5
  51. package/dist/factory/index.mjs +5 -6
  52. package/dist/{fastify-augmentation-DVsTAy6X.d.mts → fastify-augmentation-Bk_9PoiV.d.mts} +3 -3
  53. package/dist/{fields-CojeUkS4.mjs → fields-DdP225wt.mjs} +1 -1
  54. package/dist/{generate-DiB0nHPR.mjs → generate-B1bKdcV9.mjs} +2 -2
  55. package/dist/{headers-CveNO9gR.mjs → headers-DKslnhey.mjs} +2 -2
  56. package/dist/{health-CasG2A28.mjs → health-k6NODS8f.mjs} +1 -1
  57. package/dist/hooks/index.d.mts +1 -1
  58. package/dist/hooks/index.mjs +2 -2
  59. package/dist/idempotency/index.mjs +22 -56
  60. package/dist/idempotency/redis.mjs +1 -1
  61. package/dist/{index-B-DvUzsr.d.mts → index-BtYoMip1.d.mts} +21 -3
  62. package/dist/{index-BcR2bFZQ.d.mts → index-C01wWGJt.d.mts} +33 -105
  63. package/dist/{index-C5NnMEKF.d.mts → index-C50BUxKs.d.mts} +1 -1
  64. package/dist/{index-CFAJEWCX.d.mts → index-RdpLP71b.d.mts} +7 -30
  65. package/dist/{index-ugr1tQlW.d.mts → index-eyg5WEcy.d.mts} +1 -1
  66. package/dist/index.d.mts +5 -5
  67. package/dist/index.mjs +11 -12
  68. package/dist/{init-mdJ-v4Xp.mjs → init-LuGxIbQ_.mjs} +1 -1
  69. package/dist/integrations/event-gateway.mjs +2 -2
  70. package/dist/integrations/index.d.mts +2 -2
  71. package/dist/integrations/index.mjs +1 -1
  72. package/dist/integrations/jobs/index.d.mts +1 -1
  73. package/dist/integrations/jobs/index.mjs +2 -2
  74. package/dist/integrations/mcp/index.d.mts +4 -4
  75. package/dist/integrations/mcp/index.mjs +2 -2
  76. package/dist/integrations/mcp/testing.d.mts +1 -1
  77. package/dist/integrations/mcp/testing.mjs +3 -3
  78. package/dist/integrations/streamline.mjs +3 -3
  79. package/dist/integrations/webhooks.d.mts +4 -2
  80. package/dist/integrations/webhooks.mjs +28 -27
  81. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  82. package/dist/integrations/websocket-redis.mjs +1 -1
  83. package/dist/integrations/websocket.mjs +1 -1
  84. package/dist/{interface-DznJrm9R.mjs → interface-B7awDx3b.mjs} +1 -1
  85. package/dist/{jsonBody-CfGH-GxO.mjs → jsonBody-CiHih3ZU.mjs} +2 -2
  86. package/dist/{keys-WeKvY1dE.mjs → keys-BcNl12HN.mjs} +1 -1
  87. package/dist/{loadResources-Bj-aSsXi.mjs → loadResources-DGlfRQc9.mjs} +2 -2
  88. package/dist/{loadResourcesFromEntry-DwaxNcE2.mjs → loadResourcesFromEntry-VYPGSas8.mjs} +2 -2
  89. package/dist/logger/index.d.mts +23 -4
  90. package/dist/logger/index.mjs +50 -7
  91. package/dist/{mcpPlugin-DX3PHD7K.mjs → mcpPlugin-NxSlc-Ks.mjs} +15 -16
  92. package/dist/{memory-qYSB7rMQ.mjs → memory-BU-C-MV3.mjs} +2 -2
  93. package/dist/{metrics-D7uchLjN.mjs → metrics-7_1GbJpl.mjs} +2 -2
  94. package/dist/middleware/index.d.mts +1 -1
  95. package/dist/middleware/index.mjs +3 -3
  96. package/dist/migrations/index.mjs +7 -4
  97. package/dist/{module-CwKsgZXp.mjs → module-CQNHzE7g.mjs} +1 -1
  98. package/dist/{multipartBody-4w-S3-PU.mjs → multipartBody-Bi5yqW0M.mjs} +1 -1
  99. package/dist/{openapi-CkV5mQ53.mjs → openapi-RpNbPBL-.mjs} +6 -6
  100. package/dist/outbox-admin/index.d.mts +2 -2
  101. package/dist/outbox-admin/index.mjs +4 -4
  102. package/dist/{outbox-module-D1QfHqO4.mjs → outbox-module-ClcFrq98.mjs} +6 -4
  103. package/dist/{outbox-module-C1Bs_FXF.d.mts → outbox-module-b1TyDGGi.d.mts} +24 -1
  104. package/dist/permissions/index.d.mts +2 -2
  105. package/dist/permissions/index.mjs +5 -5
  106. package/dist/{permissions-CP47jdi7.mjs → permissions-DgPrZsJh.mjs} +36 -9
  107. package/dist/{pipe-BzzoEhHj.mjs → pipe-Cjy1UiXX.mjs} +2 -2
  108. package/dist/pipeline/index.d.mts +1 -1
  109. package/dist/pipeline/index.mjs +2 -2
  110. package/dist/plugins/index.d.mts +92 -7
  111. package/dist/plugins/index.mjs +168 -17
  112. package/dist/plugins/response-cache.mjs +2 -2
  113. package/dist/plugins/tracing-entry.mjs +2 -2
  114. package/dist/{pluralize-B7xf8qZF.mjs → pluralize-B7m0dPm8.mjs} +1 -1
  115. package/dist/{preloadResources-g07ldtzh.d.mts → preloadResources-BuutdWgr.d.mts} +54 -270
  116. package/dist/{createApp-zR-DEesr.mjs → preloadResources-CjnW4iIX.mjs} +170 -34
  117. package/dist/presets/filesUpload.d.mts +3 -3
  118. package/dist/presets/filesUpload.mjs +5 -5
  119. package/dist/presets/index.d.mts +2 -2
  120. package/dist/presets/index.mjs +2 -2
  121. package/dist/presets/multiTenant.d.mts +1 -1
  122. package/dist/presets/multiTenant.mjs +3 -3
  123. package/dist/presets/search.d.mts +9 -47
  124. package/dist/presets/search.mjs +2 -2
  125. package/dist/{presets-BAPijK_A.mjs → presets-CsGg8B58.mjs} +4 -4
  126. package/dist/{projection-BKM0Ui-s.mjs → projection-koISNyQo.mjs} +2 -2
  127. package/dist/{queryCachePlugin-DhsZ0ZIc.mjs → queryCachePlugin-DHueQT9O.mjs} +5 -5
  128. package/dist/{rateLimitKey-XngqVNEV.mjs → rateLimitKey-Bma8VXDO.mjs} +1 -1
  129. package/dist/{realtime-G1TC6Yzc.mjs → realtime-BBoC91Bz.mjs} +9 -9
  130. package/dist/{rateLimit-D4-ASwsI.mjs → registerRoute-BTgORTlR.mjs} +114 -3
  131. package/dist/registry/index.d.mts +2 -2
  132. package/dist/registry/index.mjs +3 -3
  133. package/dist/{registry-CLxCR5LF.mjs → registry-BzvRCYS6.mjs} +15 -56
  134. package/dist/{renewingLease-BnWtbA2U.mjs → renewingLease-CgIGh0Ab.mjs} +14 -2
  135. package/dist/{replyHelpers-BSBVCwL3.mjs → replyHelpers-BN16KPHr.mjs} +2 -2
  136. package/dist/{requestContext-uYE35JxI.mjs → requestContext-B93dmpf0.mjs} +1 -1
  137. package/dist/{rolldown-runtime-DNg-KNCk.mjs → rolldown-runtime-JJWfp8rp.mjs} +1 -1
  138. package/dist/{runtimeCapabilities-Dm88DaTW.mjs → runtimeCapabilities-jK99Ciw_.mjs} +1 -1
  139. package/dist/{schedules-CAcZgia0.mjs → schedules-CKKEL21P.mjs} +6 -4
  140. package/dist/{schemaConverter-XH1RZRyu.mjs → schemaConverter-gg0_aUPF.mjs} +19 -32
  141. package/dist/{schemaIR-CDvclAew.mjs → schemaIR-CngYvpCF.mjs} +2 -2
  142. package/dist/schemas/index.mjs +1 -1
  143. package/dist/scim/index.mjs +2 -2
  144. package/dist/scope/index.d.mts +1 -1
  145. package/dist/scope/index.mjs +5 -5
  146. package/dist/{scope-BaWcqBvr.mjs → scope-B1RsHdjF.mjs} +4 -4
  147. package/dist/simpleEqualityMatcher-_9NAMHUD.mjs +43 -0
  148. package/dist/{sse-Cf0jUHsh.mjs → sse-Bxt03Hk5.mjs} +5 -5
  149. package/dist/{sseStream-Bvpi-UXQ.mjs → sseStream-BMHTLlhR.mjs} +2 -2
  150. package/dist/{store-helpers-B88p99Z2.mjs → store-helpers-Brt8OxWZ.mjs} +1 -1
  151. package/dist/{streaming-DsT1B5zJ.mjs → streaming-CiTJW8kZ.mjs} +1 -1
  152. package/dist/{subscribe-helpers-CLIOHsOK.mjs → subscribe-helpers-B4fNHRNc.mjs} +21 -49
  153. package/dist/sync/changelog.mjs +1 -1
  154. package/dist/testing/cleanupStoreContract.mjs +1 -1
  155. package/dist/testing/index.d.mts +6 -6
  156. package/dist/testing/index.mjs +10 -11
  157. package/dist/testing/outboxStoreContract.mjs +1 -1
  158. package/dist/testing/storageContract.mjs +1 -1
  159. package/dist/{transactionContext-BYtWnwAS.mjs → transactionContext-D2HwJM2z.mjs} +1 -1
  160. package/dist/{typeGuards-jjKJDN13.mjs → typeGuards-IE1laMDf.mjs} +1 -1
  161. package/dist/types/index.d.mts +3 -3
  162. package/dist/types/index.mjs +1 -1
  163. package/dist/types/storage.mjs +1 -1
  164. package/dist/{types-CoqIRrkY.mjs → types-B8lwJv9Q.mjs} +10 -53
  165. package/dist/{types-BfC83KsP.d.mts → types-BXnTOsXP.d.mts} +1 -1
  166. package/dist/{types-BcTumAz_.d.mts → types-DDD5RKTu.d.mts} +30 -87
  167. package/dist/{types-ChuShpCP.d.mts → types-Ddl9OjmE.d.mts} +18 -121
  168. package/dist/usage/index.d.mts +2 -2
  169. package/dist/usage/index.mjs +0 -0
  170. package/dist/{userHelpers-CWF5pbZD.mjs → userHelpers-xQAi5bPF.mjs} +1 -1
  171. package/dist/utils/index.d.mts +16 -42
  172. package/dist/utils/index.mjs +11 -11
  173. package/dist/{utils-h1V_sT-t.mjs → utils-DgNFsJ2c.mjs} +3 -3
  174. package/dist/{validate-CdQlz52F.mjs → validate-DI3L5V9w.mjs} +2 -2
  175. package/dist/{versioning-rZbyLVD-.mjs → versioning-COpAyfpZ.mjs} +3 -3
  176. package/package.json +8 -8
  177. package/dist/actionPermissions-CUeCmpUd.mjs +0 -25
  178. package/dist/defineAggregation-Nv-DwfOf.mjs +0 -48
  179. package/dist/preloadResources-BkyqeTDz.mjs +0 -46
  180. package/dist/registerRoute-Ba8v0PCa.mjs +0 -113
  181. package/dist/repositoryFilter-BANpz6BX.mjs +0 -78
  182. package/dist/simpleEqualityMatcher-Se8VrsaP.mjs +0 -69
@@ -1,61 +1,27 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { r as createEvent } from "./EventTransport-tq4CEB-q.mjs";
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
+ import { r as createEvent } from "./EventTransport-D23Uvhg4.mjs";
3
3
  //#region src/events/defineEvent.ts
4
4
  /**
5
- * defineEvent — Typed Event Definitions with Optional Schema Validation
5
+ * Typed event declarations, a registry for introspection, and `.create()` to
6
+ * build a `DomainEvent` with generated metadata.
6
7
  *
7
- * Provides:
8
- * 1. defineEvent() — declare an event with name, schema, version, description
9
- * 2. EventRegistry — catalog of all known events + payload validation
10
- * 3. .create() helper — build DomainEvent with auto-generated metadata
11
- *
12
- * The built-in validator checks: object type, required fields, and top-level
13
- * property types. It does NOT recurse into nested objects, validate arrays,
14
- * enums, patterns, formats, or $ref. This is intentional — it's a lightweight
15
- * guard, not a full JSON Schema engine.
16
- *
17
- * For full validation, pass a custom `validate` function to `createEventRegistry()`:
18
- *
19
- * @example
20
- * ```typescript
21
- * import Ajv from 'ajv';
22
- * const ajv = new Ajv();
23
- *
24
- * const registry = createEventRegistry({
25
- * validate: (schema, payload) => {
26
- * const valid = ajv.validate(schema, payload);
27
- * return valid
28
- * ? { valid: true }
29
- * : { valid: false, errors: ajv.errorsText().split(', ') };
30
- * },
31
- * });
32
- * ```
8
+ * The BUILT-IN validator is deliberately shallow: object type, required
9
+ * fields, and top-level property types only. It does not recurse, nor handle
10
+ * arrays, enums, patterns, formats, or `$ref` — it is a guard, not a JSON
11
+ * Schema engine. Pass `validate` to `createEventRegistry()` to delegate to AJV
12
+ * or similar when you need the real thing.
33
13
  *
34
14
  * @example
35
15
  * ```typescript
36
- * import { defineEvent, createEventRegistry } from '@classytic/arc/events';
37
- *
38
16
  * const OrderCreated = defineEvent({
39
17
  * name: 'order.created',
40
18
  * version: 1,
41
- * schema: {
42
- * type: 'object',
43
- * properties: {
44
- * orderId: { type: 'string' },
45
- * total: { type: 'number' },
46
- * },
47
- * required: ['orderId', 'total'],
48
- * },
19
+ * schema: { type: 'object', properties: { orderId: { type: 'string' } },
20
+ * required: ['orderId'] },
49
21
  * });
50
22
  *
51
- * // Type-safe event creation
52
- * const event = OrderCreated.create({ orderId: 'o-1', total: 100 });
23
+ * const event = OrderCreated.create({ orderId: 'o-1' });
53
24
  * await fastify.events.publish(event.type, event.payload, event.meta);
54
- *
55
- * // Registry for introspection + validation
56
- * const registry = createEventRegistry();
57
- * registry.register(OrderCreated);
58
- * const result = registry.validate('order.created', payload);
59
25
  * ```
60
26
  */
61
27
  /**
@@ -171,9 +137,15 @@ function validatePayload(payload, schema) {
171
137
  if (expectedType) {
172
138
  const value = record[key];
173
139
  const actualType = Array.isArray(value) ? "array" : typeof value;
174
- if (expectedType === "integer") {
175
- if (actualType !== "number" || !Number.isInteger(value)) errors.push(`Field '${key}': expected integer, got ${actualType}`);
176
- } else if (expectedType !== actualType) errors.push(`Field '${key}': expected ${expectedType}, got ${actualType}`);
140
+ /**
141
+ * `type` may be a UNION (`['string','null']`) — that is how every
142
+ * nullable field compiles. Comparing the array to a `typeof` string
143
+ * is never equal, so a union-typed field failed for EVERY value it
144
+ * allows, and the array stringified into the message as the
145
+ * self-contradictory "expected string,null, got string".
146
+ */
147
+ const allowed = Array.isArray(expectedType) ? expectedType : [expectedType];
148
+ if (!allowed.some((t) => t === "integer" ? actualType === "number" && Number.isInteger(value) : t === actualType)) errors.push(`Field '${key}': expected ${allowed.join(" | ")}, got ${actualType}`);
177
149
  }
178
150
  }
179
151
  }
@@ -1,3 +1,3 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  import { CursorExpiredError, MemoryChangeLogStore } from "@classytic/repo-core/sync";
3
3
  export { CursorExpiredError, MemoryChangeLogStore };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
3
  //#region src/testing/cleanupStoreContract.ts
4
4
  /**
@@ -1,10 +1,10 @@
1
1
 
2
- import { i as RequestScope } from "../types-ChuShpCP.mjs";
3
- import { l as PermissionCheck, t as AnyRecord } from "../base-Csjc2oF-.mjs";
4
- import { V as ResourceDefinition } from "../index-BcR2bFZQ.mjs";
5
- import { n as ArcModuleInput, o as ModuleDisposer, s as ModuleSetupContext } from "../types-BcTumAz_.mjs";
6
- import "../index-C5NnMEKF.mjs";
7
- import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-g07ldtzh.mjs";
2
+ import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
+ import { l as PermissionCheck, t as AnyRecord } from "../base-C-NL2fTi.mjs";
4
+ import { V as ResourceDefinition } from "../index-C01wWGJt.mjs";
5
+ import { n as ArcModuleInput, o as ModuleDisposer, s as ModuleSetupContext } from "../types-DDD5RKTu.mjs";
6
+ import "../index-C50BUxKs.mjs";
7
+ import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-BuutdWgr.mjs";
8
8
  import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
9
9
  import { StandardRepo } from "@classytic/repo-core/repository";
10
10
  import { FastifyInstance, FastifyServerOptions } from "fastify";
@@ -1,13 +1,12 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as CRUD_OPERATIONS } from "../constants-BxCoxmiN.mjs";
3
- import { g as getUserId } from "../types-CoqIRrkY.mjs";
4
- import { t as BaseController } from "../BaseController-CbkuhNI2.mjs";
5
- import { t as defineResource } from "../defineResource-ZhuLonLm.mjs";
6
- import { t as defineAggregation } from "../defineAggregation-Nv-DwfOf.mjs";
7
- import { E as requireOrgRole } from "../permissions-CP47jdi7.mjs";
8
- import { r as createApp } from "../createApp-zR-DEesr.mjs";
9
- import { n as getModuleExports } from "../module-CwKsgZXp.mjs";
10
- import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-BkyqeTDz.mjs";
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
+ import { t as CRUD_OPERATIONS } from "../constants-DUoKAGYD.mjs";
3
+ import { g as getUserId } from "../types-B8lwJv9Q.mjs";
4
+ import { t as BaseController } from "../BaseController-CTJKm6Cb.mjs";
5
+ import { t as defineResource } from "../defineResource-DWzjyxb2.mjs";
6
+ import { t as defineAggregation } from "../defineAggregation-CpQfcM8R.mjs";
7
+ import { E as requireOrgRole } from "../permissions-DgPrZsJh.mjs";
8
+ import { a as createApp, n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-CjnW4iIX.mjs";
9
+ import { n as getModuleExports } from "../module-CQNHzE7g.mjs";
11
10
  import { runStorageContract } from "./storageContract.mjs";
12
11
  import Fastify from "fastify";
13
12
  import { afterAll, describe, expect, it, vi } from "vitest";
@@ -1505,7 +1504,7 @@ function pickDefaultAuth(authMode, callerAuth) {
1505
1504
  };
1506
1505
  }
1507
1506
  async function createTestApp(options = {}) {
1508
- const { createApp } = await import("../createApp-zR-DEesr.mjs").then((n) => n.i);
1507
+ const { createApp } = await import("../preloadResources-CjnW4iIX.mjs").then((n) => n.o);
1509
1508
  const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
1510
1509
  let dbHandle;
1511
1510
  let dbUri;
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  import { OutboxOwnershipError } from "@classytic/primitives/outbox";
3
3
  import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
4
4
  //#region src/testing/outboxStoreContract.ts
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
3
  //#region src/testing/storageContract.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
3
  //#region src/context/transactionContext.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  //#region src/utils/typeGuards.ts
3
3
  /** Check if fastify has the events plugin registered */
4
4
  function hasEvents(instance) {
@@ -1,6 +1,6 @@
1
1
 
2
- import { i as RequestScope } from "../types-ChuShpCP.mjs";
3
- import { a as UserLike, c as DataPolicy, d as UserBase, i as ObjectId, l as PermissionCheck, n as ArcRequest, o as UserOrganization, r as JWTPayload, s as AuthorizationDecision, t as AnyRecord, u as PermissionContext } from "../base-Csjc2oF-.mjs";
4
- import { $ as OpenApiSchemas, A as RequestIdOptions, An as WriteVerbKey, At as RouteDefinition, C as RequestContext, D as HealthCheck, Dn as MutationWriteContext, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Fn as IControllerResponse, Ft as AuthPluginOptions, G as OnTenantDeleteConfig, H as CrudController, I as MiddlewareHandler, In as IRequestContext, It as Authenticator, J as PresetHook, K as ResolvedTenantPurge, L as RequestWithExtras, Ln as RawRouteHandler, Lt as AuthenticatorContext, M as EventsDecorator, Mn as ControllerLike, Mt as RouteMethod, N as FastifyRequestExtras, Nn as FastifyHandler, Nt as RateLimitConfig, O as HealthOptions, On as ResourceWrites, Ot as ActionHandlerFn, P as FastifyWithAuth, Pn as IController, Pt as AuthHelpers, Q as MiddlewareConfig, Rn as RouteHandler, Rt as JwtContext, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, W as ResourcePermissions, X as CrudRouteKey, Y as PresetResult, Z as CrudSchemas, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jn as ControllerHandler, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kn as WriteContext, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, xn as BaseControllerOptions, y as OwnershipCheck, zt as TokenPair } from "../index-BcR2bFZQ.mjs";
2
+ import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
+ import { a as UserLike, c as DataPolicy, d as UserBase, i as ObjectId, l as PermissionCheck, n as ArcRequest, o as UserOrganization, r as JWTPayload, s as AuthorizationDecision, t as AnyRecord, u as PermissionContext } from "../base-C-NL2fTi.mjs";
4
+ import { $ as OpenApiSchemas, A as RequestIdOptions, An as WriteVerbKey, At as RouteDefinition, C as RequestContext, D as HealthCheck, Dn as MutationWriteContext, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Fn as IControllerResponse, Ft as AuthPluginOptions, G as OnTenantDeleteConfig, H as CrudController, I as MiddlewareHandler, In as IRequestContext, It as Authenticator, J as PresetHook, K as ResolvedTenantPurge, L as RequestWithExtras, Ln as RawRouteHandler, Lt as AuthenticatorContext, M as EventsDecorator, Mn as ControllerLike, Mt as RouteMethod, N as FastifyRequestExtras, Nn as FastifyHandler, Nt as RateLimitConfig, O as HealthOptions, On as ResourceWrites, Ot as ActionHandlerFn, P as FastifyWithAuth, Pn as IController, Pt as AuthHelpers, Q as MiddlewareConfig, Rn as RouteHandler, Rt as JwtContext, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, W as ResourcePermissions, X as CrudRouteKey, Y as PresetResult, Z as CrudSchemas, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jn as ControllerHandler, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kn as WriteContext, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, xn as BaseControllerOptions, y as OwnershipCheck, zt as TokenPair } from "../index-C01wWGJt.mjs";
5
5
  import { n as ElevationOptions, t as ElevationEvent } from "../elevation-BXCfVcNG.mjs";
6
6
  export type { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, AuthorizationDecision, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, DataPolicy, 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, MutationWriteContext, ObjectId, OnTenantDeleteConfig, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RawRouteHandler, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResolvedTenantPurge, ResourceCacheConfig, ResourceConfig, ResourceExtensions, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, ResourceWrites, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult, WriteContext, WriteVerbKey };
@@ -1,2 +1,2 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  export {};
@@ -1,2 +1,2 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
2
  export {};
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { l as UnauthorizedError, o as OrgRequiredError } from "./errors-Cd_yy7aW.mjs";
3
- import { n as requireSingleHeaderValue } from "./headers-CveNO9gR.mjs";
4
- import { t as getUserId$1 } from "./userHelpers-CWF5pbZD.mjs";
1
+ /*! @classytic/arc v2.36.0 | MIT | https://classytic.com */
2
+ import { l as UnauthorizedError, o as OrgRequiredError } from "./errors-DDKni6Tz.mjs";
3
+ import { n as requireSingleHeaderValue } from "./headers-DKslnhey.mjs";
4
+ import { t as getUserId$1 } from "./userHelpers-xQAi5bPF.mjs";
5
5
  //#region src/scope/types.ts
6
6
  /**
7
7
  * Request Scope — The One Standard
@@ -50,36 +50,10 @@ function getOrgId(scope) {
50
50
  if (scope.kind === "elevated") return scope.organizationId;
51
51
  }
52
52
  /**
53
- * Resolve the tenant (organization) id from an incoming Fastify request,
54
- * walking the canonical sources in order: `request.scope` → `x-organization-id`
55
- * header. Returns `{ organizationId, source }` so callers can log or branch
56
- * on which path won.
53
+ * Tenant id for a request, from its scope.
57
54
  *
58
- * Use in custom handlers that bypass arc's auto-CRUD path (worker routes,
59
- * batch importers, cross-engine subscribers) and therefore don't get the
60
- * tenant injected by `BaseCrudController`. Bridges arc's HTTP shape to
61
- * the mongokit/kit-side tenant context (e.g. `createTenantContext().run`).
62
- *
63
- * Header fallback is intentional: matches `resolveOrgFromHeader` semantics
64
- * for hosts that select an org via header instead of session/scope. Don't
65
- * reach for this when `getOrgId(scope)` alone is enough — for standard
66
- * authenticated routes the scope is already authoritative.
67
- *
68
- * @example
69
- * ```typescript
70
- * import { getTenantFromRequest } from '@classytic/arc/scope';
71
- * import { tenantContext } from './lib/mongokit.js';
72
- *
73
- * fastify.post('/statements/batch', async (req, reply) => {
74
- * const { organizationId } = getTenantFromRequest(req);
75
- * if (!organizationId) {
76
- * return reply.code(400).send({ error: 'organizationId required' });
77
- * }
78
- * await tenantContext.run({ tenantId: organizationId }, async () => {
79
- * await statementRepository.create(req.body);
80
- * });
81
- * });
82
- * ```
55
+ * Reads the SCOPE, never `request.user` — the two drift once elevation or a
56
+ * service token is involved, and the scope is the resolved answer.
83
57
  */
84
58
  function getTenantFromRequest(req, options = {}) {
85
59
  if (req.scope) {
@@ -374,27 +348,10 @@ function getOrgContext(request) {
374
348
  };
375
349
  }
376
350
  /**
377
- * Read `request.scope` safely from any object that *might* have one.
378
- * Falls back to `PUBLIC_SCOPE` when the field is absent or undefined.
379
- *
380
- * This is the canonical way for permission checks, presets, and middleware
381
- * to read scope — never access `request.scope` directly because it can be
382
- * `undefined` on requests that haven't been touched by an auth adapter yet.
351
+ * Read `request.scope`, falling back to `PUBLIC_SCOPE`.
383
352
  *
384
- * Accepts a structural shape (`{ scope?: RequestScope }`) instead of the
385
- * full Fastify request type so it can be called from any layer without
386
- * dragging in the Fastify type. The actual runtime is identical.
387
- *
388
- * @example
389
- * ```typescript
390
- * import { getRequestScope } from '@classytic/arc/scope';
391
- *
392
- * function myCheck(ctx: PermissionContext) {
393
- * const scope = getRequestScope(ctx.request);
394
- * if (isElevated(scope)) return true;
395
- * // ...
396
- * }
397
- * ```
353
+ * Never returns undefined: an absent scope means an unauthenticated request,
354
+ * not a missing one, and callers must not have to distinguish those.
398
355
  */
399
356
  function getRequestScope(request) {
400
357
  return request.scope ?? PUBLIC_SCOPE;
@@ -1,5 +1,5 @@
1
1
 
2
- import { V as ResourceDefinition } from "./index-BcR2bFZQ.mjs";
2
+ import { V as ResourceDefinition } from "./index-C01wWGJt.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/integrations/mcp/types.d.ts
5
5
  /** Behavioral hints for MCP clients (tool annotations per MCP spec) */
@@ -446,52 +446,23 @@ interface ArcModule<TExports = unknown> {
446
446
  */
447
447
  readonly dependsOn?: readonly string[];
448
448
  /**
449
- * Infra SETUP function — the module-level analog of the app's `plugins()`
450
- * slot. Runs in the plugins phase: AFTER the app's own `plugins()` (so
451
- * module infra can build on app foundations like a DB connection) and in
452
- * `dependsOn` order, but BEFORE any module `bootstrap` — so engines
453
- * initialised in `bootstrap` can rely on what's registered here.
454
- *
455
- * ## ⚠ This is NOT itself a Fastify plugin
456
- *
457
- * Despite the name, arc CALLS this function directly — it is never handed
458
- * to `fastify.register()`. Two consequences, both of which bite module
459
- * authors coming from ordinary Fastify plugins:
460
- *
461
- * - **You must register plugins yourself.** `await fastify.register(x)`,
462
- * not `return x`. A returned function is read as a DISPOSER (below),
463
- * so returning a plugin silently registers nothing and then invokes
464
- * your plugin as a teardown callback. Arc detects the `fastify-plugin`
465
- * case and throws; the bare-function case warns.
466
- * - **There is no encapsulation and no prefix here.** Decorators and
467
- * hooks you add land on the instance arc passes you — the same
468
- * instance every other module sees. Fastify's encapsulation applies
469
- * only inside a `register()` call you make yourself. See
470
- * https://fastify.dev/docs/latest/Reference/Plugins/
449
+ * Infra SETUP — the module analog of the app's `plugins()`. Runs after the
450
+ * app's own `plugins()`, in `dependsOn` order, before any module `bootstrap`.
471
451
  *
472
- * ```ts
473
- * plugins: async (fastify) => {
474
- * await fastify.register(somePlugin, { prefix: '/x' }); // ✅ register
475
- * fastify.decorate('thing', thing); // ✅ root-visible
476
- * // return somePlugin // ❌ never registered
477
- * },
478
- * ```
452
+ * ⚠ NOT a Fastify plugin: arc CALLS it, never `register()`s it. So you
453
+ * register your own (`await fastify.register(x)`, not `return x` — a
454
+ * returned function is read as a DISPOSER), and there is no encapsulation
455
+ * or prefix: decorators land on the shared instance. Returning an `fp()`
456
+ * plugin throws; a bare multi-arg function warns.
479
457
  *
480
- * The name is kept for the 2.x line; v3 renames the slot to `setup` and
481
- * REMOVES `plugins` outright — v3 ships no compatibility aliases (v3.md).
482
- *
483
- * Separating this from `bootstrap` keeps lifecycle intent explicit for
484
- * published ecosystem packages: `plugins` = "register infra"; `bootstrap` =
485
- * "initialise engines (and return the public export)". Registering a plugin
486
- * inside `bootstrap` still works — this slot just makes the two distinct.
487
- *
488
- * Receives `{ defer }` to register teardown for anything it acquires, and
489
- * may RETURN a single disposer as shorthand for the one-resource case:
458
+ * Receives `{ defer }` for teardown, or return one disposer as shorthand.
459
+ * v3 renames this slot to `setup` with no alias (v3.md).
490
460
  *
491
461
  * ```ts
492
462
  * plugins: async (fastify) => {
463
+ * await fastify.register(somePlugin, { prefix: '/x' });
493
464
  * const conn = await connect();
494
- * return () => conn.close(); // equivalent to defer(...)
465
+ * return () => conn.close();
495
466
  * },
496
467
  * ```
497
468
  */
@@ -550,53 +521,25 @@ interface ArcModule<TExports = unknown> {
550
521
  */
551
522
  bootstrap?: (fastify: FastifyInstance, context: ModuleSetupContext) => TExports | Promise<TExports>;
552
523
  /**
553
- * App-level resource NAMES this module authoritatively SUPERSEDES. When the
554
- * host also lists (or `resourceDir`-discovers) a resource of the same name,
555
- * arc DROPS the app copy and registers THIS module's version instead — so the
556
- * host keeps NO hand-maintained "which resources did modules take over" filter
557
- * set. Ownership lives on the atom that provides it (colocated, drift-proof):
558
- *
559
- * ```ts
560
- * defineModule({ name: "order", owns: ["order", "quotation", "rfq"], resources: () => [...] })
561
- * ```
562
- *
563
- * Semantics:
564
- * - Purely a supersede-the-app-fork switch — it filters the *app* resource
565
- * list, never the module's own `resources` (those always register).
566
- * - Idempotent + tolerant on the APP side: an `owns` entry with no matching
567
- * app resource is a silent no-op — a module may pre-declare a name before
568
- * any fork exists.
569
- * - Collected across ALL modules; the app-side supersession is their union.
570
- *
571
- * **Prefer `owns: "provided"`** (2.32) — arc derives the list from the
572
- * resources this module actually returned, so the claim and the reality
573
- * cannot disagree:
574
- *
575
- * ```ts
576
- * defineModule({ name: "order", owns: "provided", resources: () => [...] })
577
- * ```
578
- *
579
- * That is the whole arm for most modules. Resources are resolved exactly
580
- * once, names are validated (duplicates within a module are rejected), the
581
- * effective `owns` is derived, and supersession runs afterward — one atomic
582
- * phase arc owns, rather than a rule each module re-states.
583
- *
584
- * **The explicit-array form is ENFORCED at boot: if you `owns` a name,
585
- * PROVIDE it** — in THIS module's own `resources`. Arc fails boot when a
586
- * claimed name is not among them, naming the module, the unmet names, and
587
- * what it did supply. Without that check, a typo or a resource deleted
588
- * without updating the list booted "successfully" with the app's route
589
- * silently removed and nothing serving it. Two real cases, both silent 404s:
590
- * a POS module claiming a checkout resource it mounted only when the host
591
- * supplied a pipeline, and a device gateway claiming a name it never
592
- * provided at all.
593
- *
594
- * Keep the explicit array only when the list is genuinely NOT the module's
595
- * own resource set — e.g. pre-declaring a name before the fork exists.
596
- *
597
- * A sibling module supplying the name does NOT satisfy the claim — ownership
598
- * is local to the module that declares it, which is the colocation this arm
599
- * exists for.
524
+ * App-level resource NAMES this module SUPERSEDES. When the host lists (or
525
+ * discovers) a resource of the same name, arc drops the app copy and
526
+ * registers this module's — so the host maintains no "which resources did
527
+ * modules take over" filter set.
528
+ *
529
+ * PREFER `owns: "provided"`: arc derives the list from the resources the
530
+ * module actually returned, so the claim cannot disagree with reality.
531
+ * Resources resolve once, names are validated, `owns` is derived, and
532
+ * supersession runs after — one phase arc owns.
533
+ *
534
+ * The explicit array is ENFORCED at boot: a claimed name must appear in THIS
535
+ * module's own `resources`, or boot fails naming the unmet names. Without
536
+ * that, a typo or a deleted resource booted "successfully" with the app's
537
+ * route removed and nothing serving it. A sibling module providing the name
538
+ * does NOT satisfy the claim — ownership is local to its declarer.
539
+ *
540
+ * It only filters the APP list, never the module's own resources; an entry
541
+ * matching no app resource is a silent no-op (a module may pre-declare a
542
+ * name before any fork exists). Collected across modules as a union.
600
543
  */
601
544
  readonly owns?: readonly string[] | "provided";
602
545
  /**
@@ -36,48 +36,17 @@ declare function requireSingleHeaderValue(headers: ArcHeaders | Record<string, s
36
36
  //#endregion
37
37
  //#region src/scope/types.d.ts
38
38
  /**
39
- * A capability mandate — declarative, time-boxed, optionally cap-bounded
40
- * authorization for a single high-value action by an AI agent or service.
39
+ * A capability mandate — time-boxed, optionally cap-bounded authorization for
40
+ * ONE high-value action by an agent or service.
41
41
  *
42
- * Models the **2025 agent-payments / agent-authorization conventions**:
43
- * - Google + Anthropic + Stripe **AP2** (Agent Payments Protocol)
44
- * - Stripe **x402 / Agentic Commerce**
45
- * - **MCP authorization** (RFC 9728 + RFC 9700 + RFC 9449)
42
+ * Where OAuth scopes answer "what may this client EVER do?", a mandate answers
43
+ * "what is THIS request authorized to do right now?" — narrower in capability,
44
+ * time, and value. Shape follows the 2025 agent-authorization conventions
45
+ * (AP2, x402, MCP authorization: RFC 9728/9700/9449).
46
46
  *
47
- * Where OAuth `scopes` answer "what can this client EVER do?", a mandate
48
- * answers "what is THIS REQUEST authorized to do RIGHT NOW?" — narrower in
49
- * capability, time, and value.
50
- *
51
- * Arc does **not** parse mandate JWTs / Verifiable Credentials — your
52
- * authenticate callback does (one `jose.jwtVerify()` or `did-jwt-vc.verify()`
53
- * call) and populates this object. Arc's `requireMandate(capability, opts)`
54
- * permission helper reads it and validates the action against the mandate's
55
- * declared bounds.
56
- *
57
- * @example
58
- * ```typescript
59
- * // Inbound: Authorization: Mandate eyJhbGc...
60
- * authenticate: async (request) => {
61
- * const proof = request.headers['authorization']?.replace(/^Mandate /, '');
62
- * const claims = await verifyMandate(proof); // host's verifier
63
- * request.scope = {
64
- * kind: 'service',
65
- * clientId: claims.iss,
66
- * organizationId: claims.org,
67
- * scopes: claims.scope?.split(' '),
68
- * mandate: {
69
- * id: claims.jti,
70
- * capability: claims.cap, // 'payment.charge' / 'inbox.send' / ...
71
- * cap: claims.amount, // numeric ceiling
72
- * expiresAt: claims.exp * 1000,
73
- * parent: claims.parent, // delegated mandate chain
74
- * audience: claims.aud, // resource id mandate is bound to
75
- * },
76
- * dpopJkt: claims.cnf?.jkt, // RFC 7638 — sender-constrained
77
- * };
78
- * return { id: claims.iss };
79
- * };
80
- * ```
47
+ * Arc does NOT verify mandate JWTs or VCs. The host's `authenticate` callback
48
+ * verifies and populates this object; `requireMandate(capability, opts)` then
49
+ * validates the action against its bounds.
81
50
  */
82
51
  interface Mandate {
83
52
  /** Mandate identifier — typically the JWT `jti` claim. */
@@ -252,36 +221,10 @@ declare function isAuthenticated(scope: RequestScope): boolean;
252
221
  /** Get organizationId from scope (member, service, or elevated — undefined otherwise) */
253
222
  declare function getOrgId(scope: RequestScope): string | undefined;
254
223
  /**
255
- * Resolve the tenant (organization) id from an incoming Fastify request,
256
- * walking the canonical sources in order: `request.scope` → `x-organization-id`
257
- * header. Returns `{ organizationId, source }` so callers can log or branch
258
- * on which path won.
259
- *
260
- * Use in custom handlers that bypass arc's auto-CRUD path (worker routes,
261
- * batch importers, cross-engine subscribers) and therefore don't get the
262
- * tenant injected by `BaseCrudController`. Bridges arc's HTTP shape to
263
- * the mongokit/kit-side tenant context (e.g. `createTenantContext().run`).
224
+ * Tenant id for a request, from its scope.
264
225
  *
265
- * Header fallback is intentional: matches `resolveOrgFromHeader` semantics
266
- * for hosts that select an org via header instead of session/scope. Don't
267
- * reach for this when `getOrgId(scope)` alone is enough — for standard
268
- * authenticated routes the scope is already authoritative.
269
- *
270
- * @example
271
- * ```typescript
272
- * import { getTenantFromRequest } from '@classytic/arc/scope';
273
- * import { tenantContext } from './lib/mongokit.js';
274
- *
275
- * fastify.post('/statements/batch', async (req, reply) => {
276
- * const { organizationId } = getTenantFromRequest(req);
277
- * if (!organizationId) {
278
- * return reply.code(400).send({ error: 'organizationId required' });
279
- * }
280
- * await tenantContext.run({ tenantId: organizationId }, async () => {
281
- * await statementRepository.create(req.body);
282
- * });
283
- * });
284
- * ```
226
+ * Reads the SCOPE, never `request.user` — the two drift once elevation or a
227
+ * service token is involved, and the scope is the resolved answer.
285
228
  */
286
229
  declare function getTenantFromRequest(req: {
287
230
  scope?: RequestScope;
@@ -510,27 +453,10 @@ declare function getOrgContext(request: {
510
453
  orgRoles: string[];
511
454
  };
512
455
  /**
513
- * Read `request.scope` safely from any object that *might* have one.
514
- * Falls back to `PUBLIC_SCOPE` when the field is absent or undefined.
515
- *
516
- * This is the canonical way for permission checks, presets, and middleware
517
- * to read scope — never access `request.scope` directly because it can be
518
- * `undefined` on requests that haven't been touched by an auth adapter yet.
519
- *
520
- * Accepts a structural shape (`{ scope?: RequestScope }`) instead of the
521
- * full Fastify request type so it can be called from any layer without
522
- * dragging in the Fastify type. The actual runtime is identical.
456
+ * Read `request.scope`, falling back to `PUBLIC_SCOPE`.
523
457
  *
524
- * @example
525
- * ```typescript
526
- * import { getRequestScope } from '@classytic/arc/scope';
527
- *
528
- * function myCheck(ctx: PermissionContext) {
529
- * const scope = getRequestScope(ctx.request);
530
- * if (isElevated(scope)) return true;
531
- * // ...
532
- * }
533
- * ```
458
+ * Never returns undefined: an absent scope means an unauthenticated request,
459
+ * not a missing one, and callers must not have to distinguish those.
534
460
  */
535
461
  declare function getRequestScope(request: {
536
462
  scope?: RequestScope;
@@ -576,38 +502,9 @@ interface ScopeFirstCtxRequest {
576
502
  headers?: ArcHeaders | undefined;
577
503
  }
578
504
  /**
579
- * The ONE shared actor/org derivation for resource handlers and module
580
- * packages (`arc-*` fleet) — scope-FIRST, per the canonical precedence:
581
- *
582
- * 1. `request.scope` (arc's validated `RequestScope`, set by auth adapters):
583
- * `userId` (human) or `clientId` (service) → `actorId`; `getOrgId(scope)`
584
- * → `organizationId`.
585
- * 2. The `x-organization-id` header — ONLY when the scope carries no org.
586
- * This is the bridge for custom-auth/public routes where arc leaves scope
587
- * unpopulated; it never overrides a validated scope.
588
- *
589
- * Replaces per-package hand-rolled clones of
590
- * `req.user?._id ?? req.user?.id ?? headers['x-organization-id']` — that
591
- * pattern re-implements (and drifts from) arc's canonical resolution. A
592
- * package's `ctxOf(req)` should call this for the shared
593
- * `{ actorId, organizationId }` and only ADD its kernel-specific fields
594
- * (`actorKind` / `correlationId` / `roles` / `idempotencyKey`).
595
- *
596
- * Structural request shape — works on a raw `FastifyRequest` AND on arc's
597
- * `IRequestContext` (see {@link ScopeFirstCtxRequest}: the discriminated
598
- * `RequestScope` and the flat controller projection are both understood).
599
- *
600
- * @example
601
- * ```typescript
602
- * import { scopeFirstCtx } from '@classytic/arc/scope';
603
- *
604
- * // Guaranteed actor (kernel context requires one):
605
- * const { actorId, organizationId } = scopeFirstCtx(req, { fallbackActorId: 'system' });
606
- *
607
- * // Optional actor (package applies its own policy):
608
- * const ctx = scopeFirstCtx(req);
609
- * if (ctx.actorId) audit.record({ actor: ctx.actorId });
610
- * ```
505
+ * Build a scope-first context for permission checks outside a Fastify request
506
+ * (jobs, MCP tools, workers). `scope` is the identity channel; `user` is
507
+ * derived for legacy checks that still read it.
611
508
  */
612
509
  declare function scopeFirstCtx(req: ScopeFirstCtxRequest, options: ScopeFirstCtxOptions & {
613
510
  fallbackActorId: string;
@@ -1,6 +1,6 @@
1
1
 
2
- import { i as RequestScope } from "../types-ChuShpCP.mjs";
3
- import { l as PermissionCheck, u as PermissionContext } from "../base-Csjc2oF-.mjs";
2
+ import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
+ import { l as PermissionCheck, u as PermissionContext } from "../base-C-NL2fTi.mjs";
4
4
  import { UsageBucket, UsageStore, usagePeriod } from "@classytic/repo-core/usage";
5
5
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
6
6
  //#region src/usage/requireQuota.d.ts