ankka-console 0.15.0 → 0.16.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.
@@ -1,4 +1,4 @@
1
- import { type AuthDiscovery, type BrokerDeclarationRequest, type ByteRatesRequest, type CreateDeployToken, type CreateOrganization, type DeployTokenCreated, type DeployTokenSummary, type GrantDetail, type GrantRequest, type HistoryEntry, type LogsResponse, type MachineRegistered, type MachineSummary, type MembersResponse, type OrganizationSummary, type ProjectBroker, type ProjectStatus, type CredentialReissued, type DatabaseSetting, type InstallationStatus, type ProjectHistoryView, type RestoreHoldStatus, type RehearsalView, type RestoreView, type ProjectDetail, type ProjectSecretSummary, type ProjectSummary, type ProjectTopic, type Quota, type ReceivedGrantDetail, type Role, type RolledBack, type ServiceStatus, type ServiceTopology, type SetRegistry, type TopicDeclarationRequest, type Whoami, type Installation } from "./schemas.ts";
1
+ import { type AuthDiscovery, type BrokerDeclarationRequest, type ByteRatesRequest, type CreateDeployToken, type CreateOrganization, type DeployTokenCreated, type DeployTokenSummary, type GrantDetail, type GrantRequest, type HistoryEntry, type LogsResponse, type MachineRegistered, type MachineSummary, type MembersResponse, type OrganizationSummary, type ProjectBroker, type ProjectStatus, type CredentialReissued, type DatabaseSetting, type InstallationStatus, type ProjectHistoryView, type RestoreHoldStatus, type RehearsalView, type RestoreView, type ProjectDetail, type ProjectSecretSummary, type ProjectSummary, type ProjectTopic, type Quota, type ReceivedGrantDetail, type Role, type RolledBack, type ServiceStatus, type ServiceTopology, type SetRegistry, type TopicDeclarationRequest, type Whoami, type Installation, type ErasureAsk, type ErasureCertificate, type ErasureReplace, type ErasureRequestSummary, type ErasureRequestView } from "./schemas.ts";
2
2
  /** `fetch`'s shape; the server supplies one that presents the console's certificate. */
3
3
  export type Transport = (url: string, init: RequestInit) => Promise<Response>;
4
4
  /**
@@ -127,6 +127,24 @@ export declare class ControlPlaneClient {
127
127
  receivedGrants(id: string): Promise<ReceivedGrantDetail[]>;
128
128
  /** Ends a grant from the grantor's side: a pending one is withdrawn, an accepted one revoked. */
129
129
  endGrant(id: string, grantId: string): Promise<void>;
130
+ /** Asks for the erasure of a data subject's personal data; an applied request for them already is answered as it is. */
131
+ askErasure(id: string, ask: ErasureAsk): Promise<ErasureRequestView>;
132
+ /** The project's erasure requests, newest first. */
133
+ listErasures(id: string, query?: {
134
+ subject?: string;
135
+ state?: string;
136
+ correlation?: string;
137
+ }): Promise<ErasureRequestSummary[]>;
138
+ /** One erasure request, with each service's completion and why the last attempt to apply it failed. */
139
+ getErasure(id: string, requestId: string): Promise<ErasureRequestView>;
140
+ /** Replaces a held request's not-before date and reason. */
141
+ replaceErasure(id: string, requestId: string, replace: ErasureReplace): Promise<ErasureRequestView>;
142
+ /** Withdraws a held request. */
143
+ withdrawErasure(id: string, requestId: string): Promise<ErasureRequestView>;
144
+ /** An owner's override of a hold, with a reason: the request is applied at once. */
145
+ overrideErasureHold(id: string, requestId: string, reason: string): Promise<ErasureRequestView>;
146
+ /** The erasure certificate of an applied request; refused (409) before it is applied. */
147
+ erasureCertificate(id: string, requestId: string): Promise<ErasureCertificate>;
130
148
  listServices(projectId: string): Promise<ServiceStatus[]>;
131
149
  getService(projectId: string, name: string): Promise<ServiceStatus>;
132
150
  /** The descriptor is sent as the text the person gave; the control plane is the one that validates it. */
@@ -1,5 +1,5 @@
1
1
  import { ControlPlaneError, ControlPlaneUnreachable, SignInRequired } from "./errors.js";
2
- import { authDiscoverySchema, deployTokenCreatedSchema, deployTokenSummarySchema, grantDetailSchema, historyEntrySchema, machineRegisteredSchema, machineSummarySchema, logsResponseSchema, membersResponseSchema, organizationSummarySchema, projectBrokerSchema, projectStatusSchema, credentialReissuedSchema, databaseSettingSchema, projectHistoryViewSchema, restoreHoldStatusSchema, rehearsalViewSchema, restoreViewSchema, projectDetailSchema, projectSecretSummarySchema, projectSummarySchema, projectTopicSchema, receivedGrantDetailSchema, rolledBackSchema, serviceStatusSchema, serviceTopologySchema, whoamiSchema, installationSchema, } from "./schemas.js";
2
+ import { authDiscoverySchema, deployTokenCreatedSchema, deployTokenSummarySchema, grantDetailSchema, historyEntrySchema, machineRegisteredSchema, machineSummarySchema, logsResponseSchema, membersResponseSchema, organizationSummarySchema, projectBrokerSchema, projectStatusSchema, credentialReissuedSchema, databaseSettingSchema, projectHistoryViewSchema, restoreHoldStatusSchema, rehearsalViewSchema, restoreViewSchema, projectDetailSchema, projectSecretSummarySchema, projectSummarySchema, projectTopicSchema, receivedGrantDetailSchema, rolledBackSchema, serviceStatusSchema, serviceTopologySchema, whoamiSchema, installationSchema, erasureCertificateSchema, erasureRequestSummarySchema, erasureRequestViewSchema, } from "./schemas.js";
3
3
  /** A descriptor is passed through as the control plane wrote it, as `applyService` takes one. */
