@classytic/arc 2.25.0 → 2.28.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 (48) hide show
  1. package/README.md +2 -0
  2. package/dist/{BaseController--M8EMzt0.mjs → BaseController-B65zbz9m.mjs} +4 -2
  3. package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
  4. package/dist/cleanup/index.d.mts +313 -0
  5. package/dist/cleanup/index.mjs +1267 -0
  6. package/dist/cli/commands/docs.mjs +1 -1
  7. package/dist/core/index.d.mts +1 -1
  8. package/dist/core/index.mjs +4 -3
  9. package/dist/core-BGa8TXJR.mjs +221 -0
  10. package/dist/{createActionRouter-B6GvAuAX.mjs → createActionRouter-C-N7z2EL.mjs} +2 -2
  11. package/dist/{preloadResources-DfyngIN9.mjs → createApp-Du5G54TM.mjs} +2 -74
  12. package/dist/{core-EUNu0u07.mjs → defineResource-2zvVVpxg.mjs} +6 -224
  13. package/dist/docs/index.mjs +1 -1
  14. package/dist/events/index.d.mts +28 -2
  15. package/dist/events/index.mjs +20 -4
  16. package/dist/factory/index.d.mts +4 -3
  17. package/dist/factory/index.mjs +3 -1
  18. package/dist/{fastify-augmentation-dPNBVeIj.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +2 -1
  19. package/dist/index-CZY4EGga.d.mts +107 -0
  20. package/dist/{index-f4PTrk3B.d.mts → index-CjVg-Epg.d.mts} +1 -1
  21. package/dist/index.d.mts +2 -2
  22. package/dist/index.mjs +6 -5
  23. package/dist/integrations/mcp/index.mjs +1 -1
  24. package/dist/integrations/mcp/testing.mjs +1 -1
  25. package/dist/{mcpPlugin-Cb7olw0j.mjs → mcpPlugin-Cs6nRGCt.mjs} +3 -3
  26. package/dist/module-_-M7C5ES.mjs +29 -0
  27. package/dist/{openapi-DrLtog_B.mjs → openapi-BLV4OMXb.mjs} +1 -1
  28. package/dist/permissions/index.mjs +1 -1
  29. package/dist/{pipeline-BN5G05rd.mjs → pipeline-BGRnV-To.mjs} +1 -1
  30. package/dist/plugins/index.d.mts +2 -2
  31. package/dist/plugins/index.mjs +1 -1
  32. package/dist/plugins/tracing-entry.mjs +1 -1
  33. package/dist/{preloadResources-0oe-JSSS.d.mts → preloadResources-CM4uUaFv.d.mts} +2 -1
  34. package/dist/preloadResources-DSY5HGpS.mjs +45 -0
  35. package/dist/{realtime-Cuw_deA-.mjs → realtime-DEuDu9io.mjs} +1 -1
  36. package/dist/{registerRoute-v0Wb6Plt.mjs → registerRoute-DTp18Ah_.mjs} +1 -1
  37. package/dist/registry/index.d.mts +1 -1
  38. package/dist/registry/index.mjs +1 -1
  39. package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
  40. package/dist/testing/cleanupStoreContract.d.mts +10 -0
  41. package/dist/testing/cleanupStoreContract.mjs +375 -0
  42. package/dist/testing/index.d.mts +3 -2
  43. package/dist/testing/index.mjs +4 -2
  44. package/dist/testing/outboxStoreContract.d.mts +13 -0
  45. package/dist/testing/outboxStoreContract.mjs +247 -0
  46. package/dist/types-C_ZZRcD6.d.mts +450 -0
  47. package/dist/{index-KUoxjIq1.d.mts → types-Cu6vVJmh.d.mts} +1 -105
  48. package/package.json +27 -12
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @classytic/arc
2
2
 
3
+ [![Sponsor](https://img.shields.io/github/sponsors/classytic?style=flat-square&label=Sponsor&logo=GitHub&color=EA4AAA)](https://github.com/sponsors/classytic)
4
+
3
5
  Database-agnostic resource framework for Fastify. One `defineResource()` call -> REST + auth + permissions + events + caching + OpenAPI + MCP tools.
4
6
 
5
7
  Fastify 5+ | Node.js 22+ | ESM only
@@ -1,10 +1,10 @@
1
1
  import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-DNre0wRg.mjs";
2
2
  import { arcLog } from "./logger/index.mjs";
3
- import { d as createDomainError, f as createError, i as NotFoundError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
3
+ import { d as createDomainError, f as createError, i as NotFoundError, p as isArcError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
4
4
  import { n as getUserRoles, t as getUserId } from "./userHelpers-Cg_y3fBz.mjs";
5
5
  import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-CSyCxl2V.mjs";
6
6
  import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-Cg05HPsn.mjs";
7
- import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
7
+ import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-BnyVE9eD.mjs";
8
8
  import { g as ArcQueryParser, n as scheduleBackground } from "./utils-taUVD4p3.mjs";
9
9
  import { t as buildQueryKey } from "./keys-Cd316u_y.mjs";
10
10
  //#region src/core/AccessControl.ts
@@ -453,6 +453,7 @@ async function executeHookedOp(ctx, args, executor) {
453
453
  const beforeReturn = await hooks.executeBefore(resourceName, args.op, args.input, hookOpts);
454
454
  if (pipeProcessed) processedData = beforeReturn;
455
455
  } catch (err) {
456
+ if (isArcError(err)) throw err;
456
457
  throw createError(400, "Hook execution failed", {
457
458
  code: `BEFORE_${args.op.toUpperCase()}_HOOK_ERROR`,
458
459
  message: err.message
@@ -1683,6 +1684,7 @@ function SoftDeleteMixin(Base) {
1683
1684
  meta: { id }
1684
1685
  });
1685
1686
  } catch (err) {
1687
+ if (isArcError(err)) throw err;
1686
1688
  throw createError(400, "Hook execution failed", {
1687
1689
  code: "BEFORE_RESTORE_HOOK_ERROR",
1688
1690
  message: err.message
@@ -1,3 +1,4 @@
1
+ import { t as ArcError } from "./errors-DkO8Nk6-.mjs";
1
2
  //#region src/permissions/fields.ts
2
3
  /**
3
4
  * Field-Level Permissions
@@ -238,6 +239,15 @@ async function evaluateAndApplyPermission(check, context, request, reply, opts)
238
239
  resource: context.resource,
239
240
  action: context.action
240
241
  }, "Permission check threw");
242
+ if (err instanceof ArcError) {
243
+ reply.code(err.status).send({
244
+ code: err.code,
245
+ message: err.message,
246
+ status: err.status,
247
+ ...err.meta ? { meta: err.meta } : {}
248
+ });
249
+ return false;
250
+ }
241
251
  reply.code(403).send({
242
252
  code: "arc.forbidden",
243
253
  message: "Permission denied",
@@ -0,0 +1,313 @@
1
+ import { L as RequestWithExtras } from "../index-DPRH0Cme.mjs";
2
+ import { A as CleanupWriteFence, C as CleanupResult, D as CleanupRunStore, E as CleanupRunStatus, F as PurgeStrategyKind, I as PurgeVerificationSummary, L as VerificationCheck, M as PurgeActor, N as PurgeEvidence, O as CleanupRunTransitionPatch, P as PurgeResourceResult, R as VerificationResult, 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 DEFAULT_CLEANUP_LIMITS, k as CleanupStepResult, 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 } from "../types-C_ZZRcD6.mjs";
3
+ import { t as ArcModule } from "../types-Cu6vVJmh.mjs";
4
+ import { PurgeStrategyKind as PurgeStrategyKind$1 } from "@classytic/primitives/retention";
5
+ import { canonicalJson } from "@classytic/primitives/canonical";
6
+ import { CleanupStep } from "@classytic/repo-core/cleanup";
7
+ //#region src/cleanup/compose.d.ts
8
+ /** Metadata + steps for {@link recipeFromSteps}. */
9
+ interface RecipeFromStepsInput {
10
+ /** Stable machine id, unique in the registry (e.g. `'cleanup.rebuild-projections'`). */
11
+ readonly id: string;
12
+ /** Plain-language label for the UI. */
13
+ readonly label: string;
14
+ /**
15
+ * Whether this recipe removes data. Defaults to `true` iff ANY step is
16
+ * destructive (a pure-rebuild recipe is non-destructive).
17
+ */
18
+ readonly destructive?: boolean;
19
+ /** Recipe/plan-schema version (persisted on the run). Defaults to `'1'`. */
20
+ readonly version?: string;
21
+ /**
22
+ * Strategy the run's evidence records. Defaults to `'hard'` when any step is
23
+ * destructive, `'soft'` otherwise (pure rebuilds remove no source data).
24
+ */
25
+ readonly evidenceStrategy?: PurgeStrategyKind$1;
26
+ /** Exact confirmation phrase; defaults (in the service) to the recipe id. */
27
+ readonly confirmationPhrase?: string;
28
+ /** Extra top-level retains beyond per-step `retained` lines. */
29
+ readonly retains?: readonly string[];
30
+ /** Extra top-level warnings beyond per-step warnings. */
31
+ readonly warnings?: readonly string[];
32
+ /**
33
+ * Recipe-level availability gate — go-live boundary, feature flag, etc.
34
+ * STEPS never decide availability (that is host policy). Defaults to always
35
+ * available.
36
+ */
37
+ available?(ctx: CleanupContext): Promise<Availability>;
38
+ /** Provider steps in EXECUTION ORDER. */
39
+ readonly steps: readonly CleanupStep[];
40
+ }
41
+ declare function recipeFromSteps(input: RecipeFromStepsInput): CleanupRecipe;
42
+ //#endregion
43
+ //#region src/cleanup/errors.d.ts
44
+ /**
45
+ * Data Cleanup Center — stable, typed errors.
46
+ *
47
+ * Every failure the framework raises carries a stable machine `code` (so
48
+ * SDK/UI can branch) and an HTTP `status` (so the Arc resource maps cleanly).
49
+ * The codes are part of the public contract — do not rename.
50
+ */
51
+ type CleanupErrorCode =
52
+ /** No recipe registered under the requested id. */
53
+ "CLEANUP_UNKNOWN_RECIPE" |
54
+ /** Two recipes share an id at registry construction. */
55
+ "CLEANUP_DUPLICATE_RECIPE" |
56
+ /** The recipe's `available()` returned false. */
57
+ "CLEANUP_RECIPE_UNAVAILABLE" |
58
+ /** Execute called without the exact confirmation phrase. */
59
+ "CLEANUP_CONFIRMATION_REQUIRED" |
60
+ /** Execute called with an empty reason. */
61
+ "CLEANUP_REASON_REQUIRED" |
62
+ /** The re-planned digest differs from the client's — a new preview is required. */
63
+ "CLEANUP_PLAN_CHANGED" |
64
+ /** The plan carries unresolved blockers — a hard stop (§4.4). */
65
+ "CLEANUP_BLOCKED" |
66
+ /** The plan exceeds a framework size limit (items / results / reason / depth). */
67
+ "CLEANUP_PLAN_TOO_LARGE" |
68
+ /** A destructive run is already in progress (single-run fence, §8). */
69
+ "CLEANUP_ALREADY_RUNNING" |
70
+ /** The run id does not exist. */
71
+ "CLEANUP_RUN_NOT_FOUND" |
72
+ /** The action isn't valid for the run's current status (lost a CAS, etc.). */
73
+ "CLEANUP_INVALID_ACTION" |
74
+ /** No authenticated actor could be resolved for a destructive action (fail-closed). */
75
+ "CLEANUP_ACTOR_REQUIRED" |
76
+ /** Caller lacks permission for the operation. */
77
+ "CLEANUP_FORBIDDEN";
78
+ /**
79
+ * A framework error carrying BOTH `status` (read directly in code/tests) and
80
+ * `statusCode` (the Fastify convention arc's global error handler recognizes,
81
+ * step 4 of its classifier — a numeric `statusCode` + a separatored domain
82
+ * `code` maps to that exact status + code on the wire, never `arc.internal_error`).
83
+ */
84
+ declare class CleanupError extends Error {
85
+ readonly code: CleanupErrorCode;
86
+ readonly status: number;
87
+ /** Alias of {@link status} — the property arc's error handler classifies on. */
88
+ readonly statusCode: number;
89
+ readonly meta?: Record<string, unknown>;
90
+ constructor(code: CleanupErrorCode, status: number, message: string, meta?: Record<string, unknown>);
91
+ }
92
+ /**
93
+ * Internal control-flow signal thrown by `throwIfCancelled()` when a run's
94
+ * durable `cancelRequested` flag is set. NOT a wire error — `processRun`
95
+ * catches it and CAS-transitions the run to `cancelled`.
96
+ */
97
+ declare class CleanupCancelled extends Error {
98
+ readonly runId: string;
99
+ constructor(runId: string);
100
+ }
101
+ declare const CleanupErrors: {
102
+ readonly unknownRecipe: (id: string) => CleanupError;
103
+ readonly duplicateRecipe: (id: string) => CleanupError;
104
+ readonly unavailable: (id: string, reason: string) => CleanupError;
105
+ readonly confirmationRequired: (phrase: string) => CleanupError;
106
+ readonly reasonRequired: () => CleanupError;
107
+ readonly blocked: (blockers: readonly string[]) => CleanupError;
108
+ readonly planTooLarge: (detail: string) => CleanupError;
109
+ readonly actorRequired: () => CleanupError;
110
+ readonly planChanged: (expected: string, actual: string) => CleanupError;
111
+ readonly alreadyRunning: (runId: string) => CleanupError;
112
+ readonly runNotFound: (id: string) => CleanupError;
113
+ readonly invalidAction: (action: string, status: string) => CleanupError;
114
+ readonly forbidden: (operation: string) => CleanupError;
115
+ };
116
+ //#endregion
117
+ //#region src/cleanup/memory.d.ts
118
+ /**
119
+ * In-memory {@link CleanupRunStore}. Single-process only — see the module
120
+ * docblock for the production caveat.
121
+ */
122
+ declare class MemoryCleanupRunStore implements CleanupRunStore {
123
+ #private;
124
+ /** Live run state, keyed by run id. Exposed for tests/inspection. */
125
+ readonly runs: Map<string, CleanupRun>;
126
+ constructor(now?: () => Date);
127
+ createIfPermitted(run: CleanupRun): Promise<CleanupRunCreateResult>;
128
+ get(id: string): Promise<CleanupRun | null>;
129
+ claim(id: string, lease: CleanupLease): Promise<CleanupRun | null>;
130
+ compareAndTransition(id: string, expected: readonly CleanupRunStatus[], to: CleanupRunStatus, patch?: CleanupRunTransitionPatch, leaseToken?: string): Promise<CleanupRun | null>;
131
+ reArmIfPermitted(id: string, patch: CleanupRunTransitionPatch): Promise<CleanupRun | null>;
132
+ requestCancel(id: string, request?: CleanupCancelRequest): Promise<CleanupRun | null>;
133
+ saveProgress(id: string, progress: CleanupProgressSummary, lease?: CleanupLease): Promise<void>;
134
+ }
135
+ /**
136
+ * In-memory {@link CleanupEvidenceStore} — idempotent by
137
+ * `evidence.operationId`, exactly as the port requires.
138
+ */
139
+ declare class MemoryCleanupEvidenceStore implements CleanupEvidenceStore {
140
+ #private;
141
+ readonly evidence: PurgeEvidence[];
142
+ readonly manifests: CleanupManifest[];
143
+ finalize(input: {
144
+ evidence: PurgeEvidence;
145
+ manifest: CleanupManifest;
146
+ }): Promise<void>;
147
+ }
148
+ /**
149
+ * A MANUAL in-memory {@link CleanupJobQueue}: it records enqueued jobs and runs
150
+ * nothing on its own, so a test drives the worker deterministically
151
+ * (`for (const job of queue.jobs) await service.processRun(job.runId)`).
152
+ *
153
+ * Note this is NOT the service's default queue — omitting `jobQueue` entirely
154
+ * gives you a microtask-deferred in-process queue that DOES auto-run
155
+ * `processRun` off the request path.
156
+ */
157
+ declare class MemoryCleanupJobQueue implements CleanupJobQueue {
158
+ readonly jobs: CleanupJob[];
159
+ enqueue(job: CleanupJob): Promise<void>;
160
+ /** Take everything enqueued so far, clearing the buffer. */
161
+ drain(): CleanupJob[];
162
+ }
163
+ //#endregion
164
+ //#region src/cleanup/registry.d.ts
165
+ /** UI/SDK-facing description of one recipe (no methods). */
166
+ interface CleanupRecipeInfo {
167
+ readonly id: string;
168
+ readonly label: string;
169
+ readonly destructive: boolean;
170
+ }
171
+ interface CleanupRegistry {
172
+ /** Get a recipe by id, or throw `CLEANUP_UNKNOWN_RECIPE`. */
173
+ get(id: string): CleanupRecipe;
174
+ /** Get a recipe by id, or `undefined`. */
175
+ find(id: string): CleanupRecipe | undefined;
176
+ /** Every registered recipe. */
177
+ all(): readonly CleanupRecipe[];
178
+ /** Introspection payload for UI/SDK recipe cards. */
179
+ introspect(): readonly CleanupRecipeInfo[];
180
+ }
181
+ /**
182
+ * Build a registry from a recipe list. Throws `CLEANUP_DUPLICATE_RECIPE` at
183
+ * construction if two recipes share an id — a boot-time failure, never a
184
+ * silent last-wins overwrite.
185
+ */
186
+ declare function createCleanupRegistry(recipes: readonly CleanupRecipe[]): CleanupRegistry;
187
+ //#endregion
188
+ //#region src/cleanup/service.d.ts
189
+ interface CleanupServiceDeps {
190
+ registry: CleanupRegistry;
191
+ runStore: CleanupRunStore;
192
+ evidenceStore: CleanupEvidenceStore;
193
+ /** Optional write fence (§8). No-op when absent. */
194
+ writeFence?: CleanupWriteFence | undefined;
195
+ /**
196
+ * Durable job queue (§8). Defaults to a microtask-deferred in-process queue
197
+ * that calls `processRun` off the request path (single-process only). Inject
198
+ * BullMQ / SQS / repo-backed for cross-process durability + restart recovery.
199
+ */
200
+ jobQueue?: CleanupJobQueue | undefined;
201
+ /** Framework size caps. Defaults to {@link DEFAULT_CLEANUP_LIMITS}. */
202
+ limits?: Partial<CleanupLimits> | undefined;
203
+ /**
204
+ * Exclusive worker-lease duration. A crashed executor's run becomes
205
+ * claimable again this long after its last heartbeat (each persisted
206
+ * progress write renews the lease). Default 5 minutes.
207
+ */
208
+ leaseMs?: number | undefined;
209
+ /**
210
+ * Minimum interval between PERSISTED progress writes (and their piggybacked
211
+ * cancellation reads). `0` (default) persists every chunk. Failed steps are
212
+ * always persisted immediately. Keep well below `leaseMs` — persisted
213
+ * progress is also the lease heartbeat.
214
+ */
215
+ progressThrottleMs?: number | undefined;
216
+ /** Optional logger for release-failure / recovery diagnostics. */
217
+ logger?: CleanupContext["logger"];
218
+ /** Id generator — defaults to `crypto.randomUUID`. */
219
+ generateId?: () => string;
220
+ /** Clock — defaults to `() => new Date()`. */
221
+ now?: () => Date;
222
+ }
223
+ interface PreviewInput extends CleanupInput {
224
+ recipeId: string;
225
+ actor: PurgeActor;
226
+ ambient?: Readonly<Record<string, unknown>> | undefined;
227
+ }
228
+ interface ExecuteInput extends PreviewInput {
229
+ /** The digest the operator confirmed against — must match the re-plan. */
230
+ planDigest: string;
231
+ /** Non-empty justification, persisted on the run + evidence. */
232
+ reason: string;
233
+ /** For a destructive recipe: the exact confirmation phrase. */
234
+ confirmation?: string | undefined;
235
+ }
236
+ interface CancelInput {
237
+ /** Who requested the cancel (audit — persisted on the run + evidence). */
238
+ actor?: PurgeActor | undefined;
239
+ /** Why (audit). */
240
+ reason?: string | undefined;
241
+ }
242
+ interface CleanupService {
243
+ preview(input: PreviewInput): Promise<CleanupPlan>;
244
+ execute(input: ExecuteInput): Promise<CleanupRun>;
245
+ /** Worker entrypoint — run the persisted, enqueued operation to completion. */
246
+ processRun(runId: string): Promise<void>;
247
+ getRun(id: string): Promise<CleanupRun>;
248
+ cancel(id: string, input?: CancelInput): Promise<CleanupRun>;
249
+ retry(id: string): Promise<CleanupRun>;
250
+ }
251
+ /** Admission key serializing every destructive run behind one slot. */
252
+ declare const GLOBAL_DESTRUCTIVE_KEY = "global-destructive";
253
+ declare function createCleanupService(deps: CleanupServiceDeps): CleanupService;
254
+ //#endregion
255
+ //#region src/cleanup/module.d.ts
256
+ interface DataCleanupModuleDeps {
257
+ /** The recipes this deployment exposes. Ids must be unique (boot-checked). */
258
+ recipes: readonly CleanupRecipe[];
259
+ /** Durable run store — host-owned persistence (atomic create + CAS transitions). */
260
+ runStore: CleanupRunStore;
261
+ /** Durable evidence + manifest store — host-owned, idempotent by operationId. */
262
+ evidenceStore: CleanupEvidenceStore;
263
+ /** Permission checks for view / execute / manage. */
264
+ permissions: CleanupPermissions;
265
+ /** Optional write fence (§8). */
266
+ writeFence?: CleanupWriteFence | undefined;
267
+ /** Optional durable job queue (§8) — defaults to microtask-deferred in-process. */
268
+ jobQueue?: CleanupJobQueue | undefined;
269
+ /** Framework size caps. */
270
+ limits?: Partial<CleanupLimits> | undefined;
271
+ /** Exclusive worker-lease duration (ms). Default 5 minutes. */
272
+ leaseMs?: number | undefined;
273
+ /** Min interval between persisted progress writes (ms). Default 0 (every chunk). */
274
+ progressThrottleMs?: number | undefined;
275
+ /** Route prefix. Default `/governance/data-cleanup`. */
276
+ prefix?: string | undefined;
277
+ /** Module name. Default `data-cleanup`. */
278
+ moduleName?: string | undefined;
279
+ /** App resource names this module supersedes. */
280
+ owns?: readonly string[] | undefined;
281
+ /**
282
+ * Derive the acting party from the request. Default reads `getUserId(req.scope)`
283
+ * (falling back to `req.user.id`) and FAILS CLOSED — a destructive governance
284
+ * action with no resolvable authenticated actor throws `CLEANUP_ACTOR_REQUIRED`
285
+ * (401) rather than attributing the purge to `user:unknown`.
286
+ */
287
+ resolveActor?: (req: RequestWithExtras) => PurgeActor;
288
+ /**
289
+ * Capture the request's serializable ambient scope (branch/company target) so
290
+ * the worker rebuilds the exact operation context. Must be JSON-serializable.
291
+ */
292
+ resolveAmbient?: (req: RequestWithExtras) => Readonly<Record<string, unknown>> | undefined;
293
+ /** Injected id generator (tests). */
294
+ generateId?: () => string;
295
+ /** Injected clock (tests). */
296
+ now?: () => Date;
297
+ }
298
+ /**
299
+ * Build the Data Cleanup Center Arc module. The module's public export is the
300
+ * live {@link CleanupService} (so other modules / tests can drive it directly).
301
+ */
302
+ declare function createDataCleanupModule(deps: DataCleanupModuleDeps): ArcModule<CleanupService>;
303
+ //#endregion
304
+ //#region src/cleanup/plan-digest.d.ts
305
+ /** `sha256(canonical(planMaterial))` as a hex digest. */
306
+ declare function computePlanDigest(plan: Omit<CleanupPlan, "digest">): string;
307
+ /**
308
+ * Integrity checksum of a completed manifest (§5). Excludes the digest field
309
+ * itself. Dates (e.g. `completedAt`) participate via the strict canonicalizer.
310
+ */
311
+ declare function computeManifestDigest(manifest: Record<string, unknown>): string;
312
+ //#endregion
313
+ export { type Availability, CLEANUP_TERMINAL_STATUSES, type CancelInput, type CleanupCancelRequest, CleanupCancelled, type CleanupContext, CleanupError, type CleanupErrorCode, CleanupErrors, type CleanupEvidenceStore, type CleanupExecutionContext, type CleanupFinalizationPayload, type CleanupInput, type CleanupJob, type CleanupJobQueue, type CleanupLease, type CleanupLimits, type CleanupLogger, type CleanupManifest, type CleanupOutcomeStatus, type CleanupPermissionCheck, type CleanupPermissions, type CleanupPlan, type CleanupPlanDraft, type CleanupPlanItem, type CleanupProgressSummary, type CleanupRecipe, type CleanupRecipeInfo, type CleanupRegistry, type CleanupResult, type CleanupRun, type CleanupRunCreateResult, type CleanupRunStatus, type CleanupRunStore, type CleanupRunTransitionPatch, type CleanupService, type CleanupServiceDeps, type CleanupStepResult, type CleanupWriteFence, DEFAULT_CLEANUP_LIMITS, type DataCleanupModuleDeps, type ExecuteInput, GLOBAL_DESTRUCTIVE_KEY, MemoryCleanupEvidenceStore, MemoryCleanupJobQueue, MemoryCleanupRunStore, type PreviewInput, type PurgeActor, type PurgeEvidence, type PurgeResourceResult, type PurgeStrategyKind, type PurgeVerificationSummary, type RecipeFromStepsInput, type VerificationCheck, type VerificationResult, canonicalJson, computeManifestDigest, computePlanDigest, createCleanupRegistry, createCleanupService, createDataCleanupModule, recipeFromSteps };