@classytic/arc 2.33.0 → 2.35.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 (184) hide show
  1. package/dist/{BaseController-CaQC5EXX.mjs → BaseController-C-pofd0v.mjs} +131 -16
  2. package/dist/{EventTransport-tq4CEB-q.mjs → EventTransport-BrPeGUil.mjs} +1 -1
  3. package/dist/{HookSystem-DqXkMXkB.mjs → HookSystem-LlPm3Anm.mjs} +6 -4
  4. package/dist/{ResourceRegistry-DoALUlOO.mjs → ResourceRegistry-yuD1Ra53.mjs} +2 -2
  5. package/dist/{pipeline-q69DZzgG.mjs → actionPermissions-LFuOra7W.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-B3mP_QTy.mjs → authorizationDecision-DYXBBgtg.mjs} +6 -2
  13. package/dist/{base-Csjc2oF-.d.mts → base-GZU3LlzP.d.mts} +1 -1
  14. package/dist/{buildHandler-DLE9xX-V.mjs → buildHandler-Do9aMIvL.mjs} +4 -4
  15. package/dist/cache/index.mjs +4 -4
  16. package/dist/{caching-BcGmvnHh.mjs → caching-cN5X9VEL.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-BhzLXCfN.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 +3 -3
  31. package/dist/core/index.mjs +8 -8
  32. package/dist/{core-dxhcHhUD.mjs → core-DdEvmmkY.mjs} +3 -3
  33. package/dist/{createActionRouter-DVpoA0do.mjs → createActionRouter-EYWgd0ly.mjs} +27 -64
  34. package/dist/{createAggregationRouter-B_B48JZ-.mjs → createAggregationRouter-ByOK9Iev.mjs} +7 -7
  35. package/dist/defineAggregation-DoIijIFq.mjs +25 -0
  36. package/dist/{defineResource-DR9vsUYy.mjs → defineResource-BOrGeocE.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-tGIAldco.mjs} +4 -4
  41. package/dist/encryption/index.mjs +3 -3
  42. package/dist/{errorHandler-C7xBqHsI.mjs → errorHandler-BbQ1nQdk.mjs} +3 -3
  43. package/dist/{errors-Cd_yy7aW.mjs → errors-CLid6atr.mjs} +1 -1
  44. package/dist/{eventPlugin-DSNEZWfV.d.mts → eventPlugin-B47sfCJ8.d.mts} +1 -1
  45. package/dist/{eventPlugin-Dar8qRaj.mjs → eventPlugin-ClPn5kjn.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-CMMTL3cj.d.mts} +3 -3
  53. package/dist/{fields-CojeUkS4.mjs → fields-UtpiJO5R.mjs} +1 -1
  54. package/dist/{generate-DiB0nHPR.mjs → generate-BoAVy3_b.mjs} +2 -2
  55. package/dist/{headers-CveNO9gR.mjs → headers-DODWf7aC.mjs} +2 -2
  56. package/dist/{health-CasG2A28.mjs → health-_Zs3R_Sb.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 +23 -58
  60. package/dist/idempotency/redis.mjs +1 -1
  61. package/dist/{index-C5NnMEKF.d.mts → index-C50BUxKs.d.mts} +1 -1
  62. package/dist/{index-BcR2bFZQ.d.mts → index-C5K78SXo.d.mts} +33 -105
  63. package/dist/{index-ugr1tQlW.d.mts → index-CJCcbADG.d.mts} +1 -1
  64. package/dist/{index-B-DvUzsr.d.mts → index-K7auNNW0.d.mts} +2 -2
  65. package/dist/{index-Brin0ODU.d.mts → index-h-mTgMqy.d.mts} +21 -31
  66. package/dist/index.d.mts +6 -6
  67. package/dist/index.mjs +12 -12
  68. package/dist/{init-mdJ-v4Xp.mjs → init-Ce4R0ZGK.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 +1 -1
  80. package/dist/integrations/webhooks.mjs +11 -24
  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-my7sxjw5.mjs} +1 -1
  85. package/dist/{jsonBody-CfGH-GxO.mjs → jsonBody-CVKA7C5m.mjs} +2 -2
  86. package/dist/{keys-WeKvY1dE.mjs → keys-GlU7XpmG.mjs} +1 -1
  87. package/dist/{loadResources-Bj-aSsXi.mjs → loadResources-DdQb0qgV.mjs} +2 -2
  88. package/dist/{loadResourcesFromEntry-DwaxNcE2.mjs → loadResourcesFromEntry-s6aGs6-0.mjs} +2 -2
  89. package/dist/logger/index.d.mts +23 -4
  90. package/dist/logger/index.mjs +50 -7
  91. package/dist/{mcpPlugin-aJg5TqHi.mjs → mcpPlugin-DkzZ7Nfx.mjs} +15 -16
  92. package/dist/{memory-qYSB7rMQ.mjs → memory-BwedEXau.mjs} +2 -2
  93. package/dist/{metrics-D7uchLjN.mjs → metrics-5LNPvL3P.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-Bh5OC7ww.mjs} +1 -1
  98. package/dist/{multipartBody-4w-S3-PU.mjs → multipartBody-DjilSDAT.mjs} +1 -1
  99. package/dist/{openapi-BN5cQaO2.mjs → openapi-ItLH12_L.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-CXB79bvt.mjs → outbox-module-SGIZz6oS.mjs} +14 -5
  103. package/dist/{outbox-module-D-VFq37T.d.mts → outbox-module-b1TyDGGi.d.mts} +40 -3
  104. package/dist/permissions/index.d.mts +2 -2
  105. package/dist/permissions/index.mjs +5 -5
  106. package/dist/{permissions-BAfwgBxc.mjs → permissions-CpSBlFXZ.mjs} +6 -6
  107. package/dist/{pipe-BzzoEhHj.mjs → pipe-DNOuCfAs.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-DGAQQKL8.mjs} +1 -1
  115. package/dist/{preloadResources-g07ldtzh.d.mts → preloadResources--ZtY9pe9.d.mts} +54 -270
  116. package/dist/{createApp-CLGegqiG.mjs → preloadResources-DYMzmUk1.mjs} +112 -31
  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-D-h0uWtP.mjs → presets-Bi0Z6Iq8.mjs} +4 -4
  126. package/dist/{projection-BKM0Ui-s.mjs → projection-aevMua7Q.mjs} +2 -2
  127. package/dist/{queryCachePlugin-DhsZ0ZIc.mjs → queryCachePlugin-BI7Z88Cx.mjs} +5 -5
  128. package/dist/{rateLimitKey-XngqVNEV.mjs → rateLimitKey-pyevMUVf.mjs} +1 -1
  129. package/dist/{realtime-CTkv3O59.mjs → realtime-Cfl2bVjj.mjs} +9 -9
  130. package/dist/{rateLimit-D4-ASwsI.mjs → registerRoute-u1opU66a.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-B4sf9Rfm.mjs} +15 -56
  134. package/dist/{renewingLease-BnWtbA2U.mjs → renewingLease-5ESPMxp0.mjs} +14 -2
  135. package/dist/{replyHelpers-BSBVCwL3.mjs → replyHelpers-2jfmMnnV.mjs} +2 -2
  136. package/dist/{requestContext-uYE35JxI.mjs → requestContext-DzOUGNdY.mjs} +1 -1
  137. package/dist/{rolldown-runtime-DNg-KNCk.mjs → rolldown-runtime-CEvvVXTC.mjs} +1 -1
  138. package/dist/{runtimeCapabilities-Dm88DaTW.mjs → runtimeCapabilities-BagWc3TI.mjs} +1 -1
  139. package/dist/{schedules-CAcZgia0.mjs → schedules-BLpZLWGF.mjs} +6 -4
  140. package/dist/{schemaConverter-XH1RZRyu.mjs → schemaConverter-D2_bHGil.mjs} +19 -32
  141. package/dist/{schemaIR-CDvclAew.mjs → schemaIR-s3-ouZmP.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-DgepeCv6.mjs} +4 -4
  147. package/dist/simpleEqualityMatcher-BO-sFTQf.mjs +43 -0
  148. package/dist/{sse-Cf0jUHsh.mjs → sse-DUIP1XH6.mjs} +5 -5
  149. package/dist/{sseStream-Bvpi-UXQ.mjs → sseStream-ByFGyIVr.mjs} +2 -2
  150. package/dist/{store-helpers-B88p99Z2.mjs → store-helpers-kIJCY0wI.mjs} +1 -1
  151. package/dist/{streaming-DsT1B5zJ.mjs → streaming-DOpZVht6.mjs} +1 -1
  152. package/dist/{subscribe-helpers-CLIOHsOK.mjs → subscribe-helpers-Dh2BnzJf.mjs} +12 -46
  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-BTZzEcRo.mjs} +1 -1
  160. package/dist/{typeGuards-jjKJDN13.mjs → typeGuards-B-_jqaMi.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-BcTumAz_.d.mts → types-DDD5RKTu.d.mts} +30 -87
  165. package/dist/{types-BfC83KsP.d.mts → types-DSvHH0uR.d.mts} +1 -1
  166. package/dist/{types-ChuShpCP.d.mts → types-Ddl9OjmE.d.mts} +18 -121
  167. package/dist/{types-CoqIRrkY.mjs → types-GY4kATJb.mjs} +10 -53
  168. package/dist/usage/index.d.mts +2 -2
  169. package/dist/usage/index.mjs +0 -0
  170. package/dist/{userHelpers-CWF5pbZD.mjs → userHelpers-AKcB4V03.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-CBK9HQOI.mjs} +3 -3
  174. package/dist/{validate-CdQlz52F.mjs → validate-dosAIvQU.mjs} +2 -2
  175. package/dist/{versioning-rZbyLVD-.mjs → versioning-BbTs4pwM.mjs} +3 -3
  176. package/package.json +8 -8
  177. package/skills/arc/SKILL.md +9 -9
  178. package/skills/arc/references/api-reference.md +1 -1
  179. package/dist/actionPermissions-CUeCmpUd.mjs +0 -25
  180. package/dist/defineAggregation-Nv-DwfOf.mjs +0 -48
  181. package/dist/preloadResources-BkyqeTDz.mjs +0 -46
  182. package/dist/registerRoute-DNnd3vkz.mjs +0 -113
  183. package/dist/repositoryFilter-DKny7PDn.mjs +0 -45
  184. package/dist/simpleEqualityMatcher-Se8VrsaP.mjs +0 -69