4
4
  const anyJson = { parse: (value) => value };
5
5
  const arrayOf = (schema) => ({
@@ -272,6 +272,43 @@ export class ControlPlaneClient {
272
272
  endGrant(id, grantId) {
273
273
  return this.#call("DELETE", `/projects/${segment(id)}/grants/${segment(grantId)}`);
274
274
  }
275
+ // ── Erasure requests (feature 042) ────────────────────────────────────────
276
+ /** Asks for the erasure of a data subject's personal data; an applied request for them already is answered as it is. */
277
+ askErasure(id, ask) {
278
+ return this.#call("POST", `/projects/${segment(id)}/erasures`, { body: ask, schema: erasureRequestViewSchema });
279
+ }
280
+ /** The project's erasure requests, newest first. */
281
+ listErasures(id, query = {}) {
282
+ const params = new URLSearchParams();
283
+ if (query.subject)
284
+ params.set("subject", query.subject);
285
+ if (query.state)
286
+ params.set("state", query.state);
287
+ if (query.correlation)
288
+ params.set("correlation", query.correlation);
289
+ const q = params.size > 0 ? `?${params}` : "";
290
+ return this.#call("GET", `/projects/${segment(id)}/erasures${q}`, { schema: arrayOf(erasureRequestSummarySchema) });
291
+ }
292
+ /** One erasure request, with each service's completion and why the last attempt to apply it failed. */
293
+ getErasure(id, requestId) {
294
+ return this.#call("GET", `/projects/${segment(id)}/erasures/${segment(requestId)}`, { schema: erasureRequestViewSchema });
295
+ }
296
+ /** Replaces a held request's not-before date and reason. */
297
+ replaceErasure(id, requestId, replace) {
298
+ return this.#call("PUT", `/projects/${segment(id)}/erasures/${segment(requestId)}`, { body: replace, schema: erasureRequestViewSchema });
299
+ }
300
+ /** Withdraws a held request. */
301
+ withdrawErasure(id, requestId) {
302
+ return this.#call("POST", `/projects/${segment(id)}/erasures/${segment(requestId)}/withdraw`, { schema: erasureRequestViewSchema });
303
+ }
304
+ /** An owner's override of a hold, with a reason: the request is applied at once. */
305
+ overrideErasureHold(id, requestId, reason) {
306
+ return this.#call("POST", `/projects/${segment(id)}/erasures/${segment(requestId)}/override`, { body: { reason }, schema: erasureRequestViewSchema });
307
+ }
308
+ /** The erasure certificate of an applied request; refused (409) before it is applied. */
309
+ erasureCertificate(id, requestId) {
310
+ return this.#call("GET", `/projects/${segment(id)}/erasures/${segment(requestId)}/certificate`, { schema: erasureCertificateSchema });
311
+ }
275
312
  // ── Services ──────────────────────────────────────────────────────────────
276
313
  listServices(projectId) {
277
314
  return this.#call("GET", `/services/${segment(projectId)}`, { schema: arrayOf(serviceStatusSchema) });
@@ -2196,6 +2196,222 @@ export declare const rehearsalRequestSchema: z.ZodObject<{
2196
2196
  line: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2197
2197
  }, z.core.$strip>;
2198
2198
  export type RehearsalRequest = z.input<typeof rehearsalRequestSchema>;
2199
+ /** What a service's erasure handler reported for a run. */
2200
+ export declare const erasureHandlerViewSchema: z.ZodObject<{
2201
+ objectsErased: z.ZodNumber;
2202
+ finality: z.ZodString;
2203
+ error: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2204
+ }, z.core.$strip>;
2205
+ export type ErasureHandlerView = z.infer<typeof erasureHandlerViewSchema>;
2206
+ /** One service's completion of an erasure. */
2207
+ export declare const erasureCompletionViewSchema: z.ZodObject<{
2208
+ service: z.ZodString;
2209
+ completedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2210
+ rowsRedacted: z.ZodDefault<z.ZodNumber>;
2211
+ tokensDropped: z.ZodDefault<z.ZodNumber>;
2212
+ sessionsEnded: z.ZodDefault<z.ZodNumber>;
2213
+ handler: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2214
+ objectsErased: z.ZodNumber;
2215
+ finality: z.ZodString;
2216
+ error: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2217
+ }, z.core.$strip>>>, z.ZodTransform<{
2218
+ objectsErased: number;
2219
+ finality: string;
2220
+ error: string | undefined;
2221
+ } | undefined, {
2222
+ objectsErased: number;
2223
+ finality: string;
2224
+ error: string | undefined;
2225
+ } | null | undefined>>;
2226
+ }, z.core.$strip>;
2227
+ export type ErasureCompletionView = z.infer<typeof erasureCompletionViewSchema>;
2228
+ /** An owner's override of a hold. */
2229
+ export declare const erasureOverrideViewSchema: z.ZodObject<{
2230
+ owner: z.ZodString;
2231
+ reason: z.ZodString;
2232
+ at: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2233
+ }, z.core.$strip>;
2234
+ export type ErasureOverrideView = z.infer<typeof erasureOverrideViewSchema>;
2235
+ /** Why the last attempt to apply an erasure request failed, and since when. */
2236
+ export declare const erasureFailureViewSchema: z.ZodObject<{
2237
+ reason: z.ZodString;
2238
+ at: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2239
+ }, z.core.$strip>;
2240
+ export type ErasureFailureView = z.infer<typeof erasureFailureViewSchema>;
2241
+ /** The words an erasure request's state is said in today; any other is shown as it is. */
2242
+ export declare const erasureStates: readonly ["held", "applying", "applied", "withdrawn"];
2243
+ /** An erasure request, as a member reads it: where it stands, its hold, and each service's completion. */
2244
+ export declare const erasureRequestViewSchema: z.ZodObject<{
2245
+ id: z.ZodString;
2246
+ project: z.ZodString;
2247
+ subject: z.ZodString;
2248
+ state: z.ZodString;
2249
+ askedBy: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2250
+ askedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2251
+ notBefore: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2252
+ reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2253
+ correlation: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2254
+ lapsedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2255
+ holdOverride: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2256
+ owner: z.ZodString;
2257
+ reason: z.ZodString;
2258
+ at: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2259
+ }, z.core.$strip>>>, z.ZodTransform<{
2260
+ owner: string;
2261
+ reason: string;
2262
+ at: string | undefined;
2263
+ } | undefined, {
2264
+ owner: string;
2265
+ reason: string;
2266
+ at: string | undefined;
2267
+ } | null | undefined>>;
2268
+ erasureSeq: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodNumber>>, z.ZodTransform<number | undefined, number | null | undefined>>;
2269
+ destroyedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2270
+ completions: z.ZodDefault<z.ZodArray<z.ZodObject<{
2271
+ service: z.ZodString;
2272
+ completedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2273
+ rowsRedacted: z.ZodDefault<z.ZodNumber>;
2274
+ tokensDropped: z.ZodDefault<z.ZodNumber>;
2275
+ sessionsEnded: z.ZodDefault<z.ZodNumber>;
2276
+ handler: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2277
+ objectsErased: z.ZodNumber;
2278
+ finality: z.ZodString;
2279
+ error: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2280
+ }, z.core.$strip>>>, z.ZodTransform<{
2281
+ objectsErased: number;
2282
+ finality: string;
2283
+ error: string | undefined;
2284
+ } | undefined, {
2285
+ objectsErased: number;
2286
+ finality: string;
2287
+ error: string | undefined;
2288
+ } | null | undefined>>;
2289
+ }, z.core.$strip>>>;
2290
+ objectsFinalAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2291
+ finalAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2292
+ lastFailure: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2293
+ reason: z.ZodString;
2294
+ at: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2295
+ }, z.core.$strip>>>, z.ZodTransform<{
2296
+ reason: string;
2297
+ at: string | undefined;
2298
+ } | undefined, {
2299
+ reason: string;
2300
+ at: string | undefined;
2301
+ } | null | undefined>>;
2302
+ }, z.core.$strip>;
2303
+ export type ErasureRequestView = z.infer<typeof erasureRequestViewSchema>;
2304
+ /** One line of a project's erasure requests; `project` is set where a listing spans projects. */
2305
+ export declare const erasureRequestSummarySchema: z.ZodObject<{
2306
+ id: z.ZodString;
2307
+ subject: z.ZodString;
2308
+ state: z.ZodString;
2309
+ correlation: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2310
+ notBefore: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2311
+ askedBy: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2312
+ askedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2313
+ appliedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2314
+ finalAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2315
+ project: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2316
+ }, z.core.$strip>;
2317
+ export type ErasureRequestSummary = z.infer<typeof erasureRequestSummarySchema>;
2318
+ /** The hold an erasure waited under, and when it lapsed. */
2319
+ export declare const erasureHoldViewSchema: z.ZodObject<{
2320
+ notBefore: z.ZodString;
2321
+ reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2322
+ lapsedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2323
+ }, z.core.$strip>;
2324
+ /** A service's part of an erasure, as the certificate states it. */
2325
+ export declare const erasureCertificateServiceSchema: z.ZodObject<{
2326
+ service: z.ZodString;
2327
+ completedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2328
+ handler: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2329
+ objectsErased: z.ZodNumber;
2330
+ finality: z.ZodString;
2331
+ error: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2332
+ }, z.core.$strip>>>, z.ZodTransform<{
2333
+ objectsErased: number;
2334
+ finality: string;
2335
+ error: string | undefined;
2336
+ } | undefined, {
2337
+ objectsErased: number;
2338
+ finality: string;
2339
+ error: string | undefined;
2340
+ } | null | undefined>>;
2341
+ }, z.core.$strip>;
2342
+ /** The erasure certificate of an applied erasure request. It holds no personal field. */
2343
+ export declare const erasureCertificateSchema: z.ZodObject<{
2344
+ request: z.ZodString;
2345
+ project: z.ZodString;
2346
+ subject: z.ZodString;
2347
+ askedBy: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2348
+ askedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2349
+ hold: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2350
+ notBefore: z.ZodString;
2351
+ reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2352
+ lapsedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2353
+ }, z.core.$strip>>>, z.ZodTransform<{
2354
+ notBefore: string;
2355
+ reason: string | undefined;
2356
+ lapsedAt: string | undefined;
2357
+ } | undefined, {
2358
+ notBefore: string;
2359
+ reason: string | undefined;
2360
+ lapsedAt: string | undefined;
2361
+ } | null | undefined>>;
2362
+ holdOverride: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2363
+ owner: z.ZodString;
2364
+ reason: z.ZodString;
2365
+ at: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2366
+ }, z.core.$strip>>>, z.ZodTransform<{
2367
+ owner: string;
2368
+ reason: string;
2369
+ at: string | undefined;
2370
+ } | undefined, {
2371
+ owner: string;
2372
+ reason: string;
2373
+ at: string | undefined;
2374
+ } | null | undefined>>;
2375
+ keyDestroyedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2376
+ services: z.ZodDefault<z.ZodArray<z.ZodObject<{
2377
+ service: z.ZodString;
2378
+ completedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2379
+ handler: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodObject<{
2380
+ objectsErased: z.ZodNumber;
2381
+ finality: z.ZodString;
2382
+ error: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2383
+ }, z.core.$strip>>>, z.ZodTransform<{
2384
+ objectsErased: number;
2385
+ finality: string;
2386
+ error: string | undefined;
2387
+ } | undefined, {
2388
+ objectsErased: number;
2389
+ finality: string;
2390
+ error: string | undefined;
2391
+ } | null | undefined>>;
2392
+ }, z.core.$strip>>>;
2393
+ finalAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2394
+ }, z.core.$strip>;
2395
+ export type ErasureCertificate = z.infer<typeof erasureCertificateSchema>;
2396
+ /** `POST /projects/{id}/erasures`: the data subject, and an optional hold. */
2397
+ export declare const erasureAskSchema: z.ZodObject<{
2398
+ subject: z.ZodString;
2399
+ notBefore: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2400
+ reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2401
+ correlation: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2402
+ }, z.core.$strip>;
2403
+ export type ErasureAsk = z.input<typeof erasureAskSchema>;
2404
+ /** `PUT …/erasures/{rid}`: a held request's new not-before date and reason, in place of the old. */
2405
+ export declare const erasureReplaceSchema: z.ZodObject<{
2406
+ notBefore: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2407
+ reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
2408
+ }, z.core.$strip>;
2409
+ export type ErasureReplace = z.input<typeof erasureReplaceSchema>;
2410
+ /** `POST …/erasures/{rid}/override`: an owner's reason for applying a held request now. */
2411
+ export declare const erasureOverrideSchema: z.ZodObject<{
2412
+ reason: z.ZodDefault<z.ZodString>;
2413
+ }, z.core.$strip>;
2414
+ export type ErasureOverride = z.input<typeof erasureOverrideSchema>;
2199
2415
  export declare const credentialReissuedSchema: z.ZodObject<{
2200
2416
  project: z.ZodString;
2201
2417
  generation: z.ZodNumber;
@@ -704,6 +704,93 @@ export const restoreRequestSchema = z.object({ moment: z.string(), line: optiona
704
704
  export const switchRequestSchema = z.object({ cluster: z.string() });
705
705
  /** `POST /projects/{id}/rehearsals`: both may be left out, for the latest restorable moment. */
706
706
  export const rehearsalRequestSchema = z.object({ moment: optional(z.string()), line: optional(z.string()) });
707
+ // ── Erasure (feature 042), from `controlplane-api`'s `erasures.scala` ────────
708
+ /** What a service's erasure handler reported for a run. */
709
+ export const erasureHandlerViewSchema = z.object({
710
+ objectsErased: z.number().int(),
711
+ finality: z.string(),
712
+ error: optional(z.string()),
713
+ });
714
+ /** One service's completion of an erasure. */
715
+ export const erasureCompletionViewSchema = z.object({
716
+ service: z.string(),
717
+ completedAt: optional(z.string()),
718
+ rowsRedacted: z.number().int().default(0),
719
+ tokensDropped: z.number().int().default(0),
720
+ sessionsEnded: z.number().int().default(0),
721
+ handler: optional(erasureHandlerViewSchema),
722
+ });
723
+ /** An owner's override of a hold. */
724
+ export const erasureOverrideViewSchema = z.object({ owner: z.string(), reason: z.string(), at: optional(z.string()) });
725
+ /** Why the last attempt to apply an erasure request failed, and since when. */
726
+ export const erasureFailureViewSchema = z.object({ reason: z.string(), at: optional(z.string()) });
727
+ /** The words an erasure request's state is said in today; any other is shown as it is. */
728
+ export const erasureStates = ["held", "applying", "applied", "withdrawn"];
729
+ /** An erasure request, as a member reads it: where it stands, its hold, and each service's completion. */
730
+ export const erasureRequestViewSchema = z.object({
731
+ id: z.string(),
732
+ project: z.string(),
733
+ subject: z.string(),
734
+ state: z.string(),
735
+ askedBy: optional(z.string()),
736
+ askedAt: optional(z.string()),
737
+ notBefore: optional(z.string()),
738
+ reason: optional(z.string()),
739
+ correlation: optional(z.string()),
740
+ lapsedAt: optional(z.string()),
741
+ holdOverride: optional(erasureOverrideViewSchema),
742
+ erasureSeq: optional(z.number().int()),
743
+ destroyedAt: optional(z.string()),
744
+ completions: z.array(erasureCompletionViewSchema).default([]),
745
+ objectsFinalAt: optional(z.string()),
746
+ finalAt: optional(z.string()),
747
+ lastFailure: optional(erasureFailureViewSchema),
748
+ });
749
+ /** One line of a project's erasure requests; `project` is set where a listing spans projects. */
750
+ export const erasureRequestSummarySchema = z.object({
751
+ id: z.string(),
752
+ subject: z.string(),
753
+ state: z.string(),
754
+ correlation: optional(z.string()),
755
+ notBefore: optional(z.string()),
756
+ askedBy: optional(z.string()),
757
+ askedAt: optional(z.string()),
758
+ appliedAt: optional(z.string()),
759
+ finalAt: optional(z.string()),
760
+ project: optional(z.string()),
761
+ });
762
+ /** The hold an erasure waited under, and when it lapsed. */
763
+ export const erasureHoldViewSchema = z.object({ notBefore: z.string(), reason: optional(z.string()), lapsedAt: optional(z.string()) });
764
+ /** A service's part of an erasure, as the certificate states it. */
765
+ export const erasureCertificateServiceSchema = z.object({
766
+ service: z.string(),
767
+ completedAt: optional(z.string()),
768
+ handler: optional(erasureHandlerViewSchema),
769
+ });
770
+ /** The erasure certificate of an applied erasure request. It holds no personal field. */
771
+ export const erasureCertificateSchema = z.object({
772
+ request: z.string(),
773
+ project: z.string(),
774
+ subject: z.string(),
775
+ askedBy: optional(z.string()),
776
+ askedAt: optional(z.string()),
777
+ hold: optional(erasureHoldViewSchema),
778
+ holdOverride: optional(erasureOverrideViewSchema),
779
+ keyDestroyedAt: optional(z.string()),
780
+ services: z.array(erasureCertificateServiceSchema).default([]),
781
+ finalAt: optional(z.string()),
782
+ });
783
+ /** `POST /projects/{id}/erasures`: the data subject, and an optional hold. */
784
+ export const erasureAskSchema = z.object({
785
+ subject: z.string(),
786
+ notBefore: optional(z.string()),
787
+ reason: optional(z.string()),
788
+ correlation: optional(z.string()),
789
+ });
790
+ /** `PUT …/erasures/{rid}`: a held request's new not-before date and reason, in place of the old. */
791
+ export const erasureReplaceSchema = z.object({ notBefore: optional(z.string()), reason: optional(z.string()) });
792
+ /** `POST …/erasures/{rid}/override`: an owner's reason for applying a held request now. */
793
+ export const erasureOverrideSchema = z.object({ reason: z.string().default("") });
707
794
  export const credentialReissuedSchema = z.object({ project: z.string(), generation: z.number().int() });
708
795
  /** What a project asks of its database, whole. */
709
796
  export const databaseSettingSchema = z.object({
@@ -875,4 +962,10 @@ export const schemasByType = {
875
962
  ByteRatesRequest: byteRatesRequestSchema,
876
963
  TokenResponse: tokenResponseSchema,
877
964
  JsonWebKeySet: jsonWebKeySetSchema,
965
+ ErasureAsk: erasureAskSchema,
966
+ ErasureReplace: erasureReplaceSchema,
967
+ ErasureOverride: erasureOverrideSchema,
968
+ ErasureRequestView: erasureRequestViewSchema,
969
+ ErasureRequestSummary: erasureRequestSummarySchema,
970
+ ErasureCertificate: erasureCertificateSchema,
878
971
  };
@@ -1,7 +1,7 @@
1
1
  import type { ComponentType } from "react";
2
2
  import type { OrganizationSummary, ProjectDetail, ServiceStatus } from "../client/schemas.ts";
3
3
  /** Every operation the console offers, by the name a host uses to add beside it or hide it. */
4
- export declare const operations: readonly ["organization.create", "organization.rename", "organization.delete", "organization.disable", "organization.enable", "organization.quota.set", "organization.quota.clear", "member.invite", "member.role", "member.remove", "invitation.withdraw", "member.repair", "token.create", "token.revoke", "machine.register", "machine.delete", "machine.byte-rates", "project.create", "project.rename", "project.delete", "registry.set", "registry.clear", "project-secret.set", "project-secret.unset", "project-topic.set", "project-topic.unset", "project-broker.set", "project-broker.unset", "project.restore", "project.rehearse", "project.database", "project.backup-credential", "installation.release", "service.switch", "grant.make", "grant.end", "grant.answer", "grant.relinquish", "service.apply", "service.pause", "service.resume", "service.restart", "service.rollback", "service.expose", "service.unexpose", "service-hostname.add", "service-hostname.remove", "service.delete", "service.logs", "service.storage-credential", "service.storage-move", "service.storage-settings", "project-location.set", "project-location.clear"];
4
+ export declare const operations: readonly ["organization.create", "organization.rename", "organization.delete", "organization.disable", "organization.enable", "organization.quota.set", "organization.quota.clear", "member.invite", "member.role", "member.remove", "invitation.withdraw", "member.repair", "token.create", "token.revoke", "machine.register", "machine.delete", "machine.byte-rates", "project.create", "project.rename", "project.delete", "registry.set", "registry.clear", "project-secret.set", "project-secret.unset", "project-topic.set", "project-topic.unset", "project-broker.set", "project-broker.unset", "project.restore", "project.rehearse", "project.database", "project.backup-credential", "installation.release", "service.switch", "grant.make", "grant.end", "grant.answer", "grant.relinquish", "service.apply", "service.pause", "service.resume", "service.restart", "service.rollback", "service.expose", "service.unexpose", "service-hostname.add", "service-hostname.remove", "service.delete", "service.logs", "service.storage-credential", "service.storage-move", "service.storage-settings", "project-location.set", "project-location.clear", "erasure.ask", "erasure.replace", "erasure.withdraw", "erasure.override"];
5
5
  export type Operation = (typeof operations)[number];
6
6
  export interface PanelEntities {
7
7
  organization: OrganizationSummary;
@@ -56,4 +56,9 @@ export const operations = [
56
56
  "service.storage-settings",
57
57
  "project-location.set",
58
58
  "project-location.clear",
59
+ // Feature 042: a project's erasure requests. An override is an owner's; the control plane refuses a member's.
60
+ "erasure.ask",
61
+ "erasure.replace",
62
+ "erasure.withdraw",
63
+ "erasure.override",
59
64
  ];
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `GET <mount>/projects/:projectId/erasures/:requestId/certificate`: an applied erasure request's
3
+ * erasure certificate, as a JSON file to keep. Read as the person; refused as the control plane
4
+ * refuses it (409 before the request is applied).
5
+ */
6
+ import type { LoaderFunctionArgs } from "react-router";
7
+ export declare function loader({ params, context }: LoaderFunctionArgs): Promise<Response>;
@@ -0,0 +1,12 @@
1
+ import { guard, useConsoleContext } from "../context.js";
2
+ export async function loader({ params, context }) {
3
+ const ctx = useConsoleContext(context);
4
+ const { projectId, requestId } = params;
5
+ return guard(ctx, async () => {
6
+ const certificate = await ctx.client.erasureCertificate(projectId, requestId);
7
+ const file = `erasure-certificate-${requestId.replace(/[^A-Za-z0-9_-]/g, "")}.json`;
8
+ return new Response(JSON.stringify(certificate, null, 2) + "\n", {
9
+ headers: { "content-type": "application/json; charset=utf-8", "content-disposition": `attachment; filename="${file}"`, "cache-control": "no-store" },
10
+ });
11
+ });
12
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * An erasure request (feature 042): its data subject, where it stands, its hold and who changed it,
3
+ * why the last attempt to apply it failed, and each service's completion. A held request's not-before
4
+ * date can be changed, the request withdrawn, or its hold overridden by an owner with a reason; an
5
+ * applied one gives its erasure certificate to download. Reached by id from the project's page.
6
+ */
7
+ import { type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction } from "react-router";
8
+ import { ConsoleErrorBoundary } from "../ui/errors.tsx";
9
+ export declare const meta: MetaFunction<typeof loader>;
10
+ export declare function loader({ params, context }: LoaderFunctionArgs): Promise<{
11
+ console: import("../context.ts").ConsolePageData;
12
+ projectId: string;
13
+ request: {
14
+ id: string;
15
+ project: string;
16
+ subject: string;
17
+ state: string;
18
+ askedBy: string | undefined;
19
+ askedAt: string | undefined;
20
+ notBefore: string | undefined;
21
+ reason: string | undefined;
22
+ correlation: string | undefined;
23
+ lapsedAt: string | undefined;
24
+ holdOverride: {
25
+ owner: string;
26
+ reason: string;
27
+ at: string | undefined;
28
+ } | undefined;
29
+ erasureSeq: number | undefined;
30
+ destroyedAt: string | undefined;
31
+ completions: {
32
+ service: string;
33
+ completedAt: string | undefined;
34
+ rowsRedacted: number;
35
+ tokensDropped: number;
36
+ sessionsEnded: number;
37
+ handler: {
38
+ objectsErased: number;
39
+ finality: string;
40
+ error: string | undefined;
41
+ } | undefined;
42
+ }[];
43
+ objectsFinalAt: string | undefined;
44
+ finalAt: string | undefined;
45
+ lastFailure: {
46
+ reason: string;
47
+ at: string | undefined;
48
+ } | undefined;
49
+ };
50
+ }>;
51
+ export declare function action({ request, params, context }: ActionFunctionArgs): Promise<import("react-router").UNSAFE_DataWithResponseInit<import("../context.ts").ActionRefusal> | Response>;
52
+ export default function Erasure(): import("react").JSX.Element;
53
+ export declare const ErrorBoundary: typeof ConsoleErrorBoundary;
@@ -0,0 +1,74 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * An erasure request (feature 042): its data subject, where it stands, its hold and who changed it,
4
+ * why the last attempt to apply it failed, and each service's completion. A held request's not-before
5
+ * date can be changed, the request withdrawn, or its hold overridden by an owner with a reason; an
6
+ * applied one gives its erasure certificate to download. Reached by id from the project's page.
7
+ */
8
+ import { redirect, useLoaderData } from "react-router";
9
+ import { act, guard, pageData, projectShell, text, useConsoleContext } from "../context.js";
10
+ import { ConsoleErrorBoundary } from "../ui/errors.js";
11
+ import { ConsoleForm, Field, Submit, useConsole, when } from "../ui/console.js";
12
+ import { Page, SectionTitle } from "../ui/shell.js";
13
+ import { Refused, useRefusal } from "../ui/refused.js";
14
+ import { HostActions } from "../extensions/render.js";
15
+ import { button } from "../ui/primitives/button.js";
16
+ import { dateOf, notBeforeOf } from "../ui/erasures.js";
17
+ export const meta = ({ loaderData }) => [{ title: `Erasure request for ${loaderData?.request.subject ?? "a data subject"} · ankka` }];
18
+ export async function loader({ params, context }) {
19
+ const ctx = useConsoleContext(context);
20
+ const { projectId, requestId } = params;
21
+ return guard(ctx, async () => {
22
+ const [request, project, services, page] = await Promise.all([
23
+ ctx.client.getErasure(projectId, requestId),
24
+ ctx.client.getProject(projectId),
25
+ ctx.client.listServices(projectId),
26
+ pageData(ctx),
27
+ ]);
28
+ const organization = await ctx.client.getOrganization(project.organizationId);
29
+ return {
30
+ console: projectShell(page, organization, project, services, [{ label: "Erasure request" }]),
31
+ projectId,
32
+ request,
33
+ };
34
+ });
35
+ }
36
+ export async function action({ request, params, context }) {
37
+ const ctx = useConsoleContext(context);
38
+ const { projectId, requestId } = params;
39
+ const form = await request.formData();
40
+ const intent = text(form, "intent");
41
+ const self = ctx.href(`projects/${encodeURIComponent(projectId)}/erasures/${encodeURIComponent(requestId)}`);
42
+ return act(ctx, intent, form, async () => {
43
+ switch (intent) {
44
+ case "erasure-replace":
45
+ await ctx.client.replaceErasure(projectId, requestId, {
46
+ notBefore: notBeforeOf(text(form, "erasureNotBefore")),
47
+ reason: text(form, "erasureReason") || undefined,
48
+ });
49
+ return redirect(self);
50
+ case "erasure-withdraw":
51
+ await ctx.client.withdrawErasure(projectId, requestId);
52
+ return redirect(self);
53
+ case "erasure-override":
54
+ // The reason is sent as given; an empty one is the control plane's to refuse, in its words.
55
+ await ctx.client.overrideErasureHold(projectId, requestId, text(form, "overrideReason"));
56
+ return redirect(self);
57
+ default:
58
+ throw new Response(`unknown operation '${intent}'`, { status: 400 });
59
+ }
60
+ });
61
+ }
62
+ export default function Erasure() {
63
+ const { projectId, request: r } = useLoaderData();
64
+ const { shows, href } = useConsole();
65
+ const replaceRefusal = useRefusal("erasure-replace");
66
+ const overrideRefusal = useRefusal("erasure-override");
67
+ const held = r.state === "held";
68
+ const path = `projects/${encodeURIComponent(projectId)}/erasures/${encodeURIComponent(r.id)}`;
69
+ const inspector = held ? (_jsxs(_Fragment, { children: [shows("erasure.override") ? (_jsxs("section", { className: "ac-form", "aria-labelledby": "erasure-override-title", children: [_jsx(SectionTitle, { children: _jsx("span", { id: "erasure-override-title", children: "Override hold" }) }), _jsxs(ConsoleForm, { intent: "erasure-override", className: "ac-form", children: [_jsx(Field, { label: "Reason for the override", name: "overrideReason", required: true, autoComplete: "off", defaultValue: overrideRefusal?.values.overrideReason, hint: "Owners only. The request is applied at once, and the reason is on its erasure certificate." }), _jsx(Refused, { intent: "erasure-override" }), _jsx("div", { children: _jsx(Submit, { intent: "erasure-override", danger: true, children: "Override hold" }) })] }), _jsx("div", { className: "ac-ops", children: _jsx(HostActions, { operation: "erasure.override", entity: r }) })] })) : null, shows("erasure.replace") ? (_jsxs("details", { className: "ac-more", open: replaceRefusal !== undefined || undefined, children: [_jsx("summary", { children: "Change the hold" }), _jsxs(ConsoleForm, { intent: "erasure-replace", className: "ac-form", children: [_jsx(Field, { label: "Not-before date", name: "erasureNotBefore", type: "date", defaultValue: replaceRefusal?.values.erasureNotBefore ?? dateOf(r.notBefore), hint: "The start of that day, UTC. It replaces the date and the reason the request has." }), _jsx(Field, { label: "Reason", name: "erasureReason", autoComplete: "off", defaultValue: replaceRefusal?.values.erasureReason ?? r.reason }), _jsx(Refused, { intent: "erasure-replace" }), _jsx("div", { children: _jsx(Submit, { intent: "erasure-replace", children: "Change the hold" }) })] })] })) : null, shows("erasure.withdraw") ? (_jsxs(ConsoleForm, { intent: "erasure-withdraw", className: "ac-inline", children: [_jsx(Submit, { intent: "erasure-withdraw", children: "Withdraw" }), _jsx(Refused, { intent: "erasure-withdraw" })] })) : null] })) : undefined;
70
+ return (_jsxs(Page, { inspector: inspector, children: [_jsxs("h1", { children: ["Erasure request for ", _jsx("code", { children: r.subject })] }), _jsxs("dl", { className: "ac-card ac-facts", "data-erasure": r.id, "data-state": r.state, children: [_jsx("dt", { children: "Id" }), _jsx("dd", { children: _jsx("code", { children: r.id }) }), _jsx("dt", { children: "Data subject" }), _jsx("dd", { children: _jsx("code", { children: r.subject }) }), _jsx("dt", { children: "State" }), _jsx("dd", { "data-erasure-state": true, children: r.state }), _jsx("dt", { children: "Asked" }), _jsxs("dd", { "data-asked-by": r.askedBy, children: [r.askedAt ? when(r.askedAt) : "—", r.askedBy ? ` by ${r.askedBy}` : ""] }), _jsx("dt", { children: "Not-before date" }), _jsxs("dd", { "data-not-before": true, children: [r.notBefore ? when(r.notBefore) : "none: applied when asked", r.reason ? ` — ${r.reason}` : "", r.lapsedAt ? ` (the hold lapsed ${when(r.lapsedAt)})` : ""] }), r.holdOverride ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: "Hold overridden" }), _jsxs("dd", { "data-override": true, children: ["by ", r.holdOverride.owner, r.holdOverride.at ? `, ${when(r.holdOverride.at)}` : "", ": ", r.holdOverride.reason] })] })) : null, r.correlation ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: "Correlation" }), _jsx("dd", { children: _jsx("code", { children: r.correlation }) })] })) : null, r.destroyedAt ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: "Applied" }), _jsxs("dd", { children: [when(r.destroyedAt), ": the data subject's key is destroyed"] })] })) : null, r.lastFailure ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: "Not applied yet" }), _jsxs("dd", { "data-last-failure": true, role: "status", children: [r.lastFailure.reason, r.lastFailure.at ? `, since ${when(r.lastFailure.at)}` : "", ". The platform tries again."] })] })) : null, r.finalAt ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: "Final" }), _jsx("dd", { children: when(r.finalAt) })] })) : null] }), _jsxs("section", { className: "ac-card", "aria-labelledby": "completion", children: [_jsx("h2", { id: "completion", children: "Completion" }), r.completions.length === 0 ? (_jsx("p", { className: "ac-empty", children: r.state === "applied" ? "No service has reported its completion yet." : "Each service reports its completion once the request is applied." })) : (_jsx("div", { className: "ac-table-wrap", children: _jsxs("table", { className: "ac-table", "aria-describedby": "completion", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Service" }), _jsx("th", { scope: "col", children: "Completed" }), _jsx("th", { scope: "col", children: "Rows redacted" }), _jsx("th", { scope: "col", children: "Tokens dropped" }), _jsx("th", { scope: "col", children: "Sessions ended" }), _jsx("th", { scope: "col", children: "Erasure handler" })] }) }), _jsx("tbody", { children: r.completions.map((c) => (_jsxs("tr", { "data-completion": c.service, children: [_jsx("td", { children: c.service }), _jsx("td", { children: c.completedAt ? when(c.completedAt) : "not yet" }), _jsx("td", { children: c.rowsRedacted }), _jsx("td", { children: c.tokensDropped }), _jsx("td", { children: c.sessionsEnded }), _jsx("td", { children: c.handler
71
+ ? `${c.handler.objectsErased} objects erased, ${c.handler.finality}${c.handler.error ? `; failed: ${c.handler.error}` : ""}`
72
+ : "—" })] }, c.service))) })] }) })), r.state === "applied" ? (_jsx("p", { children: _jsx("a", { className: button(), href: href(`${path}/certificate`), download: `erasure-certificate-${r.id}.json`, children: "Download the erasure certificate" }) })) : null] })] }));
73
+ }
74
+ export const ErrorBoundary = ConsoleErrorBoundary;