@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.
- package/README.md +2 -0
- package/dist/{BaseController--M8EMzt0.mjs → BaseController-B65zbz9m.mjs} +4 -2
- package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
- package/dist/cleanup/index.d.mts +313 -0
- package/dist/cleanup/index.mjs +1267 -0
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/core/index.d.mts +1 -1
- package/dist/core/index.mjs +4 -3
- package/dist/core-BGa8TXJR.mjs +221 -0
- package/dist/{createActionRouter-B6GvAuAX.mjs → createActionRouter-C-N7z2EL.mjs} +2 -2
- package/dist/{preloadResources-DfyngIN9.mjs → createApp-Du5G54TM.mjs} +2 -74
- package/dist/{core-EUNu0u07.mjs → defineResource-2zvVVpxg.mjs} +6 -224
- package/dist/docs/index.mjs +1 -1
- package/dist/events/index.d.mts +28 -2
- package/dist/events/index.mjs +20 -4
- package/dist/factory/index.d.mts +4 -3
- package/dist/factory/index.mjs +3 -1
- package/dist/{fastify-augmentation-dPNBVeIj.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +2 -1
- package/dist/index-CZY4EGga.d.mts +107 -0
- package/dist/{index-f4PTrk3B.d.mts → index-CjVg-Epg.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +6 -5
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/{mcpPlugin-Cb7olw0j.mjs → mcpPlugin-Cs6nRGCt.mjs} +3 -3
- package/dist/module-_-M7C5ES.mjs +29 -0
- package/dist/{openapi-DrLtog_B.mjs → openapi-BLV4OMXb.mjs} +1 -1
- package/dist/permissions/index.mjs +1 -1
- package/dist/{pipeline-BN5G05rd.mjs → pipeline-BGRnV-To.mjs} +1 -1
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{preloadResources-0oe-JSSS.d.mts → preloadResources-CM4uUaFv.d.mts} +2 -1
- package/dist/preloadResources-DSY5HGpS.mjs +45 -0
- package/dist/{realtime-Cuw_deA-.mjs → realtime-DEuDu9io.mjs} +1 -1
- package/dist/{registerRoute-v0Wb6Plt.mjs → registerRoute-DTp18Ah_.mjs} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +1 -1
- package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
- package/dist/testing/cleanupStoreContract.d.mts +10 -0
- package/dist/testing/cleanupStoreContract.mjs +375 -0
- package/dist/testing/index.d.mts +3 -2
- package/dist/testing/index.mjs +4 -2
- package/dist/testing/outboxStoreContract.d.mts +13 -0
- package/dist/testing/outboxStoreContract.mjs +247 -0
- package/dist/types-C_ZZRcD6.d.mts +450 -0
- package/dist/{index-KUoxjIq1.d.mts → types-Cu6vVJmh.d.mts} +1 -105
- package/package.json +27 -12
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @classytic/arc
|
|
2
2
|
|
|
3
|
+
[](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-
|
|
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 };
|