@pithy-sh/core 0.10.0 → 0.12.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/dist/access/access.d.ts +172 -0
- package/dist/access/access.d.ts.map +1 -0
- package/dist/access/access.js +147 -0
- package/dist/access/acting.d.ts +49 -0
- package/dist/access/acting.d.ts.map +1 -0
- package/dist/access/acting.js +21 -0
- package/dist/access/policyCheck.d.ts +33 -0
- package/dist/access/policyCheck.d.ts.map +1 -0
- package/dist/access/policyCheck.js +48 -0
- package/dist/access/usage.d.ts +83 -0
- package/dist/access/usage.d.ts.map +1 -0
- package/dist/access/usage.js +104 -0
- package/dist/access/visibility.d.ts +66 -0
- package/dist/access/visibility.d.ts.map +1 -0
- package/dist/access/visibility.js +86 -0
- package/dist/audit/auditEvent.d.ts +5 -0
- package/dist/audit/auditEvent.d.ts.map +1 -1
- package/dist/audit/auditEvent.js +2 -0
- package/dist/audit/recorder.d.ts +10 -2
- package/dist/audit/recorder.d.ts.map +1 -1
- package/dist/capability/bindings.d.ts +47 -2
- package/dist/capability/bindings.d.ts.map +1 -1
- package/dist/capability/bindings.js +117 -7
- package/dist/capability/capability.d.ts +110 -28
- package/dist/capability/capability.d.ts.map +1 -1
- package/dist/capability/capability.js +2 -0
- package/dist/capability/manifest.d.ts +2 -1
- package/dist/capability/manifest.d.ts.map +1 -1
- package/dist/capability/manifest.js +1 -1
- package/dist/capability/purge.d.ts +80 -0
- package/dist/capability/purge.d.ts.map +1 -0
- package/dist/capability/purge.js +76 -0
- package/dist/capability/settings.d.ts +86 -2
- package/dist/capability/settings.d.ts.map +1 -1
- package/dist/capability/settings.js +80 -1
- package/dist/cli/commandCatalog.generated.d.ts +238 -24
- package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
- package/dist/cli/commandCatalog.generated.js +251 -22
- package/dist/cli/commandFacts.d.ts +2 -1
- package/dist/cli/commandFacts.d.ts.map +1 -1
- package/dist/cli/commandFacts.js +2 -0
- package/dist/controlPlane/capability.d.ts.map +1 -1
- package/dist/controlPlane/capability.js +19 -2
- package/dist/controlPlane/context.d.ts +5 -0
- package/dist/controlPlane/context.d.ts.map +1 -1
- package/dist/controlPlane/context.js +3 -1
- package/dist/controlPlane/data/connection.d.ts +49 -12
- package/dist/controlPlane/data/connection.d.ts.map +1 -1
- package/dist/controlPlane/data/connection.js +56 -2
- package/dist/controlPlane/discovery/adminRoute.d.ts +29 -0
- package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
- package/dist/controlPlane/discovery/adminRoute.js +3 -0
- package/dist/controlPlane/error/errors.d.ts +4 -3
- package/dist/controlPlane/error/errors.d.ts.map +1 -1
- package/dist/controlPlane/http/cors.d.ts +4 -4
- package/dist/controlPlane/http/cors.js +4 -4
- package/dist/controlPlane/http/guard.d.ts +11 -0
- package/dist/controlPlane/http/guard.d.ts.map +1 -1
- package/dist/controlPlane/http/guard.js +17 -1
- package/dist/controlPlane/http/handlers.d.ts +139 -0
- package/dist/controlPlane/http/handlers.d.ts.map +1 -1
- package/dist/controlPlane/http/handlers.js +67 -4
- package/dist/controlPlane/http/readiness.d.ts +33 -0
- package/dist/controlPlane/http/readiness.d.ts.map +1 -0
- package/dist/controlPlane/http/readiness.js +39 -0
- package/dist/controlPlane/http/responses.d.ts +245 -0
- package/dist/controlPlane/http/responses.d.ts.map +1 -1
- package/dist/controlPlane/http/responses.js +101 -4
- package/dist/controlPlane/http/routes.d.ts +2 -0
- package/dist/controlPlane/http/routes.d.ts.map +1 -1
- package/dist/controlPlane/http/routes.js +31 -3
- package/dist/controlPlane/http/verify.d.ts.map +1 -1
- package/dist/controlPlane/http/verify.js +2 -1
- package/dist/controlPlane/migrationState/migrationState.d.ts +102 -0
- package/dist/controlPlane/migrationState/migrationState.d.ts.map +1 -0
- package/dist/controlPlane/migrationState/migrationState.js +142 -0
- package/dist/controlPlane/migrations/0003_connection_principal.d.ts +21 -0
- package/dist/controlPlane/migrations/0003_connection_principal.d.ts.map +1 -0
- package/dist/controlPlane/migrations/0003_connection_principal.js +33 -0
- package/dist/controlPlane/scope/scope.d.ts +23 -2
- package/dist/controlPlane/scope/scope.d.ts.map +1 -1
- package/dist/controlPlane/scope/scope.js +24 -3
- package/dist/createBackend.d.ts.map +1 -1
- package/dist/createBackend.js +9 -2
- package/dist/createEntrypoint.d.ts.map +1 -1
- package/dist/createEntrypoint.js +5 -3
- package/dist/crypto/envelope.d.ts +86 -0
- package/dist/crypto/envelope.d.ts.map +1 -0
- package/dist/crypto/envelope.js +129 -0
- package/dist/crypto/fieldKeys.d.ts +41 -0
- package/dist/crypto/fieldKeys.d.ts.map +1 -0
- package/dist/crypto/fieldKeys.js +56 -0
- package/dist/data/codecs.d.ts +23 -10
- package/dist/data/codecs.d.ts.map +1 -1
- package/dist/data/codecs.js +50 -36
- package/dist/entitlement/allowance.d.ts +25 -0
- package/dist/entitlement/allowance.d.ts.map +1 -0
- package/dist/entitlement/allowance.js +80 -0
- package/dist/entitlement/entitlement.d.ts +41 -0
- package/dist/entitlement/entitlement.d.ts.map +1 -1
- package/dist/entitlement/meter.d.ts +57 -0
- package/dist/entitlement/meter.d.ts.map +1 -0
- package/dist/entitlement/meter.js +13 -0
- package/dist/error/extend.d.ts +6 -5
- package/dist/error/extend.d.ts.map +1 -1
- package/dist/error/http.d.ts +168 -0
- package/dist/error/http.d.ts.map +1 -1
- package/dist/error/payload.d.ts +336 -0
- package/dist/error/payload.d.ts.map +1 -1
- package/dist/error/payload.js +116 -4
- package/dist/error/pithyError.d.ts +18 -7
- package/dist/error/pithyError.d.ts.map +1 -1
- package/dist/error/pithyError.js +6 -0
- package/dist/http/authContext.d.ts +7 -0
- package/dist/http/authContext.d.ts.map +1 -1
- package/dist/http/authContext.js +8 -1
- package/dist/http/scopeGates.d.ts +20 -0
- package/dist/http/scopeGates.d.ts.map +1 -0
- package/dist/http/scopeGates.js +36 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/logger/logger.d.ts +12 -5
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/record.d.ts +98 -0
- package/dist/logger/record.d.ts.map +1 -1
- package/dist/migrations/capabilitySets.d.ts +23 -0
- package/dist/migrations/capabilitySets.d.ts.map +1 -0
- package/dist/migrations/capabilitySets.js +74 -0
- package/dist/migrations/registry.d.ts +26 -0
- package/dist/migrations/registry.d.ts.map +1 -1
- package/dist/migrations/registry.js +38 -7
- package/dist/migrations/state.d.ts +60 -0
- package/dist/migrations/state.d.ts.map +1 -0
- package/dist/migrations/state.js +46 -0
- package/dist/naming/feature.d.ts +27 -5
- package/dist/naming/feature.d.ts.map +1 -1
- package/dist/naming/feature.js +36 -6
- package/dist/naming/limits.d.ts +47 -3
- package/dist/naming/limits.d.ts.map +1 -1
- package/dist/naming/limits.js +47 -3
- package/dist/naming/provisionScope.d.ts +6 -0
- package/dist/naming/provisionScope.d.ts.map +1 -1
- package/dist/naming/provisionScope.js +8 -3
- package/dist/naming/resourceNames.d.ts +21 -2
- package/dist/naming/resourceNames.d.ts.map +1 -1
- package/dist/naming/resourceNames.js +21 -3
- package/dist/privacy/blindIndex.d.ts +27 -0
- package/dist/privacy/blindIndex.d.ts.map +1 -0
- package/dist/privacy/blindIndex.js +57 -0
- package/dist/privacy/classes.d.ts +142 -0
- package/dist/privacy/classes.d.ts.map +1 -0
- package/dist/privacy/classes.js +209 -0
- package/dist/privacy/classify.d.ts +336 -0
- package/dist/privacy/classify.d.ts.map +1 -0
- package/dist/privacy/classify.js +1024 -0
- package/dist/privacy/encrypted.d.ts +210 -0
- package/dist/privacy/encrypted.d.ts.map +1 -0
- package/dist/privacy/encrypted.js +350 -0
- package/dist/privacy/encryptedColumns.d.ts +64 -0
- package/dist/privacy/encryptedColumns.d.ts.map +1 -0
- package/dist/privacy/encryptedColumns.js +134 -0
- package/dist/privacy/fieldRotation.d.ts +34 -0
- package/dist/privacy/fieldRotation.d.ts.map +1 -0
- package/dist/privacy/fieldRotation.js +186 -0
- package/dist/privacy/inventory.d.ts +12 -0
- package/dist/privacy/inventory.d.ts.map +1 -0
- package/dist/privacy/inventory.js +154 -0
- package/dist/privacy/posture.d.ts +114 -0
- package/dist/privacy/posture.d.ts.map +1 -0
- package/dist/privacy/posture.js +136 -0
- package/dist/privacy/redact.d.ts +7 -0
- package/dist/privacy/redact.d.ts.map +1 -0
- package/dist/privacy/redact.js +404 -0
- package/dist/privacy/report.d.ts +58 -0
- package/dist/privacy/report.d.ts.map +1 -0
- package/dist/privacy/report.js +15 -0
- package/dist/privacy/retirement.d.ts +68 -0
- package/dist/privacy/retirement.d.ts.map +1 -0
- package/dist/privacy/retirement.js +72 -0
- package/dist/privacy/sealed.d.ts +87 -0
- package/dist/privacy/sealed.d.ts.map +1 -0
- package/dist/privacy/sealed.js +221 -0
- package/dist/privacy/types.d.ts +559 -0
- package/dist/privacy/types.d.ts.map +1 -0
- package/dist/privacy/types.js +2 -0
- package/dist/seed/writeD1.d.ts +7 -1
- package/dist/seed/writeD1.d.ts.map +1 -1
- package/dist/seed/writeD1.js +9 -3
- package/dist/seed/writeKv.d.ts +2 -0
- package/dist/seed/writeKv.d.ts.map +1 -1
- package/dist/seed/writeKv.js +6 -2
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/worker/serviceLoopback.d.ts +36 -0
- package/dist/worker/serviceLoopback.d.ts.map +1 -0
- package/dist/worker/serviceLoopback.js +74 -0
- package/dist/workflow/faults.d.ts +12 -1
- package/dist/workflow/faults.d.ts.map +1 -1
- package/dist/workflow/faults.js +22 -15
- package/package.json +2 -2
- package/src/access/access.ts +298 -0
- package/src/access/acting.ts +59 -0
- package/src/access/policyCheck.ts +77 -0
- package/src/access/usage.ts +141 -0
- package/src/access/visibility.ts +109 -0
- package/src/audit/auditEvent.ts +4 -0
- package/src/audit/recorder.ts +10 -2
- package/src/capability/bindings.ts +159 -13
- package/src/capability/capability.ts +114 -29
- package/src/capability/purge.ts +124 -0
- package/src/capability/settings.ts +146 -2
- package/src/cli/commandCatalog.generated.ts +271 -22
- package/src/cli/commandFacts.ts +2 -0
- package/src/controlPlane/capability.ts +36 -1
- package/src/controlPlane/context.ts +4 -0
- package/src/controlPlane/data/connection.ts +90 -0
- package/src/controlPlane/discovery/adminRoute.ts +11 -0
- package/src/controlPlane/error/errors.ts +4 -3
- package/src/controlPlane/http/cors.ts +4 -4
- package/src/controlPlane/http/guard.ts +21 -0
- package/src/controlPlane/http/handlers.ts +98 -4
- package/src/controlPlane/http/readiness.ts +50 -0
- package/src/controlPlane/http/responses.ts +240 -2
- package/src/controlPlane/http/routes.ts +55 -1
- package/src/controlPlane/http/verify.ts +2 -0
- package/src/controlPlane/migrationState/migrationState.ts +200 -0
- package/src/controlPlane/migrations/0003_connection_principal.ts +37 -0
- package/src/controlPlane/scope/scope.ts +25 -2
- package/src/controlPlane/token/base64url.ts +1 -1
- package/src/createBackend.ts +28 -6
- package/src/createEntrypoint.ts +9 -3
- package/src/crypto/envelope.ts +196 -0
- package/src/crypto/fieldKeys.ts +76 -0
- package/src/data/codecs.ts +69 -37
- package/src/entitlement/allowance.ts +85 -0
- package/src/entitlement/entitlement.ts +38 -0
- package/src/entitlement/meter.ts +69 -0
- package/src/error/extend.ts +6 -5
- package/src/error/payload.ts +253 -4
- package/src/error/pithyError.ts +21 -7
- package/src/http/authContext.ts +13 -1
- package/src/http/scopeGates.ts +34 -0
- package/src/index.ts +2 -0
- package/src/logger/logger.ts +12 -5
- package/src/migrations/capabilitySets.ts +87 -0
- package/src/migrations/registry.ts +46 -2
- package/src/migrations/state.ts +94 -0
- package/src/naming/feature.ts +46 -7
- package/src/naming/limits.ts +35 -3
- package/src/naming/provisionScope.ts +19 -1
- package/src/naming/resourceNames.ts +52 -5
- package/src/privacy/blindIndex.ts +81 -0
- package/src/privacy/classes.ts +277 -0
- package/src/privacy/classify.ts +1447 -0
- package/src/privacy/encrypted.ts +513 -0
- package/src/privacy/encryptedColumns.ts +185 -0
- package/src/privacy/fieldRotation.ts +234 -0
- package/src/privacy/inventory.ts +147 -0
- package/src/privacy/posture.ts +205 -0
- package/src/privacy/redact.ts +423 -0
- package/src/privacy/report.ts +64 -0
- package/src/privacy/retirement.ts +102 -0
- package/src/privacy/sealed.ts +323 -0
- package/src/privacy/types.ts +804 -0
- package/src/seed/writeD1.ts +13 -4
- package/src/seed/writeKv.ts +6 -2
- package/src/version.generated.ts +1 -1
- package/src/worker/serviceLoopback.ts +105 -0
- package/src/workflow/faults.ts +51 -24
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import type { ExecutionContext, ForwardableEmailMessage } from "@cloudflare/workers-types";
|
|
4
|
-
import type { Hono } from "hono";
|
|
4
|
+
import type { Hono, MiddlewareHandler } from "hono";
|
|
5
5
|
import type { z } from "zod";
|
|
6
|
+
import type { ActingOrganizationResolver } from "../access/acting";
|
|
6
7
|
import type { AuditEmit } from "../audit/recorder";
|
|
7
8
|
import type { ControlPlaneContext } from "../controlPlane/context";
|
|
8
9
|
import type { AdminRoute } from "../controlPlane/discovery/adminRoute";
|
|
@@ -23,11 +24,25 @@ import type { SeedSet } from "../seed/seed";
|
|
|
23
24
|
import type { WorkflowSpecMap } from "../workflow/spec";
|
|
24
25
|
import { BindingSpec, type BindingSpecInput } from "./bindings";
|
|
25
26
|
import type { ClientProjection, ClientProjectionContext } from "./client";
|
|
27
|
+
import { type PurgeContribution } from "./purge";
|
|
26
28
|
import type { CapabilitySettings } from "./settings";
|
|
27
29
|
/** Hono `Variables` every capability's routes are typed against. `createBackend` seeds these per request. */
|
|
28
30
|
export interface PithyVars {
|
|
29
31
|
/** The authenticated identity, populated by `@pithy-sh/auth`; `null` until a strategy sets it. */
|
|
30
32
|
auth: AuthContext | null;
|
|
33
|
+
/**
|
|
34
|
+
* Which organization this request acts for, and what the caller's role there allows — a resolver,
|
|
35
|
+
* populated by `@pithy-sh/organization`'s middleware. With no organization capability composed it is
|
|
36
|
+
* {@link import("../access/acting").noActingOrganization}, which answers `null`: nothing is in force,
|
|
37
|
+
* every record a request writes carries a null organization, and every rule that admits by organization
|
|
38
|
+
* admits nobody.
|
|
39
|
+
*
|
|
40
|
+
* **A seam beside {@link auth}, never a field on it.** `auth` says who is signed in; this says what they
|
|
41
|
+
* may do *here*, and a capability that gates on it — media, storage — must not import the capability
|
|
42
|
+
* that computes it. Lazy, because resolving it is a D1 read most requests never need; read it with
|
|
43
|
+
* `await c.var.actingOrganization()`, or through `accessCaller` in `access/access.ts`.
|
|
44
|
+
*/
|
|
45
|
+
actingOrganization: ActingOrganizationResolver;
|
|
31
46
|
/**
|
|
32
47
|
* The resolved request locale, populated by `@pithy-sh/i18n`'s middleware; `null` in a Worker that
|
|
33
48
|
* does not compose it — which is what "nothing was negotiated" looks like, as distinct from "the
|
|
@@ -129,6 +144,11 @@ export type PithyHonoEnv = {
|
|
|
129
144
|
Variables: PithyVars;
|
|
130
145
|
};
|
|
131
146
|
export type PithyMiddleware = (app: Hono<PithyHonoEnv>) => void;
|
|
147
|
+
/**
|
|
148
|
+
* One readiness guard: a handler that refuses because **this Worker** is in a bad state. See
|
|
149
|
+
* {@link Capability.readiness} for where it runs and where it does not.
|
|
150
|
+
*/
|
|
151
|
+
export type PithyReadinessGuard = MiddlewareHandler<PithyHonoEnv>;
|
|
132
152
|
/**
|
|
133
153
|
* The structural seam for one capability's secret-registry slice — secret name → its declaration.
|
|
134
154
|
* Core carries it on the {@link Capability} contract so a capability can declare which secrets it
|
|
@@ -223,27 +243,17 @@ export type SecretBranchSeam = Record<string, readonly string[]>;
|
|
|
223
243
|
*/
|
|
224
244
|
export type InapplicableSecretSeam = Record<string, string>;
|
|
225
245
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* `TokenProfile`, the permission catalog, `defineTokenProfile`, and the mint engine live in
|
|
231
|
-
* `@pithy-sh/cloudflare`/the CLI. `pithy token` aggregates every capability's slice into one registry.
|
|
246
|
+
* One route a capability gates on an `AuthContext` scope — see {@link Capability.scopeGates}.
|
|
247
|
+
*
|
|
248
|
+
* Descriptive, never behavioral: the gate itself is the capability's own middleware, and this is what a
|
|
249
|
+
* diagnostic reads to name it. `route` is the method and the mounted path, as the route table writes them.
|
|
232
250
|
*/
|
|
233
|
-
export interface
|
|
234
|
-
/** The
|
|
235
|
-
readonly
|
|
236
|
-
/** The
|
|
237
|
-
readonly
|
|
238
|
-
/** The secret-registry name the minted value is stored under — its backend decides the destination. */
|
|
239
|
-
readonly secret?: string;
|
|
240
|
-
/** A built-in destination override (`dev-vars` | `ephemeral` | `secrets-store`) for a tooling profile. */
|
|
241
|
-
readonly defaultStore?: string;
|
|
242
|
-
/** Why the profile exists / what consumes it — surfaced in `pithy token` help and docs. */
|
|
243
|
-
readonly description?: string;
|
|
251
|
+
export interface ScopeGate {
|
|
252
|
+
/** The scope the gate requires, as the resolved config names it — `leaderboard:submit`. */
|
|
253
|
+
readonly scope: string;
|
|
254
|
+
/** The method and mounted path it guards, `POST /leaderboard/:board`. */
|
|
255
|
+
readonly route: string;
|
|
244
256
|
}
|
|
245
|
-
/** A capability's token-profile slice: profile name → {@link TokenProfileSeam}. */
|
|
246
|
-
export type TokenProfileSeamMap = Record<string, TokenProfileSeam>;
|
|
247
257
|
/**
|
|
248
258
|
* One thing an **adopter** plugged into a capability — the capability's own name for something the kit
|
|
249
259
|
* did not ship and cannot enumerate. `@pithy-sh/auth` declares one per additional Better Auth plugin
|
|
@@ -358,13 +368,6 @@ export interface Capability<Databases extends DatabaseSpecMap = DatabaseSpecMap,
|
|
|
358
368
|
* whenever it is composed declares nothing, which is the ordinary case.
|
|
359
369
|
*/
|
|
360
370
|
inapplicableSecrets?: InapplicableSecretSeam;
|
|
361
|
-
/**
|
|
362
|
-
* The scoped CF API tokens this capability's code needs, as a token-profile slice (profile name →
|
|
363
|
-
* {@link TokenProfileSeam}). Additive and optional, and declared next to {@link Capability.secretRegistry}
|
|
364
|
-
* — the value of a minted token lands in the secret the profile names. `pithy token` aggregates every
|
|
365
|
-
* capability's slice with the built-in tooling profiles into one registry.
|
|
366
|
-
*/
|
|
367
|
-
tokenProfiles?: TokenProfileSeamMap;
|
|
368
371
|
/**
|
|
369
372
|
* Extra CF permissions this capability needs the **CI system token** to hold, as short catalog keys
|
|
370
373
|
* (e.g. `email:routing` for email provisioning, `kv:write` for a module that seeds KV in CI). They
|
|
@@ -372,6 +375,20 @@ export interface Capability<Databases extends DatabaseSpecMap = DatabaseSpecMap,
|
|
|
372
375
|
* extends what CI can do without the adopter hand-editing token scopes. Additive and optional.
|
|
373
376
|
*/
|
|
374
377
|
ciPermissions?: readonly string[];
|
|
378
|
+
/**
|
|
379
|
+
* The CF permissions this capability's **deployed Workers** call Cloudflare with, as short catalog keys
|
|
380
|
+
* (`secrets:write`, `images:write`, `analytics:read`). The runtime counterpart of {@link ciPermissions}.
|
|
381
|
+
*
|
|
382
|
+
* **A deployed application holds one Cloudflare API token, and this is the only way into it (#728).**
|
|
383
|
+
* Every composed capability's keys union into the project-level `runtime` profile, minted per
|
|
384
|
+
* environment into the Secrets Store and read by every Worker of that environment through one binding,
|
|
385
|
+
* the way every capability's `ciPermissions` union into the one `ci-system` token. A capability cannot
|
|
386
|
+
* declare a token of its own. A project whose composition declares none mints no runtime token.
|
|
387
|
+
*
|
|
388
|
+
* Build it from the resolved config, as `secretRegistry` is built: a capability configured to call
|
|
389
|
+
* nothing on Cloudflare declares nothing, and adds nothing to the token. Additive and optional.
|
|
390
|
+
*/
|
|
391
|
+
runtimePermissions?: readonly string[];
|
|
375
392
|
/**
|
|
376
393
|
* The admin routes this capability contributes behind the `control-plane` strategy, described well
|
|
377
394
|
* enough for a management client to call them — full mounted path, required scope, and a one-line
|
|
@@ -518,8 +535,30 @@ export interface Capability<Databases extends DatabaseSpecMap = DatabaseSpecMap,
|
|
|
518
535
|
* parsed with. A capability with no socket leaves it unset.
|
|
519
536
|
*/
|
|
520
537
|
sockets?: readonly SocketChannel[];
|
|
521
|
-
/**
|
|
538
|
+
/**
|
|
539
|
+
* Composable middleware (e.g. turnstile(), requireAuth()). Runs on every request, the control-plane
|
|
540
|
+
* surface included. A refusal that protects a route — a rate limit, authentication, a CSRF check —
|
|
541
|
+
* belongs here. A refusal because the Worker itself is broken belongs in {@link Capability.readiness}.
|
|
542
|
+
*/
|
|
522
543
|
middleware?: PithyMiddleware[];
|
|
544
|
+
/**
|
|
545
|
+
* Guards that refuse because **this Worker is misconfigured or unprovisioned** — `@pithy-sh/vector`'s
|
|
546
|
+
* `provisionGuard`, when the deployed index disagrees with config. Not for protecting a route: a rate
|
|
547
|
+
* limit, authentication and a CSRF check refuse a *caller*, and they belong in
|
|
548
|
+
* {@link Capability.middleware}.
|
|
549
|
+
*
|
|
550
|
+
* `createBackend` mounts every readiness guard after every capability's `middleware` and before any
|
|
551
|
+
* capability's routes, so the verifier, `emit` and every rate limit are already in place. It skips
|
|
552
|
+
* each guard on the admin surface — every composed capability's `adminRoutes`, the seam's own
|
|
553
|
+
* included, the set CORS is derived from. The control plane is the diagnostic surface, so it cannot
|
|
554
|
+
* sit behind the faults it diagnoses. Every other request meets the guard, `GET /health` aside, which
|
|
555
|
+
* is registered ahead of all of it.
|
|
556
|
+
*
|
|
557
|
+
* A handler rather than a registrar like `middleware`, because core decides where it mounts. A guard
|
|
558
|
+
* that wanted its own path would be deciding the exemption by path shape, which is the thing this
|
|
559
|
+
* exists to take away from it.
|
|
560
|
+
*/
|
|
561
|
+
readiness?: PithyReadinessGuard[];
|
|
523
562
|
/**
|
|
524
563
|
* Whether this capability fills the entitlement seam — replaces `c.var.entitlements` with a real
|
|
525
564
|
* resolver. `@pithy-sh/payments` declares it; nothing else does today.
|
|
@@ -531,6 +570,27 @@ export interface Capability<Databases extends DatabaseSpecMap = DatabaseSpecMap,
|
|
|
531
570
|
* flag and say so, which turns a fleet of production 403s into one line at startup.
|
|
532
571
|
*/
|
|
533
572
|
providesEntitlements?: boolean;
|
|
573
|
+
/**
|
|
574
|
+
* Whether this capability fills `AuthContext.scopes` — whether anything will ever put a string in the
|
|
575
|
+
* array the scope gates read. `@pithy-sh/auth` declares it when an adopter supplies `resolveScopes`, and
|
|
576
|
+
* not otherwise (#560).
|
|
577
|
+
*
|
|
578
|
+
* The same reason {@link providesEntitlements} exists, one seam over. Every scope gate fails closed, so a
|
|
579
|
+
* Worker composing `leaderboard` with no resolver is not broken: it refuses every caller on the gated
|
|
580
|
+
* routes, forever, and the runtime cannot tell that from a caller who simply is not trusted. `pithy doctor`
|
|
581
|
+
* compares this flag against {@link scopeGates} and says so.
|
|
582
|
+
*/
|
|
583
|
+
providesScopes?: boolean;
|
|
584
|
+
/**
|
|
585
|
+
* The routes this capability gates on an `AuthContext` scope, built from the resolved config — a gate the
|
|
586
|
+
* config has switched off is not declared. Omit it when nothing is gated, which is the ordinary case.
|
|
587
|
+
*
|
|
588
|
+
* **Declared so the CLI can name what will deny.** With no capability composed that
|
|
589
|
+
* {@link providesScopes}, every route listed here answers 403 to every caller, and `pithy doctor` names
|
|
590
|
+
* each one. Each capability's route-contract test holds this list to the `scopes` its `describeRoute`s
|
|
591
|
+
* carry, so the declaration cannot drift from the routes.
|
|
592
|
+
*/
|
|
593
|
+
scopeGates?: readonly ScopeGate[];
|
|
534
594
|
/**
|
|
535
595
|
* This capability's client-safe projection — the **only** values of its config that may reach a
|
|
536
596
|
* browser bundle. Resolved at build time against a {@link ClientProjectionContext} (the environment),
|
|
@@ -578,6 +638,28 @@ export interface Capability<Databases extends DatabaseSpecMap = DatabaseSpecMap,
|
|
|
578
638
|
* time, and read by `pithy doctor`.
|
|
579
639
|
*/
|
|
580
640
|
extensions?: readonly CapabilityExtension[];
|
|
641
|
+
/**
|
|
642
|
+
* How this capability erases one subject's rows when that subject is purged — see `./purge.ts`. Omit it
|
|
643
|
+
* when this capability holds nothing keyed to a person, which is the ordinary case.
|
|
644
|
+
*
|
|
645
|
+
* **The purge Workflow runs each composed contribution as its own step**, named for this capability, so a
|
|
646
|
+
* failure retries this capability alone and the steps that finished are not re-run. That is why there is
|
|
647
|
+
* at most one per subject here, and why `erase` must succeed when there is nothing left to erase.
|
|
648
|
+
* `defineCapability` refuses a contribution for a subject the kit does not know. An adopter declares one
|
|
649
|
+
* on their own `app` capability for their own tables, with the same types.
|
|
650
|
+
*/
|
|
651
|
+
purge?: readonly PurgeContribution[];
|
|
652
|
+
/**
|
|
653
|
+
* The role powers this capability's own routes gate on, through the acting-organization seam —
|
|
654
|
+
* `media:manage`, `storage:manage`. Omit it when a capability asks no power, which is the normal case.
|
|
655
|
+
*
|
|
656
|
+
* **Declared so the catalog requirement can be conditional.** `@pithy-sh/organization` refuses a role
|
|
657
|
+
* catalog in which no role holds a power a composed capability gates on — a route nobody could ever use
|
|
658
|
+
* — and a project that composes no media must not be refused for not placing `media:manage`. The
|
|
659
|
+
* organization capability reads this list at compose, across every composed capability, rather than
|
|
660
|
+
* importing any of them.
|
|
661
|
+
*/
|
|
662
|
+
powers?: readonly string[];
|
|
581
663
|
/** Bindings this capability needs in the env (normalized — `optional` is always set). */
|
|
582
664
|
requiredBindings: BindingSpec[];
|
|
583
665
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/capability/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/capability/capability.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { BindingSpec } from "./bindings.js";
|
|
4
4
|
import { declareRetained } from "../migrations/retainedDeclaration.js";
|
|
5
|
+
import { assertPurgeContributions } from "./purge.js";
|
|
5
6
|
//#region src/capability/capability.ts
|
|
6
7
|
/**
|
|
7
8
|
* Author a capability. Parses each binding through `BindingSpec` — normalizing `optional`
|
|
@@ -11,6 +12,7 @@ import { declareRetained } from "../migrations/retainedDeclaration.js";
|
|
|
11
12
|
*/
|
|
12
13
|
function defineCapability(input) {
|
|
13
14
|
for (const spec of Object.values(input.databases ?? {})) if (spec.migrations && spec.retained) declareRetained(spec.migrations, spec.retained);
|
|
15
|
+
if (input.purge) assertPurgeContributions(input.name, input.purge);
|
|
14
16
|
return {
|
|
15
17
|
...input,
|
|
16
18
|
requiredBindings: input.requiredBindings.map((binding) => BindingSpec.parse(binding))
|
|
@@ -295,13 +295,14 @@ export declare const CapabilityManifest: z.ZodObject<{
|
|
|
295
295
|
name: z.ZodString;
|
|
296
296
|
package: z.ZodString;
|
|
297
297
|
requiredBindings: z.ZodArray<z.ZodObject<{
|
|
298
|
-
type: z.ZodUnion<readonly [z.ZodLiteral<"d1">, z.ZodLiteral<"kv">, z.ZodLiteral<"r2">, z.ZodLiteral<"ai">, z.ZodLiteral<"vectorize">, z.ZodLiteral<"queue">, z.ZodLiteral<"ratelimit">, z.ZodLiteral<"email">, z.ZodLiteral<"secret">, z.ZodLiteral<"workflow">, z.ZodLiteral<"service">, z.ZodLiteral<"durable_object">]>;
|
|
298
|
+
type: z.ZodUnion<readonly [z.ZodLiteral<"d1">, z.ZodLiteral<"kv">, z.ZodLiteral<"r2">, z.ZodLiteral<"ai">, z.ZodLiteral<"vectorize">, z.ZodLiteral<"queue">, z.ZodLiteral<"ratelimit">, z.ZodLiteral<"email">, z.ZodLiteral<"secret">, z.ZodLiteral<"workflow">, z.ZodLiteral<"service">, z.ZodLiteral<"durable_object">, z.ZodLiteral<"hyperdrive">, z.ZodLiteral<"browser">, z.ZodLiteral<"images">, z.ZodLiteral<"analytics_engine_datasets">, z.ZodLiteral<"pipelines">, z.ZodLiteral<"mtls_certificates">, z.ZodLiteral<"dispatch_namespaces">, z.ZodLiteral<"artifacts">]>;
|
|
299
299
|
name: z.ZodString;
|
|
300
300
|
optional: z.ZodDefault<z.ZodBoolean>;
|
|
301
301
|
className: z.ZodOptional<z.ZodString>;
|
|
302
302
|
classModule: z.ZodOptional<z.ZodString>;
|
|
303
303
|
job: z.ZodOptional<z.ZodString>;
|
|
304
304
|
service: z.ZodOptional<z.ZodString>;
|
|
305
|
+
target: z.ZodOptional<z.ZodString>;
|
|
305
306
|
remote: z.ZodOptional<z.ZodBoolean>;
|
|
306
307
|
scope: z.ZodOptional<z.ZodEnum<{
|
|
307
308
|
environment: "environment";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/capability/manifest.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/capability/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { ValidationError } from "../error/pithyError.js";
|
|
4
4
|
import { BindingSpec } from "./bindings.js";
|
|
5
|
+
import { NAMESPACE_PATTERN } from "../migrations/registry.js";
|
|
5
6
|
import { manifestRecord } from "./vanishingKey.js";
|
|
6
7
|
import { DevSecret } from "./devSecret.js";
|
|
7
|
-
import { NAMESPACE_PATTERN } from "../migrations/registry.js";
|
|
8
8
|
import { DeclaredSecret } from "./secretOrigin.js";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
//#region src/capability/manifest.ts
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
/**
|
|
5
|
+
* How a capability says what erasing one subject means for its own rows — the purge contribution.
|
|
6
|
+
*
|
|
7
|
+
* ## Why a contribution, and not a cascade
|
|
8
|
+
*
|
|
9
|
+
* A hard delete has to reach rows that `@pithy-sh/media`, `@pithy-sh/support`, `@pithy-sh/testers` and the
|
|
10
|
+
* adopter's own tables hold, and no foreign key reaches across capabilities: each owns its tables, and none
|
|
11
|
+
* references another's. So the only party that knows how one person's rows are erased — deleted outright,
|
|
12
|
+
* or anonymized because a ledger must still add up — is the capability that owns them. It says so here,
|
|
13
|
+
* the way it states its migrations and its seeds, and the purge runs what was composed.
|
|
14
|
+
*
|
|
15
|
+
* ## One capability, one step
|
|
16
|
+
*
|
|
17
|
+
* The purge Workflow (#622) runs every composed contribution as **its own step**, named for the
|
|
18
|
+
* capability. A step that fails retries alone, and the ones that finished are not re-run — which is why a
|
|
19
|
+
* capability may declare at most one contribution per subject, and why every `erase` must be idempotent:
|
|
20
|
+
* a step is retried after a failure that may have come after its rows were already gone, and erasing
|
|
21
|
+
* nothing must then succeed.
|
|
22
|
+
*
|
|
23
|
+
* ## An adopter declares one too
|
|
24
|
+
*
|
|
25
|
+
* On their own `app` capability, for their own tables, with the same types. The contract is exported for
|
|
26
|
+
* exactly that, and nothing distinguishes a kit contribution from theirs.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* The kinds of subject a purge erases. Closed, so a typo is refused where it is written rather than becoming
|
|
30
|
+
* a contribution no purge ever asks for. `organization` joins when organization state does (#623).
|
|
31
|
+
*/
|
|
32
|
+
export declare const PurgeSubject: z.ZodEnum<{
|
|
33
|
+
user: "user";
|
|
34
|
+
}>;
|
|
35
|
+
export type PurgeSubject = z.output<typeof PurgeSubject>;
|
|
36
|
+
/** What one step is handed: who is being erased, and this run's bindings. */
|
|
37
|
+
export interface PurgeContext {
|
|
38
|
+
/** The subject being erased — its kind, and its id in that kind's own table. */
|
|
39
|
+
readonly subject: {
|
|
40
|
+
readonly type: PurgeSubject;
|
|
41
|
+
readonly id: string;
|
|
42
|
+
};
|
|
43
|
+
/** The Worker's bindings for this run, from which the contribution opens its own database. */
|
|
44
|
+
readonly env: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
/** One capability's answer to "how are this subject's rows erased". */
|
|
47
|
+
export interface PurgeContribution {
|
|
48
|
+
/** The kind of subject this erases. */
|
|
49
|
+
readonly subject: PurgeSubject;
|
|
50
|
+
/**
|
|
51
|
+
* One sentence on what is erased or anonymized — `Deletes the user's uploads and their stored bytes.`
|
|
52
|
+
* Recorded with the step that ran it, so the trail says what a purge did and not only that it ran.
|
|
53
|
+
*/
|
|
54
|
+
readonly summary: string;
|
|
55
|
+
/**
|
|
56
|
+
* Erase or anonymize this subject's rows. **Idempotent**: run again after it succeeded, or after a failure
|
|
57
|
+
* that came once the rows were gone, it must succeed having found nothing to do. Throwing fails this step
|
|
58
|
+
* and only this step.
|
|
59
|
+
*/
|
|
60
|
+
readonly erase: (context: PurgeContext) => Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/** One step of a purge: which capability it runs, and the contribution that capability declared. */
|
|
63
|
+
export interface PurgeStep {
|
|
64
|
+
/** The step's name — the capability's, so a retried step names who failed. */
|
|
65
|
+
readonly step: string;
|
|
66
|
+
/** The contribution this step runs. */
|
|
67
|
+
readonly contribution: PurgeContribution;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Refuse a capability's purge contributions that no purge could run as declared: an unknown subject, two for
|
|
71
|
+
* one subject, or one with nothing to record. Called by `defineCapability`, so the refusal names the capability
|
|
72
|
+
* where it is written rather than surfacing in a Workflow on the day somebody is deleted.
|
|
73
|
+
*/
|
|
74
|
+
export declare function assertPurgeContributions(capability: string, contributions: readonly PurgeContribution[]): void;
|
|
75
|
+
/** The steps a purge of `subject` runs, in composition order — libraries first, the adopter's `app` last. */
|
|
76
|
+
export declare function purgeSteps(capabilities: readonly {
|
|
77
|
+
readonly name: string;
|
|
78
|
+
readonly purge?: readonly PurgeContribution[];
|
|
79
|
+
}[], subject: PurgeSubject): PurgeStep[];
|
|
80
|
+
//# sourceMappingURL=purge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purge.d.ts","sourceRoot":"","sources":["../../src/capability/purge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY;;EAItB,CAAC;AACJ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAEzD,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,8FAA8F;IAC9F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,oGAAoG;AACpG,MAAM,WAAW,SAAS;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,iBAAiB,EAAE,GAAG,IAAI,CA8B9G;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,CACxB,YAAY,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,EAAE,EACjG,OAAO,EAAE,YAAY,GACpB,SAAS,EAAE,CAOb"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { ValidationError } from "../error/pithyError.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/capability/purge.ts
|
|
6
|
+
/**
|
|
7
|
+
* How a capability says what erasing one subject means for its own rows — the purge contribution.
|
|
8
|
+
*
|
|
9
|
+
* ## Why a contribution, and not a cascade
|
|
10
|
+
*
|
|
11
|
+
* A hard delete has to reach rows that `@pithy-sh/media`, `@pithy-sh/support`, `@pithy-sh/testers` and the
|
|
12
|
+
* adopter's own tables hold, and no foreign key reaches across capabilities: each owns its tables, and none
|
|
13
|
+
* references another's. So the only party that knows how one person's rows are erased — deleted outright,
|
|
14
|
+
* or anonymized because a ledger must still add up — is the capability that owns them. It says so here,
|
|
15
|
+
* the way it states its migrations and its seeds, and the purge runs what was composed.
|
|
16
|
+
*
|
|
17
|
+
* ## One capability, one step
|
|
18
|
+
*
|
|
19
|
+
* The purge Workflow (#622) runs every composed contribution as **its own step**, named for the
|
|
20
|
+
* capability. A step that fails retries alone, and the ones that finished are not re-run — which is why a
|
|
21
|
+
* capability may declare at most one contribution per subject, and why every `erase` must be idempotent:
|
|
22
|
+
* a step is retried after a failure that may have come after its rows were already gone, and erasing
|
|
23
|
+
* nothing must then succeed.
|
|
24
|
+
*
|
|
25
|
+
* ## An adopter declares one too
|
|
26
|
+
*
|
|
27
|
+
* On their own `app` capability, for their own tables, with the same types. The contract is exported for
|
|
28
|
+
* exactly that, and nothing distinguishes a kit contribution from theirs.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* The kinds of subject a purge erases. Closed, so a typo is refused where it is written rather than becoming
|
|
32
|
+
* a contribution no purge ever asks for. `organization` joins when organization state does (#623).
|
|
33
|
+
*/
|
|
34
|
+
const PurgeSubject = z.enum(["user"]).describe("The kind of subject a purge erases. `user` is one person's account in `@pithy-sh/auth`, identified by its user id.");
|
|
35
|
+
/**
|
|
36
|
+
* Refuse a capability's purge contributions that no purge could run as declared: an unknown subject, two for
|
|
37
|
+
* one subject, or one with nothing to record. Called by `defineCapability`, so the refusal names the capability
|
|
38
|
+
* where it is written rather than surfacing in a Workflow on the day somebody is deleted.
|
|
39
|
+
*/
|
|
40
|
+
function assertPurgeContributions(capability, contributions) {
|
|
41
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42
|
+
for (const contribution of contributions) {
|
|
43
|
+
const subject = PurgeSubject.safeParse(contribution.subject);
|
|
44
|
+
if (!subject.success) throw new ValidationError({
|
|
45
|
+
message: `The ${capability} capability declares a purge for "${String(contribution.subject)}", which is not a subject a purge erases.`,
|
|
46
|
+
action: `Declare it for one of: ${PurgeSubject.options.join(", ")}.`
|
|
47
|
+
});
|
|
48
|
+
if (seen.has(subject.data)) throw new ValidationError({
|
|
49
|
+
message: `The ${capability} capability declares two purge contributions for ${subject.data}.`,
|
|
50
|
+
action: "Fold them into one: each capability is one step of the purge, and a retried step re-runs all of it."
|
|
51
|
+
});
|
|
52
|
+
seen.add(subject.data);
|
|
53
|
+
if (contribution.summary.trim() === "") throw new ValidationError({
|
|
54
|
+
message: `The ${capability} capability's ${subject.data} purge has no summary.`,
|
|
55
|
+
action: "Say in one sentence what it erases. The purge records it with the step."
|
|
56
|
+
});
|
|
57
|
+
if (typeof contribution.erase !== "function") throw new ValidationError({
|
|
58
|
+
message: `The ${capability} capability's ${subject.data} purge has no erase function.`,
|
|
59
|
+
action: "Give it an `erase` that removes or anonymizes the subject's rows."
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** The steps a purge of `subject` runs, in composition order — libraries first, the adopter's `app` last. */
|
|
64
|
+
function purgeSteps(capabilities, subject) {
|
|
65
|
+
const steps = [];
|
|
66
|
+
for (const capability of capabilities) {
|
|
67
|
+
const contribution = capability.purge?.find((candidate) => candidate.subject === subject);
|
|
68
|
+
if (contribution) steps.push({
|
|
69
|
+
step: capability.name,
|
|
70
|
+
contribution
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return steps;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { PurgeSubject, assertPurgeContributions, purgeSteps };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { InternalError } from "../error/pithyError";
|
|
4
5
|
import type { HostEnvReport } from "../workflow/hostEnv";
|
|
5
6
|
/**
|
|
6
7
|
* How a capability checks that its own settings **work** — the seam `pithy doctor` runs (#411).
|
|
@@ -81,8 +82,8 @@ export interface SettingsCheckContext {
|
|
|
81
82
|
*
|
|
82
83
|
* Closed for the reason `CapabilityHealth` is: handing a capability a raw Cloudflare client would make
|
|
83
84
|
* every capability a place a network call can be invented, and would put `@pithy-sh/cloudflare` in the
|
|
84
|
-
* dependency list of packages that need none of it.
|
|
85
|
-
*
|
|
85
|
+
* dependency list of packages that need none of it. A question lands here once a capability actually needs
|
|
86
|
+
* it, and not before: six cover what the checks ask.
|
|
86
87
|
*
|
|
87
88
|
* Every method may throw. A throw is the account failing to answer, and the runner reports that
|
|
88
89
|
* capability's account tier as unchecked rather than as a pass.
|
|
@@ -110,12 +111,84 @@ export interface SettingsAccountReader {
|
|
|
110
111
|
* created it with, so the check looks where the value went.
|
|
111
112
|
*/
|
|
112
113
|
storeEntry(name: string): Promise<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* The ids of the rows in one of the kit's own tables whose column holds one of `values`, in the app
|
|
116
|
+
* database a Worker binds as `DB` in one deployed environment (#727).
|
|
117
|
+
*
|
|
118
|
+
* Asked by media and storage after a project narrows `visibility.allowed`: narrowing never rewrites a
|
|
119
|
+
* record, so the records left outside the policy have to be *named*, and only the deployed database
|
|
120
|
+
* holds them. Closed in its own way — the table must be a `pithy_` one and the column an identifier,
|
|
121
|
+
* both refused otherwise, and the values are bound, never interpolated — so it reads a fact about a
|
|
122
|
+
* capability's own rows and cannot become a query language.
|
|
123
|
+
*/
|
|
124
|
+
appRows(request: AppRowsRequest): Promise<readonly string[]>;
|
|
125
|
+
/**
|
|
126
|
+
* Whether that environment's **D1 vault** holds a `json` secret that still carries a top-level `field` —
|
|
127
|
+
* asked of its manager Worker, which opens the value and answers a bit, never the value (#728). `false`
|
|
128
|
+
* for a secret that is not stored, since it carries nothing.
|
|
129
|
+
*
|
|
130
|
+
* Asked by media of `media-storage-credentials`, which held a supplied Cloudflare token until the runtime
|
|
131
|
+
* token replaced it: the upgrade is finished only once the bundle has been rewritten without it.
|
|
132
|
+
*/
|
|
133
|
+
vaultSecretField(request: {
|
|
134
|
+
name: string;
|
|
135
|
+
environment: string;
|
|
136
|
+
field: string;
|
|
137
|
+
}): Promise<boolean>;
|
|
138
|
+
/**
|
|
139
|
+
* Whether the account holds a Cloudflare API token of exactly this name (#728). The name is the token's,
|
|
140
|
+
* composed with the namer that minted it — never a pattern, so a check names one credential it can see.
|
|
141
|
+
*
|
|
142
|
+
* Asked by secrets of the manager token the runtime token superseded, which nothing revokes for you.
|
|
143
|
+
*/
|
|
144
|
+
accountToken(name: string): Promise<boolean>;
|
|
145
|
+
}
|
|
146
|
+
/** What {@link SettingsAccountReader.appRows} is asked. */
|
|
147
|
+
export interface AppRowsRequest {
|
|
148
|
+
/** The deployed environment whose app database is read. `dev` is refused: it has no deployed database. */
|
|
149
|
+
readonly environment: string;
|
|
150
|
+
/** The Worker's `apps/` directory — whose `wrangler.jsonc` names the database for that environment. */
|
|
151
|
+
readonly workerDirectory: string;
|
|
152
|
+
/** The kit table, as it is named in SQL — `pithy_media_assets`. Must carry the `pithy_` prefix. */
|
|
153
|
+
readonly table: string;
|
|
154
|
+
/** The column compared, as it is named in SQL — `visibility`. */
|
|
155
|
+
readonly column: string;
|
|
156
|
+
/** The values that make a row worth naming. */
|
|
157
|
+
readonly values: readonly string[];
|
|
113
158
|
}
|
|
114
159
|
/** What the account tier is told: everything the local tier gets, plus the account it may ask. */
|
|
115
160
|
export interface SettingsAccountContext extends SettingsCheckContext {
|
|
116
161
|
/** The account, as the closed set of questions above. */
|
|
117
162
|
account: SettingsAccountReader;
|
|
118
163
|
}
|
|
164
|
+
/** One environment an account check could not answer for, and why. */
|
|
165
|
+
export interface UnansweredEnvironment {
|
|
166
|
+
/** The environment id. */
|
|
167
|
+
readonly environment: string;
|
|
168
|
+
/** What the account, or the project, said instead of an answer. */
|
|
169
|
+
readonly cause: unknown;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* An account check that answered for some environments and not for others.
|
|
173
|
+
*
|
|
174
|
+
* A check that asks per environment must not let one unanswerable environment cost the others their
|
|
175
|
+
* findings: staging stood up before prod is the ordinary bring-up, and a check that threw on prod's missing
|
|
176
|
+
* database would throw away what it had already found in staging. So it collects what it can, and throws
|
|
177
|
+
* this at the end carrying those findings. The runner reports them as findings and the tier as unchecked,
|
|
178
|
+
* both — a fault and an unanswered question are two facts, and neither hides the other.
|
|
179
|
+
*/
|
|
180
|
+
export declare class SettingsAccountIncomplete extends InternalError {
|
|
181
|
+
/** What the environments that did answer were found to have wrong. */
|
|
182
|
+
readonly findings: readonly SettingsFinding[];
|
|
183
|
+
/** The environments that did not answer, in the order they were asked. */
|
|
184
|
+
readonly unanswered: readonly string[];
|
|
185
|
+
constructor(findings: readonly SettingsFinding[], unanswered: readonly UnansweredEnvironment[]);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The findings an account check reached before it threw — none for an ordinary throw, and what it had
|
|
189
|
+
* collected for a {@link SettingsAccountIncomplete}.
|
|
190
|
+
*/
|
|
191
|
+
export declare function settingsFindingsBefore(error: unknown): readonly SettingsFinding[];
|
|
119
192
|
/**
|
|
120
193
|
* A capability's settings check, as it hangs off the capability.
|
|
121
194
|
*
|
|
@@ -129,6 +202,17 @@ export interface CapabilitySettings {
|
|
|
129
202
|
/** The half that costs a Cloudflare call. Omitted where a capability has nothing to ask the account. */
|
|
130
203
|
account?(context: SettingsAccountContext): Promise<SettingsFinding[]>;
|
|
131
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* **Two or more checks on one capability, as the one `settings` it declares (#728).** Media names records a
|
|
207
|
+
* narrowed visibility no longer admits and a bundle still holding a superseded token: two questions, one
|
|
208
|
+
* capability, one seam.
|
|
209
|
+
*
|
|
210
|
+
* Each tier runs every check's, in declaration order. A check that could not answer for some environments
|
|
211
|
+
* costs those environments and nothing else: the others' findings are kept, and the run ends in one
|
|
212
|
+
* {@link SettingsAccountIncomplete} carrying every finding and every unanswered environment. A check that
|
|
213
|
+
* throws outright is unanswered for every environment the context declares.
|
|
214
|
+
*/
|
|
215
|
+
export declare function allSettings(...checks: readonly CapabilitySettings[]): CapabilitySettings;
|
|
132
216
|
/**
|
|
133
217
|
* Turn a host env's own parse report into findings — the bridge that makes "one schema, two readers" real.
|
|
134
218
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/capability/settings.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,mFAAmF;AACnF,eAAO,MAAM,eAAe;;;;;;iBAiC+D,CAAC;AAC5F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,6FAA6F;AAC7F,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,4GAA4G;AAC5G,MAAM,WAAW,oBAAoB;IACnC,gGAAgG;IAChG,OAAO,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC9C;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,WAAW,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC1C,+GAA+G;IAC/G,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9E;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/capability/settings.ts"],"names":[],"mappings":"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"}
|