@classytic/arc 2.32.0 → 2.34.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 (193) hide show
  1. package/README.md +1 -1
  2. package/bin/arc.js +58 -17
  3. package/dist/{BaseController-Dk34dxhI.mjs → BaseController-CbkuhNI2.mjs} +329 -35
  4. package/dist/{EventTransport-2uVV34WU.d.mts → EventTransport-BEVP50ka.d.mts} +16 -0
  5. package/dist/{EventTransport-BV10D0oG.mjs → EventTransport-tq4CEB-q.mjs} +39 -2
  6. package/dist/{HookSystem-vimw5D7Z.mjs → HookSystem-DqXkMXkB.mjs} +2 -2
  7. package/dist/{ResourceRegistry-BVr7PgA0.mjs → ResourceRegistry-DoALUlOO.mjs} +3 -2
  8. package/dist/{actionPermissions-Dti7ebyv.mjs → actionPermissions-CUeCmpUd.mjs} +1 -1
  9. package/dist/audit/index.d.mts +2 -2
  10. package/dist/audit/index.mjs +2 -2
  11. package/dist/auth/audit.mjs +1 -1
  12. package/dist/auth/index.d.mts +2 -2
  13. package/dist/auth/index.mjs +39 -7
  14. package/dist/auth/redis-session.mjs +1 -1
  15. package/dist/{authorizationDecision-__T3EJDA.mjs → authorizationDecision-CMMLWmuZ.mjs} +6 -2
  16. package/dist/{types-B7XeLEgK.d.mts → base-Csjc2oF-.d.mts} +74 -74
  17. package/dist/{buildHandler-Dn7rzUJo.mjs → buildHandler-BrdYrG_1.mjs} +4 -4
  18. package/dist/cache/index.mjs +4 -4
  19. package/dist/{caching-DFJqHyFs.mjs → caching-BcGmvnHh.mjs} +2 -2
  20. package/dist/cleanup/index.d.mts +17 -5
  21. package/dist/cleanup/index.mjs +240 -7
  22. package/dist/cli/commands/describe.d.mts +1 -1
  23. package/dist/cli/commands/describe.mjs +3 -3
  24. package/dist/cli/commands/docs.mjs +3 -3
  25. package/dist/cli/commands/doctor.mjs +1 -1
  26. package/dist/cli/commands/generate.mjs +2 -2
  27. package/dist/cli/commands/init.mjs +2 -2
  28. package/dist/cli/commands/introspect.mjs +2 -2
  29. package/dist/cli/index.mjs +3 -3
  30. package/dist/{constants-J99LGTJI.mjs → constants-BxCoxmiN.mjs} +1 -1
  31. package/dist/context/index.mjs +3 -3
  32. package/dist/core/index.d.mts +3 -3
  33. package/dist/core/index.mjs +9 -8
  34. package/dist/{core-BwwmNSp7.mjs → core-BYKa8wHu.mjs} +3 -3
  35. package/dist/{createActionRouter-DkJZWo0m.mjs → createActionRouter-D6T0jWhS.mjs} +8 -8
  36. package/dist/{createAggregationRouter-B1exOVD8.mjs → createAggregationRouter-QLV2PrWP.mjs} +7 -7
  37. package/dist/{createApp-Bv0m-Q6_.mjs → createApp-zR-DEesr.mjs} +128 -24
  38. package/dist/{defineAggregation-gX4No1gt.mjs → defineAggregation-Nv-DwfOf.mjs} +1 -1
  39. package/dist/{defineResource-XFPPUu2c.mjs → defineResource-ZhuLonLm.mjs} +297 -23
  40. package/dist/discovery/index.mjs +1 -1
  41. package/dist/docs/index.d.mts +1 -1
  42. package/dist/docs/index.mjs +3 -3
  43. package/dist/{elevation-DNXXxgjl.mjs → elevation-B0tlY8y1.mjs} +4 -4
  44. package/dist/encryption/index.mjs +3 -3
  45. package/dist/{errorHandler-B2AZUJUj.mjs → errorHandler-C7xBqHsI.mjs} +42 -5
  46. package/dist/{errorHandler-CpYUJmPA.d.mts → errorHandler-DI5AN2b5.d.mts} +14 -3
  47. package/dist/{errors-Bh1KK5ps.mjs → errors-Cd_yy7aW.mjs} +1 -1
  48. package/dist/{eventPlugin-GF5nnBIq.d.mts → eventPlugin-DSNEZWfV.d.mts} +29 -1
  49. package/dist/{eventPlugin-Dk_TyXeE.mjs → eventPlugin-Dar8qRaj.mjs} +65 -9
  50. package/dist/events/index.d.mts +5 -351
  51. package/dist/events/index.mjs +5 -780
  52. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  53. package/dist/events/transports/redis-stream-entry.mjs +1 -1
  54. package/dist/events/transports/redis.d.mts +1 -1
  55. package/dist/events/transports/redis.mjs +1 -1
  56. package/dist/factory/index.d.mts +20 -5
  57. package/dist/factory/index.mjs +7 -7
  58. package/dist/{fastify-augmentation-BppG5bMT.d.mts → fastify-augmentation-DVsTAy6X.d.mts} +3 -3
  59. package/dist/{fields-DEi65pGj.mjs → fields-CojeUkS4.mjs} +1 -1
  60. package/dist/{generate-CRSG0jTv.mjs → generate-DiB0nHPR.mjs} +2 -2
  61. package/dist/{headers-BXjQYbeC.mjs → headers-CveNO9gR.mjs} +2 -2
  62. package/dist/{health-5XnTa2uo.mjs → health-CasG2A28.mjs} +1 -1
  63. package/dist/hooks/index.d.mts +1 -1
  64. package/dist/hooks/index.mjs +2 -2
  65. package/dist/idempotency/index.d.mts +45 -1
  66. package/dist/idempotency/index.mjs +90 -9
  67. package/dist/idempotency/redis.mjs +1 -1
  68. package/dist/{index-zq6VrjaB.d.mts → index-B-DvUzsr.d.mts} +12 -2
  69. package/dist/{index-CpzxqePp.d.mts → index-BcR2bFZQ.d.mts} +707 -373
  70. package/dist/{index-ZWhLTp_s.d.mts → index-C5NnMEKF.d.mts} +1 -1
  71. package/dist/{index-DXlHwvuE.d.mts → index-CFAJEWCX.d.mts} +18 -5
  72. package/dist/{index-C-481Zf2.d.mts → index-ugr1tQlW.d.mts} +17 -1
  73. package/dist/index.d.mts +6 -6
  74. package/dist/index.mjs +13 -12
  75. package/dist/{init-BbfP8aky.mjs → init-mdJ-v4Xp.mjs} +18 -12
  76. package/dist/integrations/event-gateway.d.mts +1 -1
  77. package/dist/integrations/event-gateway.mjs +2 -2
  78. package/dist/integrations/index.d.mts +2 -2
  79. package/dist/integrations/index.mjs +1 -1
  80. package/dist/integrations/jobs/index.d.mts +1 -1
  81. package/dist/integrations/jobs/index.mjs +99 -97
  82. package/dist/integrations/mcp/index.d.mts +12 -3
  83. package/dist/integrations/mcp/index.mjs +2 -2
  84. package/dist/integrations/mcp/testing.d.mts +1 -1
  85. package/dist/integrations/mcp/testing.mjs +2 -2
  86. package/dist/integrations/streamline.mjs +3 -3
  87. package/dist/integrations/webhooks.d.mts +124 -1
  88. package/dist/integrations/webhooks.mjs +180 -2
  89. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  90. package/dist/integrations/websocket-redis.mjs +1 -1
  91. package/dist/integrations/websocket.mjs +1 -1
  92. package/dist/{interface-BP4IJCBO.mjs → interface-DznJrm9R.mjs} +1 -1
  93. package/dist/{jsonBody-DDSR2HFl.mjs → jsonBody-CfGH-GxO.mjs} +2 -2
  94. package/dist/{keys-B-HBTGXl.mjs → keys-WeKvY1dE.mjs} +1 -1
  95. package/dist/{loadResources-DisEAmkm.mjs → loadResources-Bj-aSsXi.mjs} +2 -2
  96. package/dist/{loadResourcesFromEntry-D1MJp13f.mjs → loadResourcesFromEntry-DwaxNcE2.mjs} +2 -2
  97. package/dist/logger/index.mjs +1 -1
  98. package/dist/{mcpPlugin-CpfVpKHE.mjs → mcpPlugin-DX3PHD7K.mjs} +60 -21
  99. package/dist/{memory-DsuWnj4T.mjs → memory-qYSB7rMQ.mjs} +2 -2
  100. package/dist/{metrics-DdQL8syd.mjs → metrics-D7uchLjN.mjs} +2 -2
  101. package/dist/middleware/index.d.mts +1 -1
  102. package/dist/middleware/index.mjs +3 -3
  103. package/dist/migrations/index.mjs +2 -2
  104. package/dist/{module-Br81am6m.mjs → module-CwKsgZXp.mjs} +1 -1
  105. package/dist/{multipartBody-BnkBLN7v.mjs → multipartBody-4w-S3-PU.mjs} +1 -1
  106. package/dist/{openapi-DIRzvxC0.mjs → openapi-CkV5mQ53.mjs} +6 -6
  107. package/dist/outbox-admin/index.d.mts +2 -2
  108. package/dist/outbox-admin/index.mjs +4 -4
  109. package/dist/outbox-module-C1Bs_FXF.d.mts +382 -0
  110. package/dist/outbox-module-D1QfHqO4.mjs +846 -0
  111. package/dist/permissions/index.d.mts +2 -2
  112. package/dist/permissions/index.mjs +5 -5
  113. package/dist/{permissions-Dl_m0NCe.mjs → permissions-CP47jdi7.mjs} +25 -6
  114. package/dist/{pipe-ELHCP3zj.mjs → pipe-BzzoEhHj.mjs} +2 -2
  115. package/dist/pipeline/index.d.mts +1 -1
  116. package/dist/pipeline/index.mjs +2 -2
  117. package/dist/{pipeline-CPA-jx0M.mjs → pipeline-q69DZzgG.mjs} +6 -6
  118. package/dist/plugins/index.d.mts +6 -6
  119. package/dist/plugins/index.mjs +16 -16
  120. package/dist/plugins/response-cache.mjs +2 -2
  121. package/dist/plugins/tracing-entry.mjs +2 -2
  122. package/dist/{pluralize-C2ZkKbsn.mjs → pluralize-B7xf8qZF.mjs} +1 -1
  123. package/dist/{preloadResources-CDs87fTw.mjs → preloadResources-BkyqeTDz.mjs} +1 -1
  124. package/dist/{preloadResources-T1hmlTEZ.d.mts → preloadResources-g07ldtzh.d.mts} +30 -7
  125. package/dist/presets/filesUpload.d.mts +2 -2
  126. package/dist/presets/filesUpload.mjs +5 -5
  127. package/dist/presets/index.d.mts +2 -2
  128. package/dist/presets/index.mjs +2 -2
  129. package/dist/presets/multiTenant.d.mts +1 -1
  130. package/dist/presets/multiTenant.mjs +3 -3
  131. package/dist/presets/search.d.mts +2 -2
  132. package/dist/presets/search.mjs +2 -2
  133. package/dist/{presets-Dntd_pUO.mjs → presets-BAPijK_A.mjs} +4 -4
  134. package/dist/projection-BKM0Ui-s.mjs +42 -0
  135. package/dist/{queryCachePlugin-Dhps-Ldt.mjs → queryCachePlugin-DhsZ0ZIc.mjs} +49 -7
  136. package/dist/{rateLimit-C7mmAGs3.mjs → rateLimit-D4-ASwsI.mjs} +2 -2
  137. package/dist/{rateLimitKey-CccEFBfk.mjs → rateLimitKey-XngqVNEV.mjs} +1 -1
  138. package/dist/{realtime-DVvVZZFz.mjs → realtime-G1TC6Yzc.mjs} +9 -9
  139. package/dist/{redis-stream-qN6A-RFv.d.mts → redis-stream-mwLWlfHG.d.mts} +1 -1
  140. package/dist/{registerRoute-B_QrjYZS.mjs → registerRoute-Ba8v0PCa.mjs} +4 -4
  141. package/dist/registry/index.d.mts +3 -3
  142. package/dist/registry/index.mjs +3 -3
  143. package/dist/{registry-DqUmzA9W.mjs → registry-CLxCR5LF.mjs} +2 -2
  144. package/dist/{renewingLease-BpP9wwj7.mjs → renewingLease-BnWtbA2U.mjs} +1 -1
  145. package/dist/{replyHelpers-DS-P0b32.mjs → replyHelpers-BSBVCwL3.mjs} +2 -2
  146. package/dist/repositoryFilter-BANpz6BX.mjs +78 -0
  147. package/dist/{requestContext-B6vQF-sy.mjs → requestContext-uYE35JxI.mjs} +1 -1
  148. package/dist/{rolldown-runtime-BhflRrBs.mjs → rolldown-runtime-DNg-KNCk.mjs} +1 -1
  149. package/dist/runtimeCapabilities-Dm88DaTW.mjs +49 -0
  150. package/dist/{schedules-C2Q3uv2d.mjs → schedules-CAcZgia0.mjs} +3 -3
  151. package/dist/{schemaConverter-Crjvczi_.mjs → schemaConverter-XH1RZRyu.mjs} +1 -1
  152. package/dist/{schemaIR-CnyKK_1j.mjs → schemaIR-CDvclAew.mjs} +2 -2
  153. package/dist/schemas/index.mjs +1 -1
  154. package/dist/scim/index.mjs +2 -2
  155. package/dist/scope/index.mjs +5 -5
  156. package/dist/{scope-afz_Ieek.mjs → scope-BaWcqBvr.mjs} +4 -4
  157. package/dist/{simpleEqualityMatcher-C4KiMWUa.mjs → simpleEqualityMatcher-Se8VrsaP.mjs} +1 -1
  158. package/dist/{sse-B9P4Y-UI.mjs → sse-Cf0jUHsh.mjs} +5 -5
  159. package/dist/{sseStream-B3zb_d1h.mjs → sseStream-Bvpi-UXQ.mjs} +2 -2
  160. package/dist/{store-helpers-DmEMHlPQ.mjs → store-helpers-B88p99Z2.mjs} +1 -1
  161. package/dist/{streaming-DPZdQmDz.mjs → streaming-DsT1B5zJ.mjs} +1 -1
  162. package/dist/{subscribe-helpers-sp5xTDVA.mjs → subscribe-helpers-CLIOHsOK.mjs} +2 -2
  163. package/dist/sync/changelog.mjs +1 -1
  164. package/dist/testing/cleanupStoreContract.d.mts +1 -1
  165. package/dist/testing/cleanupStoreContract.mjs +3 -1
  166. package/dist/testing/index.d.mts +24 -12
  167. package/dist/testing/index.mjs +24 -12
  168. package/dist/testing/outboxStoreContract.mjs +1 -1
  169. package/dist/testing/storageContract.mjs +1 -1
  170. package/dist/{transactionContext-BvLKvOIL.mjs → transactionContext-BYtWnwAS.mjs} +1 -1
  171. package/dist/{typeGuards-B_5-hKjJ.mjs → typeGuards-jjKJDN13.mjs} +1 -1
  172. package/dist/types/index.d.mts +3 -3
  173. package/dist/types/index.mjs +1 -1
  174. package/dist/types/storage.mjs +1 -1
  175. package/dist/{types-CT5vu6M0.d.mts → types-BcTumAz_.d.mts} +41 -7
  176. package/dist/{types-CwcUQ610.d.mts → types-BfC83KsP.d.mts} +15 -1
  177. package/dist/{types-DXYX7dIR.mjs → types-CoqIRrkY.mjs} +4 -4
  178. package/dist/{types-C4QE8qnm.d.mts → types-DVLddu2q.d.mts} +144 -1
  179. package/dist/usage/index.d.mts +1 -1
  180. package/dist/usage/index.mjs +0 -0
  181. package/dist/{userHelpers-BNsOXzOd.mjs → userHelpers-CWF5pbZD.mjs} +1 -1
  182. package/dist/utils/index.d.mts +57 -5
  183. package/dist/utils/index.mjs +12 -11
  184. package/dist/{utils-BtOha_Sp.mjs → utils-h1V_sT-t.mjs} +38 -7
  185. package/dist/{validate-PMCPR9El.mjs → validate-CdQlz52F.mjs} +2 -2
  186. package/dist/{versioning-D_rhHfZm.d.mts → versioning-DEs-_hFi.d.mts} +1 -1
  187. package/dist/{versioning-CKv2jz_U.mjs → versioning-rZbyLVD-.mjs} +3 -3
  188. package/package.json +17 -16
  189. package/skills/arc/SKILL.md +96 -4
  190. package/skills/arc/references/api-reference.md +19 -0
  191. package/skills/arc/references/production.md +2 -2
  192. package/dist/projection-CjHhM5W1.mjs +0 -18
  193. package/dist/repositoryFilter-DotlxG3L.mjs +0 -45
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { g as getUserId } from "../types-DXYX7dIR.mjs";
3
- import { t as defineResource } from "../defineResource-XFPPUu2c.mjs";
4
- import { t as defineModule } from "../module-Br81am6m.mjs";
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-ZhuLonLm.mjs";
4
+ import { t as defineModule } from "../module-CwKsgZXp.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
@@ -85,6 +85,18 @@ const CleanupErrors = {
85
85
  status
86
86
  });
