@classytic/arc 2.23.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 (147) hide show
  1. package/README.md +2 -0
  2. package/dist/{BaseController-BQ8KNvWC.mjs → BaseController-B65zbz9m.mjs} +94 -23
  3. package/dist/QueryCache-DOMT_iq7.d.mts +72 -0
  4. package/dist/actionPermissions-CAphcmUG.mjs +24 -0
  5. package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
  6. package/dist/audit/index.d.mts +32 -3
  7. package/dist/audit/index.mjs +7 -4
  8. package/dist/auth/index.d.mts +2 -2
  9. package/dist/auth/index.mjs +263 -259
  10. package/dist/{buildHandler-D_2ULWxZ.mjs → buildHandler-Dtwd8fdz.mjs} +1 -1
  11. package/dist/cache/index.d.mts +41 -3
  12. package/dist/cache/index.mjs +33 -4
  13. package/dist/cleanup/index.d.mts +313 -0
  14. package/dist/cleanup/index.mjs +1267 -0
  15. package/dist/cli/commands/describe.d.mts +1 -1
  16. package/dist/cli/commands/docs.mjs +1 -1
  17. package/dist/cli/commands/generate.mjs +1 -1
  18. package/dist/cli/commands/init.mjs +1 -1
  19. package/dist/cli/index.mjs +2 -2
  20. package/dist/context/index.mjs +2 -64
  21. package/dist/core/index.d.mts +3 -3
  22. package/dist/core/index.mjs +5 -4
  23. package/dist/core-BGa8TXJR.mjs +221 -0
  24. package/dist/{createActionRouter-BuOv-m4Y.mjs → createActionRouter-C-N7z2EL.mjs} +3 -2
  25. package/dist/{createAggregationRouter-CgJv_BU_.mjs → createAggregationRouter-wzwFa_dW.mjs} +3 -3
  26. package/dist/{createApp-CjOeWJ9_.mjs → createApp-Du5G54TM.mjs} +187 -96
  27. package/dist/{core-DQ6rDTyH.mjs → defineResource-2zvVVpxg.mjs} +297 -230
  28. package/dist/docs/index.d.mts +1 -1
  29. package/dist/docs/index.mjs +2 -2
  30. package/dist/{elevation-COpYKt2h.mjs → elevation-CIz1GjGm.mjs} +4 -3
  31. package/dist/{eventPlugin-A6EbthXR.d.mts → eventPlugin-Bg3cP2_u.d.mts} +38 -3
  32. package/dist/{eventPlugin-DJG2D7sy.mjs → eventPlugin-DrQ_FesK.mjs} +22 -4
  33. package/dist/events/index.d.mts +83 -314
  34. package/dist/events/index.mjs +183 -95
  35. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  36. package/dist/events/transports/redis-stream-entry.mjs +24 -2
  37. package/dist/factory/index.d.mts +5 -5
  38. package/dist/factory/index.mjs +30 -8
  39. package/dist/{fastify-augmentation-g_f9omSc.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +26 -2
  40. package/dist/{generate-usRSdQmP.mjs → generate-D0wZupBg.mjs} +1 -1
  41. package/dist/headers-BvlQSDMB.mjs +45 -0
  42. package/dist/health-CB_m4S32.mjs +377 -0
  43. package/dist/hooks/index.d.mts +1 -1
  44. package/dist/idempotency/index.mjs +27 -3
  45. package/dist/{index-wMoWBLkn.d.mts → index-BMnbzvtO.d.mts} +3 -3
  46. package/dist/{integrations/jobs.d.mts → index-CG4yqMDB.d.mts} +82 -2
  47. package/dist/index-CZY4EGga.d.mts +107 -0
  48. package/dist/{index-CC5ruDGh.d.mts → index-CjVg-Epg.d.mts} +37 -5
  49. package/dist/{index-BBsvQt7U.d.mts → index-DPRH0Cme.d.mts} +36 -106
  50. package/dist/index.d.mts +6 -6
  51. package/dist/index.mjs +7 -6
  52. package/dist/{init-CjO4p_q1.mjs → init-tlRSt667.mjs} +206 -208
  53. package/dist/integrations/event-gateway.d.mts +1 -1
  54. package/dist/integrations/event-gateway.mjs +1 -1
  55. package/dist/integrations/index.d.mts +3 -3
  56. package/dist/integrations/jobs/index.d.mts +2 -0
  57. package/dist/integrations/{jobs.mjs → jobs/index.mjs} +110 -67
  58. package/dist/integrations/mcp/index.d.mts +4 -4
  59. package/dist/integrations/mcp/index.mjs +2 -2
  60. package/dist/integrations/mcp/testing.d.mts +1 -1
  61. package/dist/integrations/mcp/testing.mjs +1 -1
  62. package/dist/integrations/webhooks.d.mts +37 -30
  63. package/dist/integrations/webhooks.mjs +51 -62
  64. package/dist/integrations/websocket-redis.d.mts +1 -1
  65. package/dist/integrations/websocket.d.mts +1 -1
  66. package/dist/integrations/websocket.mjs +41 -14
  67. package/dist/{interface-C1_WLTMK.d.mts → interface-BGZCzOGL.d.mts} +24 -31
  68. package/dist/{mcpPlugin-BtzIL_kM.mjs → mcpPlugin-Cs6nRGCt.mjs} +12 -10
  69. package/dist/{memory-CLXA2VeJ.mjs → memory-CmnzXE5Z.mjs} +29 -0
  70. package/dist/middleware/index.d.mts +17 -1
  71. package/dist/middleware/index.mjs +1 -1
  72. package/dist/migrations/index.d.mts +94 -44
  73. package/dist/migrations/index.mjs +196 -44
  74. package/dist/module-_-M7C5ES.mjs +29 -0
  75. package/dist/{multipartBody-CHEQSZ6G.mjs → multipartBody-D0eBvkeq.mjs} +17 -0
  76. package/dist/{openapi-jSDx-VDv.mjs → openapi-BLV4OMXb.mjs} +3 -3
  77. package/dist/permissions/index.d.mts +2 -2
  78. package/dist/permissions/index.mjs +3 -3
  79. package/dist/{permissions-tbxFHxjZ.mjs → permissions-BrtiBKGs.mjs} +229 -229
  80. package/dist/pipeline/index.d.mts +1 -1
  81. package/dist/{actionPermissions-lszoJle1.mjs → pipeline-BGRnV-To.mjs} +4 -27
  82. package/dist/plugins/index.d.mts +6 -77
  83. package/dist/plugins/index.mjs +11 -95
  84. package/dist/plugins/response-cache.mjs +1 -0
  85. package/dist/plugins/tracing-entry.mjs +1 -1
  86. package/dist/{index-Ba10fdKp.d.mts → preloadResources-CM4uUaFv.d.mts} +51 -50
  87. package/dist/preloadResources-DSY5HGpS.mjs +45 -0
  88. package/dist/presets/filesUpload.d.mts +3 -3
  89. package/dist/presets/filesUpload.mjs +3 -3
  90. package/dist/presets/index.d.mts +2 -1
  91. package/dist/presets/index.mjs +1 -1
  92. package/dist/presets/multiTenant.d.mts +1 -1
  93. package/dist/presets/multiTenant.mjs +1 -1
  94. package/dist/presets/search.d.mts +2 -2
  95. package/dist/presets/search.mjs +1 -1
  96. package/dist/{presets-BOe4Ugkl.mjs → presets-DwFDVKP0.mjs} +2 -2
  97. package/dist/{queryCachePlugin-DzqLN904.mjs → queryCachePlugin-DSGuSVLW.mjs} +39 -8
  98. package/dist/{queryCachePlugin-CfYWq-Ak.d.mts → queryCachePlugin-br4otiqF.d.mts} +2 -2
  99. package/dist/{registerRoute-CWO6mdYe.mjs → rateLimit-DrED-C4v.mjs} +2 -109
  100. package/dist/{realtime-I8OrfnMR.mjs → realtime-DEuDu9io.mjs} +3 -3
  101. package/dist/{redis-stream-iVR2l7aS.d.mts → redis-stream-uIaauNpZ.d.mts} +22 -0
  102. package/dist/registerRoute-DTp18Ah_.mjs +108 -0
  103. package/dist/registry/index.d.mts +2 -2
  104. package/dist/registry/index.mjs +1 -1
  105. package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
  106. package/dist/renewingLease-3cuQH7h8.mjs +44 -0
  107. package/dist/{requestContext-BZKOr13H.mjs → requestContext-MrlJwECf.mjs} +2 -2
  108. package/dist/schedules-CjOd2MG9.mjs +113 -0
  109. package/dist/schemas/index.d.mts +67 -67
  110. package/dist/schemas/index.mjs +2 -3
  111. package/dist/scim/index.mjs +1 -2
  112. package/dist/scope/index.d.mts +2 -2
  113. package/dist/scope/index.mjs +4 -4
  114. package/dist/{scope-LONQ3mfZ.mjs → scope-DfaPntpo.mjs} +4 -5
  115. package/dist/{sse-0u4P4-OI.mjs → sse-BQ1w0dfl.mjs} +1 -1
  116. package/dist/testing/cleanupStoreContract.d.mts +10 -0
  117. package/dist/testing/cleanupStoreContract.mjs +375 -0
  118. package/dist/testing/index.d.mts +5 -3
  119. package/dist/testing/index.mjs +4 -2
  120. package/dist/testing/outboxStoreContract.d.mts +13 -0
  121. package/dist/testing/outboxStoreContract.mjs +247 -0
  122. package/dist/transactionContext-PNdoyLLm.mjs +64 -0
  123. package/dist/types/index.d.mts +4 -4
  124. package/dist/{types-bHblZ9YN.mjs → types-CSyCxl2V.mjs} +38 -7
  125. package/dist/types-C_ZZRcD6.d.mts +450 -0
  126. package/dist/{module-B4u10iOJ.d.mts → types-Cu6vVJmh.d.mts} +196 -60
  127. package/dist/{types-DH0GupI7.d.mts → types-DjC-KUCz.d.mts} +118 -20
  128. package/dist/{types-Csxga7d3.d.mts → types-Dmw-6Qeo.d.mts} +96 -4
  129. package/dist/{types-5OuqeTDX.d.mts → types-QFGc3Cjv.d.mts} +1 -1
  130. package/dist/usage/index.d.mts +3 -52
  131. package/dist/usage/index.mjs +0 -0
  132. package/dist/userHelpers-Cg_y3fBz.mjs +29 -0
  133. package/dist/utils/index.d.mts +58 -85
  134. package/dist/utils/index.mjs +4 -2
  135. package/dist/{utils-CZyeRXTh.mjs → utils-taUVD4p3.mjs} +47 -337
  136. package/dist/{validate-C2mGgSXg.mjs → validate-Bk44F2yT.mjs} +1 -1
  137. package/dist/{versioning-D6PvCwpq.d.mts → versioning-DXgY5BUm.d.mts} +11 -29
  138. package/dist/{versioning-Dk-71iIz.mjs → versioning-gKWyryT3.mjs} +8 -1
  139. package/dist/{websocket-uuWqM6UY.d.mts → websocket-_Pd0CgR0.d.mts} +15 -1
  140. package/package.json +541 -513
  141. package/skills/arc/references/api-reference.md +3 -1
  142. package/skills/arc/references/integrations.md +20 -11
  143. package/dist/QueryCache-Z1B38koh.d.mts +0 -40
  144. package/dist/betterAuthOpenApi-CV1lxpRX.mjs +0 -2
  145. package/dist/health-ScyjIVo5.mjs +0 -202
  146. package/dist/requestId-5BhTVzBv.mjs +0 -113
  147. package/dist/types-XTApa8b6.mjs +0 -24
@@ -1,6 +1,6 @@
1
- import { t as MemoryCacheStore } from "../memory-CLXA2VeJ.mjs";
2
1
  import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-Cd316u_y.mjs";
3
- import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-DzqLN904.mjs";
2
+ import { t as MemoryCacheStore } from "../memory-CmnzXE5Z.mjs";
3
+ import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-DSGuSVLW.mjs";
4
4
  //#region src/cache/redis.ts
5
5
  /**
6
6
  * Redis-backed cache store.
@@ -47,6 +47,30 @@ var RedisCacheStore = class {
47
47
  await this.client.del(this.withPrefix(key));
48
48
  }
49
49
  /**
50
+ * Atomic increment (canonical `CacheAdapter` signature: key, by, ttl).
51
+ *
52
+ * The atomic path requires `incrBy` AND — when a TTL is requested —
53
+ * `expireIfAbsent`: TTL-on-create cannot be inferred from INCRBY's
54
+ * return value (an existing counter at 0 also returns `by`), so the
55
+ * NX-expiry must be its own atomic server-side operation. A client that
56
+ * can't honor the COMPLETE contract falls back to read-modify-write
57
+ * (monotonic, but a concurrent replica's bump can be lost, and the TTL
58
+ * refreshes per write since plain SET can't preserve expiry).
59
+ */
60
+ async increment(key, by = 1, ttlSeconds) {
61
+ const prefixed = this.withPrefix(key);
62
+ const wantsTtl = ttlSeconds !== void 0 && ttlSeconds > 0;
63
+ if (this.client.incrBy && (!wantsTtl || this.client.expireIfAbsent)) {
64
+ const next = await this.client.incrBy(prefixed, by);
65
+ if (wantsTtl) await this.client.expireIfAbsent?.(prefixed, Math.ceil(ttlSeconds));
66
+ return next;
67
+ }
68
+ const current = await this.get(key);
69
+ const next = (typeof current === "number" ? current : 0) + by;
70
+ await this.set(key, next, ttlSeconds ?? this.defaultTtlSeconds);
71
+ return next;
72
+ }
73
+ /**
50
74
  * Invalidate keys. Pass a glob pattern to delete a subset (`user:*:v2`);
51
75
  * omit to clear every key under this store's prefix.
52
76
  */
@@ -125,9 +149,12 @@ function ioredisAsCacheClient(client) {
125
149
  const [next, keys] = await client.scan(cursor, ...args);
126
150
  return [next, keys];
127
151
  },
128
- pipeline: client.pipeline ? () => client.pipeline() : void 0
152
+ pipeline: client.pipeline ? () => client.pipeline() : void 0,
153
+ incrBy: client.incrby ? (key, by) => client.incrby(key, by) : void 0,
154
+ expireIfAbsent: client.eval ? (key, seconds) => client.eval(EXPIRE_IF_ABSENT_LUA, 1, key, seconds) : void 0
129
155
  };
130
156
  }
157
+ const EXPIRE_IF_ABSENT_LUA = "if redis.call('TTL', KEYS[1]) < 0 then return redis.call('EXPIRE', KEYS[1], ARGV[1]) else return 0 end";
131
158
  /**
132
159
  * Wrap an `@upstash/redis` REST client as a `RedisCacheClient`.
133
160
  *
@@ -176,7 +203,9 @@ function upstashAsCacheClient(client) {
176
203
  }
177
204
  const [next, keys] = await client.scan(cursor, opts);
178
205
  return [next, keys];
179
- }
206
+ },
207
+ incrBy: client.incrby ? (key, by) => client.incrby(key, by) : void 0,
208
+ expireIfAbsent: client.eval ? (key, seconds) => client.eval(EXPIRE_IF_ABSENT_LUA, [key], [seconds]) : void 0
180
209
  };
181
210
  }
182
211
  //#endregion
@@ -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 };