@@ -1,17 +1,92 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-BxCoxmiN.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-BhzLXCfN.mjs";
3
3
  import { arcLog } from "./logger/index.mjs";
4
- import { a as conjoinPolicyFilters } from "./authorizationDecision-B3mP_QTy.mjs";
5
- import { d as createDomainError, f as createError, i as NotFoundError, p as isArcError, r as ForbiddenError } from "./errors-Cd_yy7aW.mjs";
6
- import { n as getUserRoles, t as getUserId } from "./userHelpers-CWF5pbZD.mjs";
7
- import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-CoqIRrkY.mjs";
8
- import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-Se8VrsaP.mjs";
9
- import { t as toRepositoryFilter } from "./repositoryFilter-DKny7PDn.mjs";
10
- import { i as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-CojeUkS4.mjs";
11
- import { g as ArcQueryParser, n as scheduleBackground } from "./utils-h1V_sT-t.mjs";
12
- import { t as buildQueryKey } from "./keys-WeKvY1dE.mjs";
4
+ import { a as conjoinPolicyFilters } from "./authorizationDecision-DYXBBgtg.mjs";
5
+ import { d as createDomainError, f as createError, i as NotFoundError, p as isArcError, r as ForbiddenError } from "./errors-CLid6atr.mjs";
6
+ import { n as getUserRoles, t as getUserId } from "./userHelpers-AKcB4V03.mjs";
7
+ import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-GY4kATJb.mjs";
8
+ import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-BO-sFTQf.mjs";
9
+ import { i as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-UtpiJO5R.mjs";
10
+ import { g as ArcQueryParser, n as scheduleBackground } from "./utils-CBK9HQOI.mjs";
11
+ import { t as buildQueryKey } from "./keys-GlU7XpmG.mjs";
12
+ import { t as transactionContext } from "./transactionContext-BTZzEcRo.mjs";
13
+ import { policyRecordToFilter } from "@classytic/repo-core/filter";
13
14
  import { retryingTransaction } from "@classytic/repo-core/repository";
14
15
  import { isProductionEnv } from "@classytic/primitives/environment";
16
+ //#region src/core/repositoryFilter.ts
17
+ const NATIVE_FILTER_DIALECT = Symbol.for("@classytic/arc/native-filter-dialect");
18
+ /**
19
+ * Mark a server-owned policy as intentionally adapter-native. This is an
20
+ * explicit escape hatch for semantics absent from repo-core's universal IR
21
+ * (for example MongoDB `$elemMatch`). Never apply it to client input.
22
+ */
23
+ function nativePolicyFilter(dialect, record) {
24
+ return Object.freeze({
25
+ ...record,
26
+ [NATIVE_FILTER_DIALECT]: dialect
27
+ });
28
+ }
29
+ function nativeDialects(value, found = /* @__PURE__ */ new Set()) {
30
+ if (value === null || typeof value !== "object") return found;
31
+ const dialect = value[NATIVE_FILTER_DIALECT];
32
+ if (typeof dialect === "string") found.add(dialect);
33
+ if (Array.isArray(value)) for (const entry of value) nativeDialects(entry, found);
34
+ else for (const entry of Object.values(value)) nativeDialects(entry, found);
35
+ return found;
36
+ }
37
+ function stripNativeMarkers(value) {
38
+ if (Array.isArray(value)) return value.map(stripNativeMarkers);
39
+ if (value === null || typeof value !== "object" || value instanceof Date) return value;
40
+ const out = {};
41
+ for (const [key, entry] of Object.entries(value)) out[key] = stripNativeMarkers(entry);
42
+ return out;
43
+ }
44
+ const log$2 = arcLog("repository-filter");
45
+ let warnedUnconvertible = false;
46
+ /** True for a `{ $op: ... }` operator object (all keys `$`-prefixed). */
47
+ function isOperatorObject(value) {
48
+ if (value === null || typeof value !== "object") return false;
49
+ if (Array.isArray(value) || value instanceof Date) return false;
50
+ const keys = Object.keys(value);
51
+ return keys.length > 0 && keys.some((k) => k.startsWith("$"));
52
+ }
53
+ /**
54
+ * Does this compound filter contain any `$`-operator (top-level logical like
55
+ * `$and`/`$or`, or a field operator object like `{ $in: [...] }`)? If not, it is
56
+ * pure flat equality and portable as-is.
57
+ */
58
+ function filterHasDollarOperator(record) {
59
+ for (const [key, value] of Object.entries(record)) {
60
+ if (key.startsWith("$")) return true;
61
+ if (isOperatorObject(value)) return true;
62
+ }
63
+ return false;
64
+ }
65
+ /**
66
+ * Normalize a compound query/policy filter for the repository layer. Flat
67
+ * equality records pass through unchanged; anything carrying `$`-operators is
68
+ * converted to repo-core {@link Filter} IR so semantics stay explicit. Common
69
+ * operators compile identically across kits; capability-specific operators
70
+ * such as array-element matching fail loudly on adapters that cannot model them.
71
+ */
72
+ function toRepositoryFilter(record) {
73
+ const dialects = nativeDialects(record);
74
+ if (dialects.size > 0) {
75
+ if (dialects.size > 1) throw new Error(`Cannot compose native policy-filter dialects: ${[...dialects].sort().join(", ")}`);
76
+ return stripNativeMarkers(record);
77
+ }
78
+ if (!filterHasDollarOperator(record)) return record;
79
+ try {
80
+ return policyRecordToFilter(record);
81
+ } catch (err) {
82
+ if (!warnedUnconvertible) {
83
+ warnedUnconvertible = true;
84
+ log$2.warn(`A \`\$\`-operator filter could not be normalized to portable Filter IR (operator outside arc's policy dialect). Passing the raw record to the repository — MongoKit handles it; non-Mongo kits may not. (${err instanceof Error ? err.message : String(err)})`);
85
+ }
86
+ return record;
87
+ }
88
+ }
89
+ //#endregion
15
90
  //#region src/core/AccessControl.ts
16
91
  const log$1 = arcLog("access-control");
17
92
  var AccessControl = class {
@@ -801,9 +876,43 @@ var QueryResolver = class {
801
876
  * Combines what was previously _buildContext + _parseQueryOptions + _applyFilters.
802
877
  */
803
878
  resolve(req, meta) {
804
- const parsed = this.queryParser.parse(req.query);
879
+ /**
880
+ * Strip `_policyFilters` BEFORE the parse, not after.
881
+ *
882
+ * **Reachable only when a CLIENT puts it in the URL.** Trusted policy is a
883
+ * request DECORATION (`request._policyFilters`, set by the permission
884
+ * middleware) that `createRequestContext` lifts into `metadata` — it never
885
+ * enters `req.query`, which is Fastify's parsed querystring. So the key
886
+ * arriving here is always user-supplied, always inert (the authoritative
887
+ * copy is read from `arcContext` below), and must simply not reach the
888
+ * parser.
889
+ *
890
+ * The previous `delete` ran on `parsed.filters` — AFTER
891
+ * `queryParser.parse()` — so on the one path that needed it, it could
892
+ * never run: a KIT parser validates every key against the resource's
893
+ * `allowedFilterFields` and throws during the parse, before the cleanup
894
+ * line. Arc's own parser skips the key via `RESERVED_QUERY_PARAMS`, so the
895
+ * two parsers disagreed on the same request: arc ignored a probe, kits
896
+ * 400'd it.
897
+ *
898
+ * Stripping here makes them agree, and agrees with arc. Nothing is
899
+ * weakened — the client's value was already inert — but note the trade:
900
+ * under a kit parser `?_policyFilters=…` used to be a loud 400 and is now
901
+ * silently dropped. The alternative, teaching every kit's reserved-key list
902
+ * about an arc-internal key, spreads one framework's leak across the
903
+ * ecosystem for the same outcome.
904
+ *
905
+ * Copy only when present: the hot path allocates nothing, and `req.query`
906
+ * is never mutated (it is shared with the rest of the request).
907
+ */
908
+ const rawQuery = req.query;
909
+ let query = rawQuery;
910
+ if (rawQuery && "_policyFilters" in rawQuery) {
911
+ const { _policyFilters: _ignored, ...rest } = rawQuery;
912
+ query = rest;
913
+ }
914
+ const parsed = this.queryParser.parse(query);
805
915
  const arcContext = meta ?? req.metadata;
806
- delete parsed.filters?._policyFilters;
807
916
  const limit = Math.min(Math.max(1, parsed.limit || this.defaultLimit), this.maxLimit);
808
917
  const page = parsed.after ? void 0 : parsed.page ? Math.max(1, parsed.page) : 1;
809
918
  const sortString = parsed.sort ? Object.entries(parsed.sort).map(([k, v]) => v === -1 ? `-${k}` : k).join(",") : this.permittedDefaultSort();
@@ -1300,7 +1409,10 @@ var BaseCrudController = class {
1300
1409
  */
1301
1410
  runWritePersistence(fn) {
1302
1411
  if (!this._transactional) return fn(this.repository);
1303
- return retryingTransaction(this.repository, (txRepo, uow) => fn(txRepo, uow));
1412
+ return retryingTransaction(this.repository, (txRepo, uow) => {
1413
+ const run = () => fn(txRepo, uow);
1414
+ return uow?.session === void 0 ? run() : transactionContext.run(uow.session, run);
1415
+ });
1304
1416
  }
1305
1417
  /** Assemble the `WriteContext` a `create` verb receives. */
1306
1418
  writeContext(req, repo = this.repository, uow) {
@@ -1441,8 +1553,10 @@ var BaseCrudController = class {
1441
1553
  const options = this.queryResolver.resolve(req, this.meta(req));
1442
1554
  const cached = await this.withListCache(req, options);
1443
1555
  if (cached) return cached;
1556
+ const result = await this.executeListQuery(options, req);
1557
+ await this.runAfterHook(req, "list", result);
1444
1558
  return {
1445
- data: await this.executeListQuery(options, req),
1559
+ data: result,
1446
1560
  status: 200
1447
1561
  };
1448
1562
  }
@@ -1533,6 +1647,7 @@ var BaseCrudController = class {
1533
1647
  if (cached) return cached;
1534
1648
  const { doc, reason } = await this.executeGetQuery(id, options, req);
1535
1649
  if (!doc) this.throwNotFound(reason);
1650
+ await this.runAfterHook(req, "read", doc);
1536
1651
  return {
1537
1652
  data: doc,
1538
1653
  status: 200
@@ -2012,4 +2127,4 @@ function TreeMixin(Base) {
2012
2127
  */
2013
2128
  var BaseController = class extends SoftDeleteMixin(TreeMixin(SlugMixin(BulkMixin(BaseCrudController)))) {};
2014
2129
  //#endregion
2015
- export { BulkMixin as a, collectReadBlockedFields as c, isWriteVerbCapable as d, BodySanitizer as f, SlugMixin as i, isFieldReadable as l, AccessControl as m, TreeMixin as n, BaseCrudController as o, DEFAULT_FIELD_WRITE_DENIAL_POLICY as p, SoftDeleteMixin as r, QueryResolver as s, BaseController as t, detectOverriddenWriteSlots as u };
2130
+ export { BulkMixin as a, collectReadBlockedFields as c, isWriteVerbCapable as d, BodySanitizer as f, toRepositoryFilter as g, nativePolicyFilter as h, SlugMixin as i, isFieldReadable as l, AccessControl as m, TreeMixin as n, BaseCrudController as o, DEFAULT_FIELD_WRITE_DENIAL_POLICY as p, SoftDeleteMixin as r, QueryResolver as s, BaseController as t, detectOverriddenWriteSlots as u };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  import { createEvent, matchEventPattern } from "@classytic/primitives/events";
3
3
  //#region src/events/EventTransport.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as buildRequestScopeProjection } from "./projection-BKM0Ui-s.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as buildRequestScopeProjection } from "./projection-aevMua7Q.mjs";
3
3
  //#region src/hooks/HookSystem.ts
4
4
  /**
5
5
  * Hook System
@@ -116,9 +116,11 @@ var HookSystem = class {
116
116
  }
117
117
  async execute(ctx) {
118
118
  const key = this.getKey(ctx.resource, ctx.operation, ctx.phase);
119
- const hooks = this.hooks.get(key) ?? [];
119
+ const hooks = this.hooks.get(key);
120
120
  const wildcardKey = this.getKey("*", ctx.operation, ctx.phase);
121
- let allHooks = [...this.hooks.get(wildcardKey) ?? [], ...hooks];
121
+ const wildcardHooks = this.hooks.get(wildcardKey);
122
+ if (!hooks?.length && !wildcardHooks?.length) return ctx.data;
123
+ let allHooks = [...wildcardHooks ?? [], ...hooks ?? []];
122
124
  allHooks.sort((a, b) => a.priority - b.priority);
123
125
  if (allHooks.some((h) => h.dependsOn?.length)) allHooks = this.topologicalSort(allHooks);
124
126
  let result = ctx.data;
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import "./constants-BxCoxmiN.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import "./constants-BhzLXCfN.mjs";
3
3
  //#region src/registry/ResourceRegistry.ts
4
4
  /**
5
5
  * Resource Registry
@@ -1,9 +1,9 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { n as getUserRoles } from "./userHelpers-CWF5pbZD.mjs";
3
- import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-CoqIRrkY.mjs";
4
- import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-CojeUkS4.mjs";
5
- import { t as executePipeline } from "./pipe-BzzoEhHj.mjs";
6
- import { t as buildRequestScopeProjection } from "./projection-BKM0Ui-s.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { n as getUserRoles } from "./userHelpers-AKcB4V03.mjs";
3
+ import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-GY4kATJb.mjs";
4
+ import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-UtpiJO5R.mjs";
5
+ import { t as executePipeline } from "./pipe-DNOuCfAs.mjs";
6
+ import { t as buildRequestScopeProjection } from "./projection-aevMua7Q.mjs";
7
7
  import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
8
8
  //#region src/core/fastifyAdapter.ts
9
9
  /** Type guard for Mongoose-like documents with toObject() */
@@ -323,4 +323,27 @@ function buildActionPipelineHandler(handler, steps, operation, resourceName) {
323
323
  };
324
324
  }
325
325
  //#endregion
326
- export { createFastifyHandler as a, getControllerScope as c, createCrudHandlers as i, sendControllerResponse as l, buildPipelineHandler as n, createRequestContext as o, resolvePipelineSteps as r, getControllerContext as s, buildActionPipelineHandler as t };
326
+ //#region src/core/actionPermissions.ts
327
+ /**
328
+ * Return the effective `PermissionCheck` for a single action, or `undefined`
329
+ * when the resource declares no gate at any level.
330
+ *
331
+ * Callers decide what "no gate" means:
332
+ * - HTTP: boot-time throw in `normalizeActionsToRouterConfig`.
333
+ * - MCP: tool-generation throw in `resourceToTools` (mirrors HTTP — the
334
+ * two surfaces fail closed identically so MCP can't expose an
335
+ * unauthenticated mutating tool when the HTTP plugin lifecycle hasn't
336
+ * run).
337
+ * - OpenAPI: docs advertise the endpoint as unauthenticated.
338
+ */
339
+ function resolveActionPermission(input) {
340
+ const { action, resourcePermissions, resourceActionPermissions, globalAuth } = input;
341
+ const explicit = typeof action !== "function" && action.permissions ? action.permissions : void 0;
342
+ if (explicit) return explicit;
343
+ if (resourceActionPermissions) return resourceActionPermissions;
344
+ if (globalAuth) return globalAuth;
345
+ const updateFallback = resourcePermissions?.update;
346
+ if (updateFallback) return updateFallback;
347
+ }
348
+ //#endregion
349
+ export { createCrudHandlers as a, getControllerContext as c, resolvePipelineSteps as i, getControllerScope as l, buildActionPipelineHandler as n, createFastifyHandler as o, buildPipelineHandler as r, createRequestContext as s, resolveActionPermission as t, sendControllerResponse as u };
@@ -1,6 +1,6 @@
1
1
 
2
- import { d as UserBase } from "../base-Csjc2oF-.mjs";
3
- import "../index-BcR2bFZQ.mjs";
2
+ import { d as UserBase } from "../base-GZU3LlzP.mjs";
3
+ import "../index-C5K78SXo.mjs";
4
4
  import { FastifyPluginAsync } from "fastify";
5
5
  import { RepositoryLike } from "@classytic/repo-core/adapter";
6
6
  //#region src/audit/stores/interface.d.ts
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as transactionContext } from "../transactionContext-BYtWnwAS.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as transactionContext } from "../transactionContext-BTZzEcRo.mjs";
3
3
  import { and, anyOf, eq, gte, lt, lte } from "@classytic/repo-core/filter";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/audit/repository-audit-adapter.ts
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  //#region src/auth/audit.ts
3
3
  function compileGlobs(patterns) {
4
4
  if (patterns.length === 0) return () => false;
@@ -1,6 +1,6 @@
1
1
 
2
- import { l as PermissionCheck } from "../base-Csjc2oF-.mjs";
3
- import { Ft as AuthPluginOptions, Pt as AuthHelpers } from "../index-BcR2bFZQ.mjs";
2
+ import { l as PermissionCheck } from "../base-GZU3LlzP.mjs";
3
+ import { Ft as AuthPluginOptions, Pt as AuthHelpers } from "../index-C5K78SXo.mjs";
4
4
  import { t as ExternalOpenApiPaths } from "../externalPaths-CmGOxOtD.mjs";
5
5
  import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-qimlKV0P.mjs";
6
6
  import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
@@ -1,9 +1,9 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-Cd_yy7aW.mjs";
3
- import { n as requireSingleHeaderValue } from "../headers-CveNO9gR.mjs";
4
- import { n as getUserRoles, r as normalizeRoles } from "../userHelpers-CWF5pbZD.mjs";
5
- import { o as toJsonSchema } from "../schemaConverter-XH1RZRyu.mjs";
6
- import { E as requireOrgRole, T as requireOrgMembership, k as requireTeamMembership } from "../permissions-BAfwgBxc.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-CLid6atr.mjs";
3
+ import { n as requireSingleHeaderValue } from "../headers-DODWf7aC.mjs";
4
+ import { n as getUserRoles, r as normalizeRoles } from "../userHelpers-AKcB4V03.mjs";
5
+ import { o as toJsonSchema } from "../schemaConverter-D2_bHGil.mjs";
6
+ import { E as requireOrgRole, T as requireOrgMembership, k as requireTeamMembership } from "../permissions-CpSBlFXZ.mjs";
7
7
  import { isProductionEnv } from "@classytic/primitives/environment";
8
8
  import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
9
9
  import fp from "fastify-plugin";
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  import sjson from "secure-json-parse";
3
3
  //#region src/auth/redis-session.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  import { arcLog } from "./logger/index.mjs";
3
- import { t as ArcError } from "./errors-Cd_yy7aW.mjs";
3
+ import { t as ArcError } from "./errors-CLid6atr.mjs";
4
4
  //#region src/permissions/filter-merge.ts
5
5
  /**
6
6
  * Policy-filter composition — logical AND, never last-writer-wins.
@@ -91,6 +91,10 @@ function conjoinPolicyFilters(base, incoming) {
91
91
  delete result[key];
92
92
  }
93
93
  if (andParts.length > 0) result.$and = andParts;
94
+ for (const source of [base, incoming]) for (const symbol of Object.getOwnPropertySymbols(source)) {
95
+ const descriptor = Object.getOwnPropertyDescriptor(source, symbol);
96
+ if (descriptor) Object.defineProperty(result, symbol, descriptor);
97
+ }
94
98
  return result;
95
99
  }
96
100
  //#endregion
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as RequestScope } from "./types-ChuShpCP.mjs";
2
+ import { i as RequestScope } from "./types-Ddl9OjmE.mjs";
3
3
  import { FastifyRequest } from "fastify";
4
4
  //#region src/utils/userHelpers.d.ts
5
5
  /**
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { a as conjoinPolicyFilters } from "./authorizationDecision-B3mP_QTy.mjs";
3
- import { t as toRepositoryFilter } from "./repositoryFilter-DKny7PDn.mjs";
4
- import { i as compileAggRequest, t as adapterSupportsAggregate } from "./validate-CdQlz52F.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { a as conjoinPolicyFilters } from "./authorizationDecision-DYXBBgtg.mjs";
3
+ import { g as toRepositoryFilter } from "./BaseController-C-pofd0v.mjs";
4
+ import { i as compileAggRequest, t as adapterSupportsAggregate } from "./validate-dosAIvQU.mjs";
5
5
  //#region src/core/aggregation/buildHandler.ts
6
6
  /**
7
7
  * Framework-agnostic aggregation execution. Runs safety guards,
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-WeKvY1dE.mjs";
3
- import { t as MemoryCacheStore } from "../memory-qYSB7rMQ.mjs";
4
- import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-DhsZ0ZIc.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-GlU7XpmG.mjs";
3
+ import { t as MemoryCacheStore } from "../memory-BwedEXau.mjs";
4
+ import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-BI7Z88Cx.mjs";
5
5
  //#region src/cache/redis.ts
6
6
  /**
7
7
  * Redis-backed cache store.
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-CEvvVXTC.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/plugins/caching.ts
5
5
  var caching_exports = /* @__PURE__ */ __exportAll({
@@ -1,7 +1,7 @@
1
1
 
2
- import { L as RequestWithExtras } from "../index-BcR2bFZQ.mjs";
2
+ import { L as RequestWithExtras } from "../index-C5K78SXo.mjs";
3
3
  import { A as CleanupStepResult, B as VerificationResult, C as CleanupResult, D as CleanupRunStore, E as CleanupRunStatus, F as PurgeEvidence, I as PurgeResourceResult, L as PurgeStrategyKind, M as CleanupWriteFence, N as DEFAULT_CLEANUP_LIMITS, O as CleanupRunTransitionPatch, P as PurgeActor, R as PurgeVerificationSummary, S as CleanupRecipe, T as CleanupRunCreateResult, _ as CleanupPermissions, a as CleanupEvidenceStore, b as CleanupPlanItem, c as CleanupInput, d as CleanupLease, f as CleanupLimits, g as CleanupPermissionCheck, h as CleanupOutcomeStatus, i as CleanupContext, j as CleanupStepRunStatus, k as CleanupStepProgress, l as CleanupJob, m as CleanupManifest, n as CLEANUP_TERMINAL_STATUSES, o as CleanupExecutionContext, p as CleanupLogger, r as CleanupCancelRequest, s as CleanupFinalizationPayload, t as Availability, u as CleanupJobQueue, v as CleanupPlan, w as CleanupRun, x as CleanupProgressSummary, y as CleanupPlanDraft, z as VerificationCheck } from "../types-DVLddu2q.mjs";
4
- import { t as ArcModule } from "../types-BcTumAz_.mjs";
4
+ import { t as ArcModule } from "../types-DDD5RKTu.mjs";
5
5
  import { PurgeStrategyKind as PurgeStrategyKind$1 } from "@classytic/primitives/retention";
6
6
  import { canonicalJson } from "@classytic/primitives/canonical";
7
7
  import { CleanupStep } from "@classytic/repo-core/cleanup";
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { g as getUserId } from "../types-CoqIRrkY.mjs";
3
- import { t as defineResource } from "../defineResource-DR9vsUYy.mjs";
4
- import { t as defineModule } from "../module-CwKsgZXp.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { g as getUserId } from "../types-GY4kATJb.mjs";
3
+ import { t as defineResource } from "../defineResource-BOrGeocE.mjs";
4
+ import { t as defineModule } from "../module-Bh5OC7ww.mjs";
5
5
  import { createPurgeEvidence } from "@classytic/primitives/retention";
6
6
  import { canonicalJson, sha256Hex } from "@classytic/primitives/canonical";
7
7
  //#region src/cleanup/errors.ts
@@ -1,5 +1,5 @@
1
1
 
2
- import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-BcR2bFZQ.mjs";
2
+ import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-C5K78SXo.mjs";
3
3
  //#region src/cli/commands/describe.d.ts
4
4
  interface DescribedResource {
5
5
  name: string;
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as CRUD_OPERATIONS } from "../../constants-BxCoxmiN.mjs";
3
- import { n as stringifyMeasureMap } from "../../ResourceRegistry-DoALUlOO.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as CRUD_OPERATIONS } from "../../constants-BhzLXCfN.mjs";
3
+ import { n as stringifyMeasureMap } from "../../ResourceRegistry-yuD1Ra53.mjs";
4
4
  import { resolve } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
6
  //#region src/cli/commands/describe.ts
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as buildOpenApiSpec } from "../../openapi-BN5cQaO2.mjs";
3
- import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-DwaxNcE2.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as buildOpenApiSpec } from "../../openapi-ItLH12_L.mjs";
3
+ import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-s6aGs6-0.mjs";
4
4
  import { dirname, resolve } from "node:path";
5
5
  import { mkdirSync, writeFileSync } from "node:fs";
6
6
  //#region src/cli/commands/docs.ts
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  import { dirname, join, parse, resolve } from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  //#region src/cli/commands/doctor.ts
@@ -1,3 +1,3 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as generate } from "../../generate-DiB0nHPR.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as generate } from "../../generate-BoAVy3_b.mjs";
3
3
  export { generate };
@@ -1,3 +1,3 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as init } from "../../init-mdJ-v4Xp.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as init } from "../../init-Ce4R0ZGK.mjs";
3
3
  export { init };
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-DwaxNcE2.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-s6aGs6-0.mjs";
3
3
  //#region src/cli/commands/introspect.ts
4
4
  function describePermission(value) {
5
5
  if (!value) return "none";
@@ -1,8 +1,8 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  import { describe } from "./commands/describe.mjs";
3
3
  import { exportDocs } from "./commands/docs.mjs";
4
4
  import { doctor } from "./commands/doctor.mjs";
5
- import { t as generate } from "../generate-DiB0nHPR.mjs";
6
- import { t as init } from "../init-mdJ-v4Xp.mjs";
5
+ import { t as generate } from "../generate-BoAVy3_b.mjs";
6
+ import { t as init } from "../init-Ce4R0ZGK.mjs";
7
7
  import { introspect } from "./commands/introspect.mjs";
8
8
  export { describe, doctor, exportDocs, generate, init, introspect };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  //#region src/constants.ts
3
3
  /**
4
4
  * Arc Framework Constants — Single Source of Truth
@@ -1,5 +1,6 @@
1
1
 
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { CacheAdapter } from "@classytic/repo-core/cache";
3
4
  //#region src/context/requestContext.d.ts
4
5
  /**
5
6
  * Shape of the request-scoped context store.
@@ -85,6 +86,20 @@ declare const requestContext: {
85
86
  */
86
87
  declare function getTraceHeaders(): Record<string, string> | undefined;
87
88
  //#endregion
89
+ //#region src/context/requestScopedCache.d.ts
90
+ /**
91
+ * The cache for the CURRENT request, or `undefined` outside one.
92
+ *
93
+ * Lazily created on first use, so a request that never touches a cached
94
+ * repository allocates nothing.
95
+ */
96
+ declare function requestScopedCache(): CacheAdapter | undefined;
97
+ /**
98
+ * Whether the current request has allocated a cache yet. Diagnostics and
99
+ * tests only — never branch application behaviour on this.
100
+ */
101
+ declare function hasRequestScopedCache(): boolean;
102
+ //#endregion
88
103
  //#region src/context/transactionContext.d.ts
89
104
  /** DB session/transaction handle. Typed as unknown — arc is DB-agnostic. */
90
105
  type DbSession = unknown;
@@ -109,4 +124,4 @@ declare const transactionContext: {
109
124
  storage: AsyncLocalStorage<unknown>;
110
125
  };
111
126
  //#endregion
112
- export { type DbSession, type RequestStore, getTraceHeaders, requestContext, transactionContext };
127
+ export { type DbSession, type RequestStore, getTraceHeaders, hasRequestScopedCache, requestContext, requestScopedCache, transactionContext };
@@ -1,4 +1,64 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { n as requestContext, t as getTraceHeaders } from "../requestContext-uYE35JxI.mjs";
3
- import { t as transactionContext } from "../transactionContext-BYtWnwAS.mjs";
4
- export { getTraceHeaders, requestContext, transactionContext };
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { n as requestContext, t as getTraceHeaders } from "../requestContext-DzOUGNdY.mjs";
3
+ import { t as transactionContext } from "../transactionContext-BTZzEcRo.mjs";
4
+ //#region src/context/requestScopedCache.ts
5
+ /**
6
+ * The slot the per-request store hangs off. Underscore-prefixed to sit apart
7
+ * from the fields arc's own hooks populate on `RequestStore`.
8
+ */
9
+ const SLOT = "_requestCache";
10
+ /**
11
+ * A `Map`-backed `CacheAdapter` with NO eviction and NO TTL — both deliberate.
12
+ *
13
+ * The store is unreachable once the request's async context ends, so it is
14
+ * garbage in milliseconds and bounded by what one request touches; an LRU
15
+ * would guard a lifetime that cannot occur. TTL is accepted and ignored
16
+ * because repo-core stamps freshness INTO the envelope and re-checks on read —
17
+ * honouring it here too would put two clocks on one question.
18
+ */
19
+ function createRequestCacheAdapter() {
20
+ const map = /* @__PURE__ */ new Map();
21
+ return {
22
+ async get(key) {
23
+ return map.get(key);
24
+ },
25
+ async set(key, value, _ttlSeconds) {
26
+ map.set(key, value);
27
+ },
28
+ async delete(key) {
29
+ map.delete(key);
30
+ },
31
+ /**
32
+ * Ignores the pattern and wipes the whole store. Over-invalidating within
33
+ * one request costs at most a re-read; pattern matching is complexity a
34
+ * store that dies in milliseconds does not earn.
35
+ */
36
+ async clear(_pattern) {
37
+ map.clear();
38
+ }
39
+ };
40
+ }
41
+ /**
42
+ * The cache for the CURRENT request, or `undefined` outside one.
43
+ *
44
+ * Lazily created on first use, so a request that never touches a cached
45
+ * repository allocates nothing.
46
+ */
47
+ function requestScopedCache() {
48
+ const store = requestContext.get();
49
+ if (!store) return void 0;
50
+ const existing = store[SLOT];
51
+ if (existing) return existing;
52
+ const created = createRequestCacheAdapter();
53
+ store[SLOT] = created;
54
+ return created;
55
+ }
56
+ /**
57
+ * Whether the current request has allocated a cache yet. Diagnostics and
58
+ * tests only — never branch application behaviour on this.
59
+ */
60
+ function hasRequestScopedCache() {
61
+ return requestContext.get()?.[SLOT] !== void 0;
62
+ }
63
+ //#endregion
64
+ export { getTraceHeaders, hasRequestScopedCache, requestContext, requestScopedCache, transactionContext };