87
87
  },
88
+ /**
89
+ * The operator's exclusion list cannot be honoured as written.
90
+ *
91
+ * A 400 rather than a filter: dropping an unrecognised exclusion leaves
92
+ * someone believing they narrowed a destructive run when they did not, and
93
+ * dropping a PROTECTIVE one would let a guard be switched off from the same
94
+ * request that starts the run. `steps` carries the offending ids so a UI can
95
+ * point at them.
96
+ */
97
+ invalidExclusion(reason, steps) {
98
+ return new CleanupError("CLEANUP_INVALID_EXCLUSION", 400, reason, { steps: [...steps] });
99
+ },
88
100
  forbidden(operation) {
89
101
  return new CleanupError("CLEANUP_FORBIDDEN", 403, `Not authorized to ${operation} cleanup.`, { operation });
90
102
  }
@@ -102,6 +114,53 @@ function stepContextOf(ctx, parameters) {
102
114
  logger: ctx.logger
103
115
  };
104
116
  }
117
+ /**
118
+ * Resolve the operator's exclusion list against the recipe's real steps.
119
+ *
120
+ * REFUSES rather than filters. Two rejections, both of which a permissive
121
+ * version would turn into a silent lie:
122
+ *
123
+ * - **unknown id** — a typo, or a step that has since been removed. Dropping
124
+ * it leaves the operator believing they narrowed a destructive run when the
125
+ * excluded domain will in fact be purged. A free-text ignore-list that
126
+ * silently tolerates unknown entries carries exactly this hazard.
127
+ * - **a protective step** — the property an ignore-list alone cannot give. A
128
+ * guard deletes nothing; it stands between the run and records that must
129
+ * never be purged. If it can be switched off from the same screen that
130
+ * starts the run, it is advice, not a guard. `destructive: false` alone is
131
+ * not the test — a projection rebuild is also non-destructive and IS
132
+ * legitimately excludable — so the marker is the step's own
133
+ * `disposition: 'protect'`.
134
+ *
135
+ * Returns the validated set; throws `CleanupErrors.invalidInput` otherwise.
136
+ */
137
+ function resolveExclusions(steps, requested) {
138
+ if (!requested || requested.length === 0) return /* @__PURE__ */ new Set();
139
+ const byId = new Map(steps.map((s) => [s.id, s]));
140
+ const unknown = [];
141
+ const protectedIds = [];
142
+ for (const id of requested) {
143
+ const step = byId.get(id);
144
+ if (!step) {
145
+ unknown.push(id);
146
+ continue;
147
+ }
148
+ if (isProtective(step)) protectedIds.push(id);
149
+ }
150
+ if (unknown.length > 0) throw CleanupErrors.invalidExclusion(`excludeSteps names step(s) this recipe does not have: ${unknown.join(", ")}. Refusing rather than ignoring — a dropped exclusion means the run touches data the operator believed they had left out.`, unknown);
151
+ if (protectedIds.length > 0) throw CleanupErrors.invalidExclusion(`excludeSteps names protective step(s): ${protectedIds.join(", ")}. A guard deletes nothing; it refuses the run when records that must never be purged are present. It cannot be switched off from the same request that starts the run.`, protectedIds);
152
+ return new Set(requested);
153
+ }
154
+ /**
155
+ * Is this step a GUARD?
156
+ *
157
+ * Asked of the step's declared disposition rather than `destructive`, because
158
+ * `destructive: false` covers guards AND projection rebuilds — and a rebuild is
159
+ * perfectly reasonable to exclude.
160
+ */
161
+ function isProtective(step) {
162
+ return step.disposition === "protect";
163
+ }
105
164
  function errMessage(err) {
106
165
  return err instanceof Error ? err.message : String(err);
107
166
  }
@@ -131,16 +190,63 @@ function recipeFromSteps(input) {
131
190
  },
132
191
  async plan(cleanupInput, ctx) {
133
192
  const stepCtx = stepContextOf(ctx, cleanupInput.parameters ?? {});
193
+ const excluded = resolveExclusions(steps, cleanupInput.excludeSteps);
134
194
  const items = [];
135
195
  const blockers = /* @__PURE__ */ new Set();
136
196
  const retains = new Set(input.retains ?? []);
137
197
  const rebuildActions = /* @__PURE__ */ new Set();
138
198
  const warnings = new Set(input.warnings ?? []);
139
199
  for (const step of steps) {
200
+ /**
201
+ * An excluded step is NOT estimated.
202
+ *
203
+ * Estimating it would run its counting queries for a line the operator
204
+ * has already taken out — cost with no consequence — and, worse, would
205
+ * surface its blockers, so leaving a domain out of a run could still be
206
+ * refused because of that domain. A line the run will not touch must not
207
+ * be able to stop the run.
208
+ */
209
+ if (excluded.has(step.id)) {
210
+ items.push({
211
+ stepId: step.id,
212
+ resource: step.resource,
213
+ estimated: 0,
214
+ excluded: true,
215
+ retained: "excluded from this run by the operator"
216
+ });
217
+ continue;
218
+ }
140
219
  const est = await step.estimate(stepCtx);
220
+ /**
221
+ * Disposition comes from the STEP, not from `step.destructive`.
222
+ *
223
+ * (See the exclusion handling above for why `'protect'` also decides
224
+ * whether a line may be left out of a run.)
225
+ *
226
+ * They are close but not the same, and conflating them is how the bug
227
+ * this fixes arose. `destructive: false` covers BOTH a protective guard
228
+ * (counts what it defends) and a projection rebuild (counts what it
229
+ * recomputes) — opposite meanings for the headline. A step that says
230
+ * nothing means `'remove'`, which is what every purge step is.
231
+ */
232
+ /**
233
+ * A step may only PROTECT if it said so on the step itself.
234
+ *
235
+ * `resolveExclusions` runs before any estimate — it has to, since it
236
+ * decides which steps to estimate at all — so it can read only
237
+ * `step.disposition`. A guard that declared `'protect'` from its
238
+ * ESTIMATE instead was therefore excludable: the plan line came back
239
+ * marked protective, looked defended, and `excludeSteps: [thatId]`
240
+ * still switched it off. That is precisely the property this feature
241
+ * exists to make impossible, so the mismatch is an authoring error
242
+ * rather than a silent downgrade.
243
+ */
244
+ if (est.disposition === "protect" && step.disposition !== "protect") throw CleanupErrors.invalidExclusion(`step '${step.id}' returned disposition 'protect' from its estimate but does not declare \`disposition: 'protect'\` on the step. Exclusions are resolved before estimates run, so a guard declared only at estimate time can be excluded — switching off the check it exists to enforce. Declare it on the step.`, [step.id]);
141
245
  items.push({
246
+ stepId: step.id,
142
247
  resource: est.resource,
143
248
  estimated: est.estimated,
249
+ ...(est.disposition ?? step.disposition) !== void 0 ? { disposition: est.disposition ?? step.disposition } : {},
144
250
  ...est.retained !== void 0 ? { retained: est.retained } : {},
145
251
  ...est.blockers && est.blockers.length > 0 ? { blockers: est.blockers } : {}
146
252
  });
@@ -161,8 +267,44 @@ function recipeFromSteps(input) {
161
267
  async execute(plan, ctx) {
162
268
  const parameters = plan.parameters ?? {};
163
269
  const results = [];
270
+ /**
271
+ * Re-resolved from the SEALED plan, never from a fresh caller input.
272
+ *
273
+ * The exclusion set is part of the digest the operator confirmed against,
274
+ * so taking it from the plan is what makes consent binding: an execute
275
+ * cannot widen the run beyond what was previewed. Re-validating here also
276
+ * means a step that has since become protective refuses rather than being
277
+ * quietly dropped.
278
+ */
279
+ const excluded = resolveExclusions(steps, plan.excludeSteps);
280
+ /**
281
+ * Lifecycle reporting is OPTIONAL on the context — a host service that
282
+ * predates it simply gets no per-step view, rather than a crash. Resolved
283
+ * once so the loop below reads as bookkeeping, not feature detection.
284
+ */
285
+ const reportState = ctx.onStepState?.bind(ctx) ?? (async () => {});
164
286
  for (const step of steps) {
165
287
  await ctx.throwIfCancelled();
288
+ if (excluded.has(step.id)) {
289
+ await reportState({
290
+ stepId: step.id,
291
+ resource: step.resource,
292
+ status: "skipped",
293
+ processed: 0,
294
+ startedAt: ctx.now,
295
+ completedAt: ctx.now,
296
+ detail: "excluded by the operator"
297
+ });
298
+ continue;
299
+ }
300
+ const startedAt = ctx.now;
301
+ await reportState({
302
+ stepId: step.id,
303
+ resource: step.resource,
304
+ status: "running",
305
+ processed: 0,
306
+ startedAt
307
+ });
166
308
  let reported = 0;
167
309
  const execCtx = {
168
310
  ...stepContextOf(ctx, parameters),
@@ -211,6 +353,25 @@ function recipeFromSteps(input) {
211
353
  ...outcome.error !== void 0 ? { error: outcome.error } : {},
212
354
  ...outcome.cursor !== void 0 ? { cursor: outcome.cursor } : {}
213
355
  });
356
+ /**
357
+ * `skipped` is a REAL outcome, not "completed with zero".
358
+ *
359
+ * A step that had nothing in scope and a step that removed 4,000 rows
360
+ * both end `ok`, and reading a bare 0 as success is how an operator
361
+ * concludes a phase ran when its scope was simply empty. Three states
362
+ * alone only CATEGORISE the outcome; the `detail` is what makes it
363
+ * answerable.
364
+ */
365
+ const isSkip = outcome.ok && outcome.processed === 0;
366
+ await reportState({
367
+ stepId: step.id,
368
+ resource: outcome.resource,
369
+ status: outcome.ok ? isSkip ? "skipped" : "completed" : "failed",
370
+ processed: outcome.processed,
371
+ startedAt,
372
+ completedAt: ctx.now,
373
+ ...outcome.ok ? isSkip ? { detail: "nothing in scope" } : {} : { detail: outcome.error ?? "step failed" }
374
+ });
214
375
  if (!outcome.ok) break;
215
376
  }
216
377
  const allOk = results.every((r) => r.ok);
@@ -465,12 +626,15 @@ function planMaterial(plan) {
465
626
  items: plan.items.map((i) => ({
466
627
  resource: i.resource,
467
628
  estimated: i.estimated,
629
+ disposition: i.disposition ?? "remove",
630
+ excluded: i.excluded ?? false,
468
631
  retained: i.retained ?? null,
469
632
  blockers: [...i.blockers ?? []].sort()
470
633
  })).sort((a, b) => a.resource < b.resource ? -1 : a.resource > b.resource ? 1 : a.estimated - b.estimated),
471
634
  retains: [...plan.retains].sort(),
472
635
  blockers: [...plan.blockers].sort(),
473
636
  rebuildActions: [...plan.rebuildActions].sort(),
637
+ excludeSteps: [...plan.excludeSteps].sort(),
474
638
  estimatedTotal: plan.estimatedTotal,
475
639
  confirmationPhrase: plan.confirmationPhrase
476
640
  };
@@ -586,10 +750,22 @@ function createCleanupService(deps) {
586
750
  };
587
751
  }
588
752
  async function seal(recipe, input, ctx) {
589
- const draft = await recipe.plan({ parameters: input.parameters }, ctx);
753
+ const draft = await recipe.plan({
754
+ parameters: input.parameters,
755
+ excludeSteps: input.excludeSteps
756
+ }, ctx);
590
757
  const items = draft.items;
591
758
  if (items.length > limits.maxPlanItems) throw CleanupErrors.planTooLarge(`${items.length} items > maxPlanItems ${limits.maxPlanItems}`);
592
- const estimatedTotal = items.reduce((sum, i) => sum + i.estimated, 0);
759
+ /**
760
+ * Partition by DISPOSITION — a protective guard's count is not damage.
761
+ *
762
+ * This was one `reduce` over every item, so a guard reporting the 173 posted
763
+ * journal entries it defends pushed the "records to remove" headline to 540
764
+ * on a plan that removes 367. Absent disposition means `'remove'`, so every
765
+ * pre-existing recipe is unchanged.
766
+ */
767
+ const estimatedTotal = items.reduce((sum, i) => sum + ((i.disposition ?? "remove") === "remove" ? i.estimated : 0), 0);
768
+ const protectedTotal = items.reduce((sum, i) => sum + (i.disposition === "protect" ? i.estimated : 0), 0);
593
769
  const blockers = new Set(draft.blockers ?? []);
594
770
  for (const item of items) for (const b of item.blockers ?? []) blockers.add(b);
595
771
  const unsealed = {
@@ -601,6 +777,8 @@ function createCleanupService(deps) {
601
777
  rebuildActions: [...draft.rebuildActions ?? []],
602
778
  warnings: [...draft.warnings ?? []],
603
779
  estimatedTotal,
780
+ protectedTotal,
781
+ excludeSteps: [...input.excludeSteps ?? []].sort(),
604
782
  confirmationPhrase: draft.confirmationPhrase ?? recipe.id
605
783
  };
606
784
  return {
@@ -713,6 +891,14 @@ function createCleanupService(deps) {
713
891
  let processed = 0;
714
892
  let steps = 0;
715
893
  let failed = 0;
894
+ /**
895
+ * Per-step lifecycle, keyed by `stepId` and ordered by first sight.
896
+ *
897
+ * Seeded from the run's existing progress so a RESUMED run (crash recovery,
898
+ * retry) keeps the states it already recorded instead of presenting an
899
+ * operator with a blank pipeline for work that demonstrably happened.
900
+ */
901
+ const stepStates = new Map((run.progress?.stepProgress ?? []).map((s) => [s.stepId, s]));
716
902
  let cancelled = false;
717
903
  let lastSummary = run.progress ?? EMPTY_PROGRESS;
718
904
  let lastPersistMs = 0;
@@ -745,6 +931,7 @@ function createCleanupService(deps) {
745
931
  steps,
746
932
  failed,
747
933
  currentResource: step.resource,
934
+ ...stepStates.size > 0 ? { stepProgress: [...stepStates.values()] } : {},
748
935
  ...step.cursor !== void 0 ? { lastCursor: step.cursor } : {},
749
936
  heartbeatAt: now()
750
937
  };
@@ -756,6 +943,31 @@ function createCleanupService(deps) {
756
943
  await refreshCancel();
757
944
  }
758
945
  },
946
+ /**
947
+ * Upsert one step's lifecycle state, preserving FIRST-SEEN order.
948
+ *
949
+ * A `Map` keyed by `stepId` is what keeps this bounded and idempotent: a
950
+ * step transitioning running → completed updates its entry rather than
951
+ * appending, so the array length is the step count and never the
952
+ * transition count. Insertion order is the execution order, which is what
953
+ * an operator reads down the list.
954
+ *
955
+ * Persisted on the NEXT `onStep` (or the terminal write) rather than
956
+ * immediately — a lifecycle transition is not itself a committed chunk,
957
+ * and writing on every one would defeat the progress throttle that exists
958
+ * to keep a long run from hammering the store.
959
+ */
960
+ async onStepState(state) {
961
+ stepStates.set(state.stepId, state);
962
+ lastSummary = {
963
+ ...lastSummary ?? {
964
+ processed,
965
+ steps,
966
+ failed
967
+ },
968
+ stepProgress: [...stepStates.values()]
969
+ };
970
+ },
759
971
  async throwIfCancelled() {
760
972
  if (cancelled || controller.signal.aborted || await refreshCancel()) throw new CleanupCancelled(run.id);
761
973
  }
@@ -1079,13 +1291,31 @@ const parametersSchema = {
1079
1291
  type: "object",
1080
1292
  additionalProperties: true
1081
1293
  };
1294
+ /**
1295
+ * Step ids to leave out. Bounded and unique — an unbounded list on a
1296
+ * destructive endpoint is a denial-of-service surface, and a duplicate id says
1297
+ * nothing a single one does not. Unknown or PROTECTIVE ids are refused by the
1298
+ * recipe (CLEANUP_INVALID_EXCLUSION), not stripped here: the route must not
1299
+ * decide what an operator may narrow.
1300
+ */
1301
+ const excludeStepsSchema = {
1302
+ type: "array",
1303
+ items: {
1304
+ type: "string",
1305
+ minLength: 1,
1306
+ maxLength: 200
1307
+ },
1308
+ maxItems: 200,
1309
+ uniqueItems: true
1310
+ };
1082
1311
  const previewBodySchema = {
1083
1312
  type: "object",
1084
1313
  required: ["recipe"],
1085
1314
  additionalProperties: false,
1086
1315
  properties: {
1087
1316
  recipe: recipeIdSchema,
1088
- parameters: parametersSchema
1317
+ parameters: parametersSchema,
1318
+ excludeSteps: excludeStepsSchema
1089
1319
  }
1090
1320
  };
1091
1321
  const runsBodySchema = {
@@ -1099,6 +1329,7 @@ const runsBodySchema = {
1099
1329
  properties: {
1100
1330
  recipe: recipeIdSchema,
1101
1331
  parameters: parametersSchema,
1332
+ excludeSteps: excludeStepsSchema,
1102
1333
  planDigest: {
1103
1334
  type: "string",
1104
1335
  minLength: 1,
@@ -1208,6 +1439,7 @@ function createDataCleanupModule(deps) {
1208
1439
  const plan = await (await service()).preview({
1209
1440
  recipeId: body.recipe,
1210
1441
  parameters: body.parameters,
1442
+ excludeSteps: body.excludeSteps,
1211
1443
  actor: resolveActor(r),
1212
1444
  ambient: resolveAmbient?.(r)
1213
1445
  });
@@ -1226,6 +1458,7 @@ function createDataCleanupModule(deps) {
1226
1458
  const run = await (await service()).execute({
1227
1459
  recipeId: body.recipe,
1228
1460
  parameters: body.parameters,
1461
+ excludeSteps: body.excludeSteps,
1229
1462
  planDigest: body.planDigest,
1230
1463
  reason: body.reason,
1231
1464
  confirmation: body.confirmation,
@@ -1,5 +1,5 @@
1
1
 
2
- import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-CpzxqePp.mjs";
2
+ import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-BcR2bFZQ.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.32.0 | MIT | https://classytic.com */
2
- import { t as CRUD_OPERATIONS } from "../../constants-J99LGTJI.mjs";
3
- import { n as stringifyMeasureMap } from "../../ResourceRegistry-BVr7PgA0.mjs";
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";
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.32.0 | MIT | https://classytic.com */
2
- import { t as buildOpenApiSpec } from "../../openapi-DIRzvxC0.mjs";
3
- import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-D1MJp13f.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as buildOpenApiSpec } from "../../openapi-CkV5mQ53.mjs";
3
+ import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-DwaxNcE2.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.32.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.34.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.32.0 | MIT | https://classytic.com */
2
- import { t as generate } from "../../generate-CRSG0jTv.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as generate } from "../../generate-DiB0nHPR.mjs";
3
3
  export { generate };
@@ -1,3 +1,3 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { t as init } from "../../init-BbfP8aky.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as init } from "../../init-mdJ-v4Xp.mjs";
3
3
  export { init };
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-D1MJp13f.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-DwaxNcE2.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.32.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.34.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-CRSG0jTv.mjs";
6
- import { t as init } from "../init-BbfP8aky.mjs";
5
+ import { t as generate } from "../generate-DiB0nHPR.mjs";
6
+ import { t as init } from "../init-mdJ-v4Xp.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.32.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
2
  //#region src/constants.ts
3
3
  /**
4
4
  * Arc Framework Constants — Single Source of Truth
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { n as requestContext, t as getTraceHeaders } from "../requestContext-B6vQF-sy.mjs";
3
- import { t as transactionContext } from "../transactionContext-BvLKvOIL.mjs";
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
4
  export { getTraceHeaders, requestContext, transactionContext };
@@ -1,4 +1,4 @@
1
1
 
2
- import { $t as TreeMixin, An as BaseControllerOptions, B as defineResource, Fn as BodySanitizerConfig, Mn as ControllerConstructionOptions, Nn as ListResult, Pn as BodySanitizer, Qt as TreeExt, V as ResourceDefinition, Xt as SoftDeleteExt, Yt as BaseController, Zt as SoftDeleteMixin, an as QueryResolver, cn as AccessControlConfig, ct as AggMeasureShorthand, dt as AggregationDateRangeRequirement, en as SlugExt, ft as AggregationIndexHint, gt as AggregationsMap, ht as AggregationRateLimit, in as BaseCrudController, jn as ControllerConfigurableOptions, lt as AggregationCacheConfig, mt as AggregationMaterializedResult, nn as BulkExt, on as QueryResolverConfig, pt as AggregationMaterializedContext, rn as BulkMixin, sn as AccessControl, st as AggMeasureInput, tn as SlugMixin, ut as AggregationConfig } from "../index-CpzxqePp.mjs";
3
- import { A as DEFAULT_ID_FIELD, B as MAX_FILTER_DEPTH, C as DefineActionConfig, D as defineAggregation, E as createPermissionMiddleware, F as DEFAULT_UPDATE_METHOD, G as RESERVED_QUERY_PARAMS, H as MAX_SEARCH_LENGTH, I as HOOK_OPERATIONS, K as SYSTEM_FIELDS, L as HOOK_PHASES, M as DEFAULT_MAX_LIMIT, N as DEFAULT_SORT, O as CRUD_OPERATIONS, P as DEFAULT_TENANT_FIELD, R as HookOperation, S as mergeResourceConfig, T as createCrudRouter, U as MUTATION_OPERATIONS, V as MAX_REGEX_LENGTH, W as MutationOperation, _ as getEntityQuery, a as formatValidationErrors, b as ResourceSeams, c as isFieldReadable, d as createRequestContext, f as getControllerContext, g as getEntityIdField, h as getEntityId, i as assertValidConfig, j as DEFAULT_LIMIT, k as CrudOperation, l as createCrudHandlers, m as sendControllerResponse, n as ValidateOptions, o as validateResourceConfig, p as getControllerScope, r as ValidationResult, s as collectReadBlockedFields, t as ConfigError, u as createFastifyHandler, v as defineResourceVariants, w as defineAction, x as SeamedResourceConfig, y as AdapterLike, z as HookPhase } from "../index-DXlHwvuE.mjs";
4
- export { AccessControl, type AccessControlConfig, type AdapterLike, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ConfigError, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, type ResourceSeams, SYSTEM_FIELDS, type SeamedResourceConfig, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, type ValidateOptions, type ValidationResult, assertValidConfig, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, sendControllerResponse, validateResourceConfig };
2
+ import { $t as AccessControlConfig, B as defineResource, Bt as BaseController, Cn as ControllerConstructionOptions, En as BodySanitizerConfig, Gt as SlugExt, Ht as SoftDeleteMixin, Jt as BulkMixin, Kt as SlugMixin, Qt as AccessControl, Sn as ControllerConfigurableOptions, Tn as BodySanitizer, Ut as TreeExt, V as ResourceDefinition, Vt as SoftDeleteExt, Wt as TreeMixin, Xt as QueryResolver, Yt as BaseCrudController, Zt as QueryResolverConfig, ct as AggMeasureShorthand, dt as AggregationDateRangeRequirement, ft as AggregationIndexHint, gt as AggregationsMap, ht as AggregationRateLimit, lt as AggregationCacheConfig, mt as AggregationMaterializedResult, pt as AggregationMaterializedContext, qt as BulkExt, st as AggMeasureInput, ut as AggregationConfig, wn as ListResult, xn as BaseControllerOptions } from "../index-BcR2bFZQ.mjs";
3
+ import { A as CRUD_OPERATIONS, B as HookOperation, C as SeamedResourceConfig, D as createCrudRouter, E as defineAction, F as DEFAULT_SORT, G as MUTATION_OPERATIONS, H as MAX_FILTER_DEPTH, I as DEFAULT_TENANT_FIELD, J as SYSTEM_FIELDS, K as MutationOperation, L as DEFAULT_UPDATE_METHOD, M as DEFAULT_ID_FIELD, N as DEFAULT_LIMIT, O as createPermissionMiddleware, P as DEFAULT_MAX_LIMIT, R as HOOK_OPERATIONS, S as ResourceSeams, T as DefineActionConfig, U as MAX_REGEX_LENGTH, V as HookPhase, W as MAX_SEARCH_LENGTH, _ as getEntityId, a as formatValidationErrors, b as defineResourceVariants, c as nativePolicyFilter, d as createCrudHandlers, f as createFastifyHandler, g as sendControllerResponse, h as getControllerScope, i as assertValidConfig, j as CrudOperation, k as defineAggregation, l as collectReadBlockedFields, m as getControllerContext, n as ValidateOptions, o as validateResourceConfig, p as createRequestContext, q as RESERVED_QUERY_PARAMS, r as ValidationResult, s as NativePolicyFilter, t as ConfigError, u as isFieldReadable, v as getEntityIdField, w as mergeResourceConfig, x as AdapterLike, y as getEntityQuery, z as HOOK_PHASES } from "../index-CFAJEWCX.mjs";
4
+ export { AccessControl, type AccessControlConfig, type AdapterLike, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ConfigError, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, type NativePolicyFilter, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, type ResourceSeams, SYSTEM_FIELDS, type SeamedResourceConfig, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, type ValidateOptions, type ValidationResult, assertValidConfig, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, nativePolicyFilter, sendControllerResponse, validateResourceConfig };
@@ -1,8 +1,9 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-J99LGTJI.mjs";
3
- import { a as BulkMixin, c as collectReadBlockedFields, f as AccessControl, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as BodySanitizer } from "../BaseController-Dk34dxhI.mjs";
4
- import { a as ResourceDefinition, i as validateResourceConfig, n as assertValidConfig, o as createCrudRouter, r as formatValidationErrors, s as createPermissionMiddleware, t as defineResource } from "../defineResource-XFPPUu2c.mjs";
5
- import { a as createFastifyHandler, c as getControllerScope, i as createCrudHandlers, l as sendControllerResponse, o as createRequestContext, s as getControllerContext } from "../pipeline-CPA-jx0M.mjs";
6
- import { t as defineAggregation } from "../defineAggregation-gX4No1gt.mjs";
7
- import { a as mergeResourceConfig, i as defineResourceVariants, n as getEntityIdField, o as defineAction, r as getEntityQuery, t as getEntityId } from "../core-BwwmNSp7.mjs";
8
- export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, assertValidConfig, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, sendControllerResponse, validateResourceConfig };
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-BxCoxmiN.mjs";
3
+ import { t as nativePolicyFilter } from "../repositoryFilter-BANpz6BX.mjs";
4
+ import { a as BulkMixin, c as collectReadBlockedFields, f as BodySanitizer, i as SlugMixin, l as isFieldReadable, m as AccessControl, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController } from "../BaseController-CbkuhNI2.mjs";
5
+ import { a as ResourceDefinition, i as validateResourceConfig, n as assertValidConfig, o as createCrudRouter, r as formatValidationErrors, s as createPermissionMiddleware, t as defineResource } from "../defineResource-ZhuLonLm.mjs";
6
+ import { a as createFastifyHandler, c as getControllerScope, i as createCrudHandlers, l as sendControllerResponse, o as createRequestContext, s as getControllerContext } from "../pipeline-q69DZzgG.mjs";
7
+ import { t as defineAggregation } from "../defineAggregation-Nv-DwfOf.mjs";
8
+ import { a as mergeResourceConfig, i as defineResourceVariants, n as getEntityIdField, o as defineAction, r as getEntityQuery, t as getEntityId } from "../core-BYKa8wHu.mjs";
9
+ export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, assertValidConfig, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, nativePolicyFilter, sendControllerResponse, validateResourceConfig };
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import "./constants-J99LGTJI.mjs";
3
- import { t as defineResource } from "./defineResource-XFPPUu2c.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import "./constants-BxCoxmiN.mjs";
3
+ import { t as defineResource } from "./defineResource-ZhuLonLm.mjs";
4
4
  //#region src/core/defineAction.ts
5
5
  /**
6
6
  * Build an `ActionDefinition` with a typed handler. The literal schema
@@ -1,11 +1,11 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
3
- import "./constants-J99LGTJI.mjs";
4
- import { f as createError } from "./errors-Bh1KK5ps.mjs";
5
- import { a as buildRouteHooks, i as selectPluginMw, l as buildAuthMiddlewareForPermissions, n as buildRouteConfig, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-C7mmAGs3.mjs";
6
- import { n as buildActionPermissionMw, t as tryRegisterRoute } from "./registerRoute-B_QrjYZS.mjs";
7
- import { l as sendControllerResponse, r as resolvePipelineSteps, t as buildActionPipelineHandler } from "./pipeline-CPA-jx0M.mjs";
8
- import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-CnyKK_1j.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
3
+ import "./constants-BxCoxmiN.mjs";
4
+ import { f as createError } from "./errors-Cd_yy7aW.mjs";
5
+ import { a as buildRouteHooks, i as selectPluginMw, l as buildAuthMiddlewareForPermissions, n as buildRouteConfig, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-D4-ASwsI.mjs";
6
+ import { n as buildActionPermissionMw, t as tryRegisterRoute } from "./registerRoute-Ba8v0PCa.mjs";
7
+ import { l as sendControllerResponse, r as resolvePipelineSteps, t as buildActionPipelineHandler } from "./pipeline-q69DZzgG.mjs";
8
+ import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-CDvclAew.mjs";
9
9
  //#region src/core/createActionRouter.ts
10
10
  var createActionRouter_exports = /* @__PURE__ */ __exportAll({
11
11
  buildActionBodySchema: () => buildActionBodySchema,
@@ -1,10 +1,10 @@
1
- /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
- import { n as evaluateAndApplyPermission } from "./authorizationDecision-__T3EJDA.mjs";
3
- import { f as createError } from "./errors-Bh1KK5ps.mjs";
4
- import { u as getRequestScope } from "./types-DXYX7dIR.mjs";
5
- import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-C7mmAGs3.mjs";
6
- import { n as validateAggregations } from "./validate-PMCPR9El.mjs";
7
- import { t as buildAggregationHandler } from "./buildHandler-Dn7rzUJo.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { n as evaluateAndApplyPermission } from "./authorizationDecision-CMMLWmuZ.mjs";
3
+ import { f as createError } from "./errors-Cd_yy7aW.mjs";
4
+ import { u as getRequestScope } from "./types-CoqIRrkY.mjs";
5
+ import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-D4-ASwsI.mjs";
6
+ import { n as validateAggregations } from "./validate-CdQlz52F.mjs";
7
+ import { t as buildAggregationHandler } from "./buildHandler-BrdYrG_1.mjs";
8
8
  //#region src/core/aggregation/createAggregationRouter.ts
9
9
  /**
10
10
  * Register one Fastify route per aggregation. No-op when the map is