@pithy-sh/core 0.9.0 → 0.11.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/asyncapi/document.d.ts +46 -0
- package/dist/asyncapi/document.d.ts.map +1 -0
- package/dist/asyncapi/document.js +115 -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/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 +105 -28
- package/dist/capability/capability.d.ts.map +1 -1
- package/dist/capability/composition.d.ts.map +1 -1
- package/dist/capability/composition.js +5 -1
- 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/settings.d.ts +96 -4
- package/dist/capability/settings.d.ts.map +1 -1
- package/dist/capability/settings.js +85 -4
- package/dist/cli/command.d.ts +364 -0
- package/dist/cli/command.d.ts.map +1 -0
- package/dist/cli/command.js +418 -0
- package/dist/cli/commandCatalog.generated.d.ts +2437 -0
- package/dist/cli/commandCatalog.generated.d.ts.map +1 -0
- package/dist/cli/commandCatalog.generated.js +2564 -0
- package/dist/cli/commandFacts.d.ts +164 -0
- package/dist/cli/commandFacts.d.ts.map +1 -0
- package/dist/cli/commandFacts.js +216 -0
- package/dist/controlPlane/capability.d.ts.map +1 -1
- package/dist/controlPlane/capability.js +12 -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 +47 -0
- package/dist/controlPlane/data/connection.d.ts.map +1 -1
- package/dist/controlPlane/data/connection.js +55 -1
- package/dist/controlPlane/error/errors.d.ts.map +1 -1
- package/dist/controlPlane/error/errors.js +4 -3
- 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 +28 -2
- package/dist/controlPlane/http/handlers.d.ts +60 -0
- package/dist/controlPlane/http/handlers.d.ts.map +1 -1
- package/dist/controlPlane/http/handlers.js +31 -2
- 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 +147 -0
- package/dist/controlPlane/http/responses.d.ts.map +1 -1
- package/dist/controlPlane/http/responses.js +61 -4
- package/dist/controlPlane/http/routes.d.ts +2 -1
- package/dist/controlPlane/http/routes.d.ts.map +1 -1
- package/dist/controlPlane/http/routes.js +50 -9
- 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 +13 -2
- package/dist/controlPlane/scope/scope.d.ts.map +1 -1
- package/dist/controlPlane/scope/scope.js +14 -3
- package/dist/createBackend.d.ts +7 -0
- package/dist/createBackend.d.ts.map +1 -1
- package/dist/createBackend.js +41 -6
- package/dist/createEntrypoint.d.ts.map +1 -1
- package/dist/createEntrypoint.js +5 -3
- 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/entitlement/require.js +1 -1
- package/dist/error/cause.d.ts +16 -116
- package/dist/error/cause.d.ts.map +1 -1
- package/dist/error/cause.js +19 -153
- package/dist/error/http.d.ts +184 -0
- package/dist/error/http.d.ts.map +1 -1
- package/dist/error/payload.d.ts +368 -0
- package/dist/error/payload.d.ts.map +1 -1
- package/dist/error/payload.js +126 -4
- package/dist/error/pithyError.d.ts +44 -0
- package/dist/error/pithyError.d.ts.map +1 -1
- package/dist/error/pithyError.js +56 -1
- package/dist/error/unresolved.d.ts +265 -0
- package/dist/error/unresolved.d.ts.map +1 -0
- package/dist/error/unresolved.js +599 -0
- package/dist/error/wire.d.ts +20 -0
- package/dist/error/wire.d.ts.map +1 -0
- package/dist/error/wire.js +19 -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/oidcWebhook.d.ts +3 -3
- package/dist/http/oidcWebhook.js +3 -3
- package/dist/http/routeContract.js +1 -1
- package/dist/http/routeSpec.d.ts +66 -0
- package/dist/http/routeSpec.d.ts.map +1 -0
- package/dist/http/routeSpec.js +40 -0
- package/dist/http/sameOrigin.d.ts +1 -1
- package/dist/http/sameOrigin.js +1 -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/http/signedWebhook.d.ts +3 -3
- package/dist/http/signedWebhook.js +3 -3
- package/dist/http/socket.d.ts +52 -0
- package/dist/http/socket.d.ts.map +1 -0
- package/dist/http/socket.js +2 -0
- package/dist/http/subprotocol.d.ts +29 -0
- package/dist/http/subprotocol.d.ts.map +1 -0
- package/dist/http/subprotocol.js +37 -0
- package/dist/http/validation.d.ts +40 -8
- package/dist/http/validation.d.ts.map +1 -1
- package/dist/http/validation.js +25 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/logger/record.d.ts +107 -0
- package/dist/logger/record.d.ts.map +1 -1
- package/dist/migrations/bookkeeping.d.ts +21 -0
- package/dist/migrations/bookkeeping.d.ts.map +1 -1
- package/dist/migrations/bookkeeping.js +32 -3
- 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/groups.d.ts +140 -0
- package/dist/migrations/groups.d.ts.map +1 -0
- package/dist/migrations/groups.js +191 -0
- package/dist/migrations/migrateStep.d.ts +13 -0
- package/dist/migrations/migrateStep.d.ts.map +1 -0
- package/dist/migrations/migrateStep.js +20 -0
- package/dist/migrations/owner.d.ts +7 -0
- package/dist/migrations/owner.d.ts.map +1 -1
- package/dist/migrations/owner.js +11 -7
- 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/retained.js +2 -2
- package/dist/migrations/runner.d.ts +76 -5
- package/dist/migrations/runner.d.ts.map +1 -1
- package/dist/migrations/runner.js +130 -19
- 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/domains.d.ts +17 -0
- package/dist/naming/domains.d.ts.map +1 -1
- package/dist/naming/domains.js +62 -4
- package/dist/naming/environment.d.ts.map +1 -1
- package/dist/naming/environment.js +2 -1
- 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/packageName.d.ts +17 -0
- package/dist/naming/packageName.d.ts.map +1 -0
- package/dist/naming/packageName.js +22 -0
- package/dist/naming/provisionScope.d.ts +6 -0
- package/dist/naming/provisionScope.d.ts.map +1 -1
- package/dist/naming/provisionScope.js +10 -4
- 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/openapi/document.d.ts +106 -0
- package/dist/openapi/document.d.ts.map +1 -0
- package/dist/openapi/document.js +314 -0
- package/dist/openapi/routeGroups.d.ts +8 -0
- package/dist/openapi/routeGroups.d.ts.map +1 -0
- package/dist/openapi/routeGroups.js +23 -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/text/comments.js +66 -4
- package/dist/text/literals.d.ts +43 -0
- package/dist/text/literals.d.ts.map +1 -0
- package/dist/text/literals.js +293 -0
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.d.ts.map +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/dispatchRoute.d.ts.map +1 -1
- package/dist/workflow/dispatchRoute.js +12 -6
- package/dist/workflow/faults.d.ts +12 -1
- package/dist/workflow/faults.d.ts.map +1 -1
- package/dist/workflow/faults.js +24 -18
- package/dist/workflow/hostEnv.d.ts +1 -0
- package/dist/workflow/hostEnv.d.ts.map +1 -1
- package/dist/workflow/hostEnv.js +4 -2
- package/dist/workflow/loopback.d.ts.map +1 -1
- package/dist/workflow/loopback.js +2 -1
- package/package.json +4 -4
- 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/asyncapi/document.ts +164 -0
- package/src/audit/auditEvent.ts +4 -0
- package/src/capability/bindings.ts +159 -13
- package/src/capability/capability.ts +106 -29
- package/src/capability/composition.ts +2 -1
- package/src/capability/settings.ts +165 -4
- package/src/cli/command.ts +865 -0
- package/src/cli/commandCatalog.generated.ts +2723 -0
- package/src/cli/commandFacts.ts +220 -0
- package/src/controlPlane/capability.ts +25 -1
- package/src/controlPlane/context.ts +4 -0
- package/src/controlPlane/data/connection.ts +90 -0
- package/src/controlPlane/error/errors.ts +10 -3
- package/src/controlPlane/http/cors.ts +4 -4
- package/src/controlPlane/http/guard.ts +35 -2
- package/src/controlPlane/http/handlers.ts +41 -0
- package/src/controlPlane/http/readiness.ts +50 -0
- package/src/controlPlane/http/responses.ts +140 -2
- package/src/controlPlane/http/routes.ts +82 -10
- 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 +14 -2
- package/src/createBackend.ts +59 -8
- package/src/createEntrypoint.ts +9 -3
- package/src/entitlement/allowance.ts +85 -0
- package/src/entitlement/entitlement.ts +38 -0
- package/src/entitlement/meter.ts +69 -0
- package/src/entitlement/require.ts +1 -1
- package/src/error/cause.ts +18 -181
- package/src/error/payload.ts +270 -4
- package/src/error/pithyError.ts +76 -0
- package/src/error/unresolved.ts +853 -0
- package/src/error/wire.ts +40 -0
- package/src/http/authContext.ts +13 -1
- package/src/http/oidcWebhook.ts +3 -3
- package/src/http/routeContract.ts +1 -1
- package/src/http/routeSpec.ts +81 -0
- package/src/http/sameOrigin.ts +1 -1
- package/src/http/scopeGates.ts +34 -0
- package/src/http/signedWebhook.ts +3 -3
- package/src/http/socket.ts +55 -0
- package/src/http/subprotocol.ts +47 -0
- package/src/http/validation.ts +50 -8
- package/src/index.ts +1 -0
- package/src/migrations/bookkeeping.ts +39 -2
- package/src/migrations/capabilitySets.ts +87 -0
- package/src/migrations/groups.ts +295 -0
- package/src/migrations/migrateStep.ts +19 -0
- package/src/migrations/owner.ts +10 -6
- package/src/migrations/registry.ts +46 -2
- package/src/migrations/retained.ts +2 -2
- package/src/migrations/runner.ts +214 -22
- package/src/migrations/state.ts +94 -0
- package/src/naming/domains.ts +75 -16
- package/src/naming/environment.ts +2 -1
- package/src/naming/feature.ts +46 -7
- package/src/naming/limits.ts +35 -3
- package/src/naming/packageName.ts +20 -0
- package/src/naming/provisionScope.ts +21 -2
- package/src/naming/resourceNames.ts +52 -5
- package/src/openapi/document.ts +412 -0
- package/src/openapi/routeGroups.ts +25 -0
- package/src/seed/writeD1.ts +13 -4
- package/src/seed/writeKv.ts +6 -2
- package/src/text/comments.ts +56 -4
- package/src/text/literals.ts +294 -0
- package/src/version.generated.ts +1 -1
- package/src/worker/serviceLoopback.ts +105 -0
- package/src/workflow/dispatchRoute.ts +13 -6
- package/src/workflow/faults.ts +56 -27
- package/src/workflow/hostEnv.ts +9 -1
- package/src/workflow/loopback.ts +2 -1
|
@@ -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).
|
|
@@ -45,6 +46,7 @@ export declare const SettingsFinding: z.ZodObject<{
|
|
|
45
46
|
environment: z.ZodNullable<z.ZodString>;
|
|
46
47
|
problem: z.ZodString;
|
|
47
48
|
action: z.ZodString;
|
|
49
|
+
fix: z.ZodOptional<z.ZodString>;
|
|
48
50
|
}, z.core.$strip>;
|
|
49
51
|
export type SettingsFinding = z.infer<typeof SettingsFinding>;
|
|
50
52
|
/** One environment the project declares, and what this Worker's own config says about it. */
|
|
@@ -63,8 +65,15 @@ export interface SettingsEnvironment {
|
|
|
63
65
|
export interface SettingsCheckContext {
|
|
64
66
|
/** The root config's `name` — the leading segment of every resource this project provisions. */
|
|
65
67
|
project: string;
|
|
66
|
-
/** The Worker whose `pithy.config.ts` composed this capability. */
|
|
68
|
+
/** The Worker whose `pithy.config.ts` composed this capability, by its `wrangler.jsonc` name. */
|
|
67
69
|
worker: string;
|
|
70
|
+
/**
|
|
71
|
+
* That Worker's directory under `apps/` — the value `--worker` takes, and the one a refusal lists (#711).
|
|
72
|
+
*
|
|
73
|
+
* Not `worker` under another name. A scaffold deploys `apps/api` as `acme-api`, so a command a finding names
|
|
74
|
+
* for one Worker is written with this.
|
|
75
|
+
*/
|
|
76
|
+
workerDirectory: string;
|
|
68
77
|
/** Every environment the project declares, in declaration order. */
|
|
69
78
|
environments: readonly SettingsEnvironment[];
|
|
70
79
|
}
|
|
@@ -73,8 +82,8 @@ export interface SettingsCheckContext {
|
|
|
73
82
|
*
|
|
74
83
|
* Closed for the reason `CapabilityHealth` is: handing a capability a raw Cloudflare client would make
|
|
75
84
|
* every capability a place a network call can be invented, and would put `@pithy-sh/cloudflare` in the
|
|
76
|
-
* dependency list of packages that need none of it.
|
|
77
|
-
*
|
|
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.
|
|
78
87
|
*
|
|
79
88
|
* Every method may throw. A throw is the account failing to answer, and the runner reports that
|
|
80
89
|
* capability's account tier as unchecked rather than as a pass.
|
|
@@ -102,12 +111,84 @@ export interface SettingsAccountReader {
|
|
|
102
111
|
* created it with, so the check looks where the value went.
|
|
103
112
|
*/
|
|
104
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[];
|
|
105
158
|
}
|
|
106
159
|
/** What the account tier is told: everything the local tier gets, plus the account it may ask. */
|
|
107
160
|
export interface SettingsAccountContext extends SettingsCheckContext {
|
|
108
161
|
/** The account, as the closed set of questions above. */
|
|
109
162
|
account: SettingsAccountReader;
|
|
110
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[];
|
|
111
192
|
/**
|
|
112
193
|
* A capability's settings check, as it hangs off the capability.
|
|
113
194
|
*
|
|
@@ -121,6 +202,17 @@ export interface CapabilitySettings {
|
|
|
121
202
|
/** The half that costs a Cloudflare call. Omitted where a capability has nothing to ask the account. */
|
|
122
203
|
account?(context: SettingsAccountContext): Promise<SettingsFinding[]>;
|
|
123
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;
|
|
124
216
|
/**
|
|
125
217
|
* Turn a host env's own parse report into findings — the bridge that makes "one schema, two readers" real.
|
|
126
218
|
*
|
|
@@ -128,7 +220,7 @@ export interface CapabilitySettings {
|
|
|
128
220
|
* import("../workflow/hostEnv").checkHostEnv} against its own declaration, and hands the report here. The
|
|
129
221
|
* problem line is Zod's own message and the action line is {@link hostEnvProviderSentence} — the same
|
|
130
222
|
* words the host writes into its log before it refuses to start. Two readers of one declaration, and no
|
|
131
|
-
* second wording to drift.
|
|
223
|
+
* second wording to drift. A provider that names a command carries it as `fix` too.
|
|
132
224
|
*/
|
|
133
225
|
export declare function hostEnvFindings(report: HostEnvReport<unknown>, environment: string | null): SettingsFinding[];
|
|
134
226
|
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -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
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/capability/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { InternalError } from "../error/pithyError.js";
|
|
3
4
|
import { hostEnvProviderSentence } from "../workflow/hostEnv.js";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
//#region src/capability/settings.ts
|
|
@@ -45,24 +46,104 @@ const SettingsFinding = z.object({
|
|
|
45
46
|
setting: z.string().min(1).describe("What is wrong — the env field, config key, or account resource, spelled as the operator would find it. It leads the line, so it is the name they search for."),
|
|
46
47
|
environment: z.string().min(1).nullable().describe("The environment this is about, or `null` where the setting is the same in all of them. A per-environment fault is reported once per environment, because that is how many edits it takes."),
|
|
47
48
|
problem: z.string().min(1).describe("Why it does not work, in one sentence. The problem line, in the same voice every PithyError uses."),
|
|
48
|
-
action: z.string().min(1).describe("What resolves it: the `pithy` command, the `pithy.config.ts` key, or the one-time account action. Never optional — a finding nobody can act on is a complaint.")
|
|
49
|
+
action: z.string().min(1).describe("What resolves it: the `pithy` command, the `pithy.config.ts` key, or the one-time account action. Never optional — a finding nobody can act on is a complaint."),
|
|
50
|
+
fix: z.string().min(1).optional().describe("The `pithy` command that resolves it, whole and runnable as written, when one does. Absent when the remedy is an edit or an account action, and from a capability older than the field — `pithy doctor --json` reports either as `fix: null`.")
|
|
49
51
|
}).describe("One capability setting that does not work, with the action that resolves it.");
|
|
50
52
|
/**
|
|
53
|
+
* An account check that answered for some environments and not for others.
|
|
54
|
+
*
|
|
55
|
+
* A check that asks per environment must not let one unanswerable environment cost the others their
|
|
56
|
+
* findings: staging stood up before prod is the ordinary bring-up, and a check that threw on prod's missing
|
|
57
|
+
* database would throw away what it had already found in staging. So it collects what it can, and throws
|
|
58
|
+
* this at the end carrying those findings. The runner reports them as findings and the tier as unchecked,
|
|
59
|
+
* both — a fault and an unanswered question are two facts, and neither hides the other.
|
|
60
|
+
*/
|
|
61
|
+
var SettingsAccountIncomplete = class extends InternalError {
|
|
62
|
+
/** What the environments that did answer were found to have wrong. */
|
|
63
|
+
findings;
|
|
64
|
+
/** The environments that did not answer, in the order they were asked. */
|
|
65
|
+
unanswered;
|
|
66
|
+
constructor(findings, unanswered) {
|
|
67
|
+
const reasons = unanswered.map(({ environment, cause }) => `${environment}: ${describeCause(cause)}`).join("; ");
|
|
68
|
+
super({
|
|
69
|
+
message: `The account check could not answer for ${unanswered.map(({ environment }) => environment).join(", ")}.`,
|
|
70
|
+
detail: reasons
|
|
71
|
+
}, { cause: unanswered[0]?.cause });
|
|
72
|
+
this.findings = findings;
|
|
73
|
+
this.unanswered = unanswered.map(({ environment }) => environment);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
/** A cause, as one line. */
|
|
77
|
+
function describeCause(cause) {
|
|
78
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The findings an account check reached before it threw — none for an ordinary throw, and what it had
|
|
82
|
+
* collected for a {@link SettingsAccountIncomplete}.
|
|
83
|
+
*/
|
|
84
|
+
function settingsFindingsBefore(error) {
|
|
85
|
+
return error instanceof SettingsAccountIncomplete ? error.findings : [];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* **Two or more checks on one capability, as the one `settings` it declares (#728).** Media names records a
|
|
89
|
+
* narrowed visibility no longer admits and a bundle still holding a superseded token: two questions, one
|
|
90
|
+
* capability, one seam.
|
|
91
|
+
*
|
|
92
|
+
* Each tier runs every check's, in declaration order. A check that could not answer for some environments
|
|
93
|
+
* costs those environments and nothing else: the others' findings are kept, and the run ends in one
|
|
94
|
+
* {@link SettingsAccountIncomplete} carrying every finding and every unanswered environment. A check that
|
|
95
|
+
* throws outright is unanswered for every environment the context declares.
|
|
96
|
+
*/
|
|
97
|
+
function allSettings(...checks) {
|
|
98
|
+
const accounts = checks.flatMap((check) => check.account ? [check.account.bind(check)] : []);
|
|
99
|
+
return {
|
|
100
|
+
async local(context) {
|
|
101
|
+
const findings = [];
|
|
102
|
+
for (const check of checks) findings.push(...await check.local(context));
|
|
103
|
+
return findings;
|
|
104
|
+
},
|
|
105
|
+
...accounts.length === 0 ? {} : { async account(context) {
|
|
106
|
+
const findings = [];
|
|
107
|
+
const unanswered = [];
|
|
108
|
+
for (const account of accounts) try {
|
|
109
|
+
findings.push(...await account(context));
|
|
110
|
+
} catch (cause) {
|
|
111
|
+
findings.push(...settingsFindingsBefore(cause));
|
|
112
|
+
if (cause instanceof SettingsAccountIncomplete) {
|
|
113
|
+
for (const environment of cause.unanswered) if (!unanswered.some((entry) => entry.environment === environment)) unanswered.push({
|
|
114
|
+
environment,
|
|
115
|
+
cause: cause.cause ?? cause
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (context.environments.length === 0) throw cause;
|
|
120
|
+
for (const { name } of context.environments) if (!unanswered.some((entry) => entry.environment === name)) unanswered.push({
|
|
121
|
+
environment: name,
|
|
122
|
+
cause
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (unanswered.length > 0) throw new SettingsAccountIncomplete(findings, unanswered);
|
|
126
|
+
return findings;
|
|
127
|
+
} }
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
51
131
|
* Turn a host env's own parse report into findings — the bridge that makes "one schema, two readers" real.
|
|
52
132
|
*
|
|
53
133
|
* A capability's local tier builds the env its host *would* be handed, runs {@link
|
|
54
134
|
* import("../workflow/hostEnv").checkHostEnv} against its own declaration, and hands the report here. The
|
|
55
135
|
* problem line is Zod's own message and the action line is {@link hostEnvProviderSentence} — the same
|
|
56
136
|
* words the host writes into its log before it refuses to start. Two readers of one declaration, and no
|
|
57
|
-
* second wording to drift.
|
|
137
|
+
* second wording to drift. A provider that names a command carries it as `fix` too.
|
|
58
138
|
*/
|
|
59
139
|
function hostEnvFindings(report, environment) {
|
|
60
140
|
return report.problems.map((problem) => ({
|
|
61
141
|
setting: problem.field,
|
|
62
142
|
environment,
|
|
63
143
|
problem: problem.reason,
|
|
64
|
-
action: hostEnvProviderSentence(problem.provider)
|
|
144
|
+
action: hostEnvProviderSentence(problem.provider),
|
|
145
|
+
...problem.provider.command === void 0 ? {} : { fix: problem.provider.command }
|
|
65
146
|
}));
|
|
66
147
|
}
|
|
67
148
|
//#endregion
|
|
68
|
-
export { SettingsFinding, hostEnvFindings };
|
|
149
|
+
export { SettingsAccountIncomplete, SettingsFinding, allSettings, hostEnvFindings, settingsFindingsBefore };
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import type { DEFAULT_ENVIRONMENTS, LOCAL_ENVIRONMENT } from "../naming/environment";
|
|
4
|
+
import { COMMANDS, GROUPS } from "./commandCatalog.generated";
|
|
5
|
+
import { CONFIRM_FLAGS, type DestructiveFlag, type DestructivePath, EVERY_ENVIRONMENT_ALWAYS, MEANING_FLAGS, MEANING_ONLY_WITH, PRODUCTION_ENVIRONMENTS, type ProductionPath, type WorkerScopedPath } from "./commandFacts";
|
|
6
|
+
/**
|
|
7
|
+
* **The one place a `pithy` command line is spelled (#711).**
|
|
8
|
+
*
|
|
9
|
+
* #697 held every `fix` to one rule: it runs as written. The sentences beside it were held to nothing, and three
|
|
10
|
+
* review rounds each found new ones that did not run — a `--worker` dropped, an `--env` left to default to `dev`,
|
|
11
|
+
* a placeholder an agent pasted verbatim, a command that destroyed far more than the fault it was offered for.
|
|
12
|
+
* Every one was a command spelled by hand at its own site, so every fix was one site, and the next site was
|
|
13
|
+
* still spelled by hand.
|
|
14
|
+
*
|
|
15
|
+
* So no site spells one. A line that names a command gets it from here, and this module knows the tree
|
|
16
|
+
* (`commandCatalog.generated.ts`, projected from the CLI's own parser) and the facts the parser cannot state
|
|
17
|
+
* (`commandFacts.ts`). It refuses to build:
|
|
18
|
+
*
|
|
19
|
+
* - **a command that does not exist**, or a flag the command does not declare — at compile time for a literal
|
|
20
|
+
* path, at run time for one assembled from a capability's name;
|
|
21
|
+
* - **a command missing a flag it needs**: one the parser requires, one a rule over several flags requires, an
|
|
22
|
+
* `--env` whose default would change what the command acts on, or the `--worker` a command acting on one
|
|
23
|
+
* Worker is refused without — and `--worker` takes a {@link WorkerDir}, the Worker's `apps/` directory, never
|
|
24
|
+
* its deployed script name;
|
|
25
|
+
* - **a command that destroys something**, or a production provision. Those are a person's decision, and
|
|
26
|
+
* {@link personDecision} says so in a sentence that carries no command an agent would run.
|
|
27
|
+
*
|
|
28
|
+
* ## A builder reached at run time never throws because of data
|
|
29
|
+
*
|
|
30
|
+
* Every refusal above is about what **the program spells** — a command path, a flag name, an enum choice, the
|
|
31
|
+
* shape of the line — and a value is one of two kinds:
|
|
32
|
+
*
|
|
33
|
+
* - **Spelled by the program**: the path, each flag's name, a choice from a flag's fixed set, a Worker directory
|
|
34
|
+
* (a {@link WorkerDir}, which only {@link workerDirOf} makes). These are checked, at compile time where the path
|
|
35
|
+
* is a literal.
|
|
36
|
+
* - **Supplied by a caller**: a `--set` value, a secret's name, free text, anything a run read. These are
|
|
37
|
+
* shell-quoted as they are and never checked — `fromName=Acme <hi@acme.io>` is a value, not a placeholder, and a
|
|
38
|
+
* builder that read it as one turned `pithy add email` into `core/internal` after it had installed the package
|
|
39
|
+
* (#711). A `<placeholder>` is a fault of **spelling**, so the place that refuses one is the gate that reads
|
|
40
|
+
* spellings, `packages/cli/src/ci/runnableCommands.test.ts`, which reads every literal handed to a builder too.
|
|
41
|
+
*
|
|
42
|
+
* So whether a line is a person's decision never turns on a value at run time either. An environment a run reads —
|
|
43
|
+
* a deployed Worker's `ENVIRONMENT`, a caller's `--env` — is data: {@link pithyCommand} takes no `--env` on a
|
|
44
|
+
* command a production run makes a person's decision except a literal non-production one, {@link pithyCommandOf}
|
|
45
|
+
* takes none there at all, and a line that names one it read is {@link remedy}'s or {@link rerunRemedy}'s, which
|
|
46
|
+
* say a production one as a person's decision. {@link personDecision} takes only a command or flag that destroys
|
|
47
|
+
* something, which is shape. What is left to throw is a program that spelled a line wrong, every time it runs.
|
|
48
|
+
*
|
|
49
|
+
* `packages/cli/src/ci/runnableCommands.test.ts` reads every string in every package's shipped source and fails
|
|
50
|
+
* on a `pithy …` spelled anywhere but here, wherever it sits in the sentence.
|
|
51
|
+
*/
|
|
52
|
+
type Catalog = typeof COMMANDS;
|
|
53
|
+
/** A command the CLI runs, as typed after `pithy`. */
|
|
54
|
+
export type CommandPath = keyof Catalog;
|
|
55
|
+
/** A group, which only lists its subcommands. A sentence may name one in prose. */
|
|
56
|
+
export type GroupPath = (typeof GROUPS)[number];
|
|
57
|
+
declare const WORKER_DIR: unique symbol;
|
|
58
|
+
/**
|
|
59
|
+
* **A Worker as `--worker` takes it: its `apps/` directory.** Branded, so the deployed script name — a `string`
|
|
60
|
+
* every Worker also carries — cannot be passed where this is wanted. Made by {@link workerDirOf} from the
|
|
61
|
+
* Worker's directory.
|
|
62
|
+
*/
|
|
63
|
+
export type WorkerDir = string & {
|
|
64
|
+
readonly [WORKER_DIR]: true;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The Worker a directory holds, as `--worker` names it: the directory's own name. Takes the directory rather
|
|
68
|
+
* than a name, because a name in hand is as often the deployed script name, which `--worker` does not resolve.
|
|
69
|
+
*/
|
|
70
|
+
export declare function workerDirOf(worker: {
|
|
71
|
+
readonly dir: string;
|
|
72
|
+
}): WorkerDir;
|
|
73
|
+
declare const BUILT: unique symbol;
|
|
74
|
+
/** A command line this module built, and so one that runs as written. The only thing a `fix` may carry. */
|
|
75
|
+
export type Command = string & {
|
|
76
|
+
readonly [BUILT]: true;
|
|
77
|
+
};
|
|
78
|
+
type FlagDefs<P extends CommandPath> = Catalog[P]["flags"];
|
|
79
|
+
type FlagName<P extends CommandPath> = Extract<keyof FlagDefs<P>, string>;
|
|
80
|
+
type ConfirmFlag = (typeof CONFIRM_FLAGS)[number];
|
|
81
|
+
type MeaningFlag = (typeof MEANING_FLAGS)[number];
|
|
82
|
+
/**
|
|
83
|
+
* An environment no project's production run is: the local one, and the deployed one that is not `prod`. The only
|
|
84
|
+
* `--env` a literal line may give a command whose production run is a person's decision — so it is spelled, never
|
|
85
|
+
* read, and an environment a run read goes through {@link remedy}, which decides by its value.
|
|
86
|
+
*/
|
|
87
|
+
export type QuietEnvironment = typeof LOCAL_ENVIRONMENT | Exclude<(typeof DEFAULT_ENVIRONMENTS)[number], (typeof PRODUCTION_ENVIRONMENTS)[number]>;
|
|
88
|
+
/**
|
|
89
|
+
* The value one flag takes: `true` for a boolean, a {@link WorkerDir} for `--worker`, one of a fixed set's choices,
|
|
90
|
+
* a {@link QuietEnvironment} for the `--env` of a command a production run makes a person's decision, else a string.
|
|
91
|
+
*/
|
|
92
|
+
type FlagValue<P, D, F> = F extends "--worker" ? WorkerDir : D extends {
|
|
93
|
+
type: "boolean";
|
|
94
|
+
} ? true : F extends "--env" ? P extends ProductionPath ? QuietEnvironment : string : D extends {
|
|
95
|
+
enum: readonly (infer E)[];
|
|
96
|
+
} ? D extends {
|
|
97
|
+
repeatable: true;
|
|
98
|
+
} ? E | readonly E[] : E : D extends {
|
|
99
|
+
repeatable: true;
|
|
100
|
+
} ? string | readonly string[] : string;
|
|
101
|
+
/** Whether a line naming `P` must carry `F`. `add`'s Worker is its own builder's to name (`addCommand.ts`). */
|
|
102
|
+
type IsNeeded<P extends CommandPath, F extends FlagName<P>> = FlagDefs<P>[F] extends {
|
|
103
|
+
required: true;
|
|
104
|
+
} ? true : F extends MeaningFlag ? FlagDefs<P>[F] extends {
|
|
105
|
+
default: string;
|
|
106
|
+
} ? P extends keyof typeof MEANING_ONLY_WITH ? false : true : false : F extends "--worker" ? P extends Exclude<WorkerScopedPath, "add"> ? true : false : false;
|
|
107
|
+
type NeededFlag<P extends CommandPath> = {
|
|
108
|
+
[F in FlagName<P>]: IsNeeded<P, F> extends true ? F : never;
|
|
109
|
+
}[FlagName<P>];
|
|
110
|
+
/** The flags a line naming `P` passes: the ones it needs, and any other it declares that destroys nothing. */
|
|
111
|
+
export type CommandFlags<P extends CommandPath> = {
|
|
112
|
+
[F in NeededFlag<P>]: FlagValue<P, FlagDefs<P>[F], F>;
|
|
113
|
+
} & {
|
|
114
|
+
[F in Exclude<FlagName<P>, NeededFlag<P> | DestructiveFlag | ConfirmFlag>]?: FlagValue<P, FlagDefs<P>[F], F>;
|
|
115
|
+
};
|
|
116
|
+
type HasRequiredPositional<P extends CommandPath> = [
|
|
117
|
+
Extract<Catalog[P]["positionals"][number], {
|
|
118
|
+
required: true;
|
|
119
|
+
}>
|
|
120
|
+
] extends [never] ? false : true;
|
|
121
|
+
/** What a line naming `P` passes: its positional arguments, and its flags, in the order they are written. */
|
|
122
|
+
export type CommandParts<P extends CommandPath> = (HasRequiredPositional<P> extends true ? {
|
|
123
|
+
args: readonly [string, ...string[]];
|
|
124
|
+
} : {
|
|
125
|
+
args?: readonly string[];
|
|
126
|
+
}) & ([NeededFlag<P>] extends [never] ? {
|
|
127
|
+
flags?: CommandFlags<P>;
|
|
128
|
+
} : {
|
|
129
|
+
flags: CommandFlags<P>;
|
|
130
|
+
});
|
|
131
|
+
type PartsArgument<P extends CommandPath> = HasRequiredPositional<P> extends true ? [parts: CommandParts<P>] : [NeededFlag<P>] extends [never] ? [parts?: CommandParts<P>] : [parts: CommandParts<P>];
|
|
132
|
+
/**
|
|
133
|
+
* A command that is built like any other: one that destroys nothing, and that does not act on every environment on
|
|
134
|
+
* every run — a line names that one through {@link remedy}, which says it as a person's decision.
|
|
135
|
+
*/
|
|
136
|
+
export type BuildablePath = Exclude<CommandPath, DestructivePath | (typeof EVERY_ENVIRONMENT_ALWAYS)[number]>;
|
|
137
|
+
/** Parts as a builder holds them when the path is a value rather than a literal. */
|
|
138
|
+
export interface LooseParts {
|
|
139
|
+
readonly args?: readonly string[];
|
|
140
|
+
readonly flags?: Readonly<Record<string, string | true | readonly string[] | undefined>>;
|
|
141
|
+
}
|
|
142
|
+
/** What {@link commandFaults} is asked to allow. */
|
|
143
|
+
export interface FaultOptions {
|
|
144
|
+
/**
|
|
145
|
+
* A person's decision: a destructive command or flag, or a production run, is named for a person to choose,
|
|
146
|
+
* and the confirmation flags are theirs to add. {@link personDecision} asks for this; nothing else does.
|
|
147
|
+
*/
|
|
148
|
+
readonly person?: boolean;
|
|
149
|
+
/** The project's `seed.productionEnvironments`, beyond the {@link PRODUCTION_ENVIRONMENTS} every project has. */
|
|
150
|
+
readonly productionEnvironments?: readonly string[];
|
|
151
|
+
/**
|
|
152
|
+
* A line said by a **deployed** Worker — into its own log, or to an HTTP caller — which cannot know the `apps/`
|
|
153
|
+
* directory that provisioned it. Every rule holds but `--worker`. Only the files
|
|
154
|
+
* `packages/cli/src/ci/runnableCommands.test.ts` lists as runtime lines may pass it.
|
|
155
|
+
*/
|
|
156
|
+
readonly runtime?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* **Every value on the line was spelled by the program** — the gate, reading a line written in a literal — so a
|
|
159
|
+
* `<placeholder>` or a blank among them is a fault. No builder says this: a value handed to a builder is a
|
|
160
|
+
* caller's, and is shell-quoted as it is (see the module comment).
|
|
161
|
+
*/
|
|
162
|
+
readonly spelled?: boolean;
|
|
163
|
+
/** Treat `<x>`, the gate's mark for interpolation, as a value rather than a placeholder. */
|
|
164
|
+
readonly interpolated?: boolean;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* What a builder adds to {@link FaultOptions}: that every value is the caller's own run, handed back as it was
|
|
168
|
+
* given, so not even a fixed set of choices is held against it — the run's own parser has had its say.
|
|
169
|
+
*/
|
|
170
|
+
interface BuildOptions extends FaultOptions {
|
|
171
|
+
readonly given?: boolean;
|
|
172
|
+
}
|
|
173
|
+
/** Whether `path` is a command the CLI runs. */
|
|
174
|
+
export declare function isCommandPath(path: string): path is CommandPath;
|
|
175
|
+
/** Whether `path` is a group. */
|
|
176
|
+
export declare function isGroupPath(path: string): path is GroupPath;
|
|
177
|
+
/**
|
|
178
|
+
* What the catalog says about one flag of one command — its type, whether it is required, its default — or
|
|
179
|
+
* `undefined` when the command does not declare it.
|
|
180
|
+
*/
|
|
181
|
+
export declare function commandFlag(path: string, flag: string): {
|
|
182
|
+
readonly type: "boolean" | "string";
|
|
183
|
+
readonly required: boolean;
|
|
184
|
+
readonly default?: string;
|
|
185
|
+
} | undefined;
|
|
186
|
+
/**
|
|
187
|
+
* **Every reason a command line would not run as written, or would do more than a remedy may.** Empty when it
|
|
188
|
+
* runs. The builder throws on any; `runnableCommands.test.ts` holds every hand-spelled line it reads to the same
|
|
189
|
+
* function, so the gate and the builder cannot disagree about a rule.
|
|
190
|
+
*/
|
|
191
|
+
export declare function commandFaults(path: string, parts?: LooseParts, options?: BuildOptions): string[];
|
|
192
|
+
/**
|
|
193
|
+
* **What a command does that only a person may decide**: what it deletes, or that it runs against production. Empty
|
|
194
|
+
* for a command a remedy may name.
|
|
195
|
+
*/
|
|
196
|
+
export declare function consequencesOf(path: string, parts?: LooseParts, options?: FaultOptions): string[];
|
|
197
|
+
/**
|
|
198
|
+
* Whether `path` acts on every environment, production among them, when its `--env` is left off — `pithy testers
|
|
199
|
+
* provision` (`commandFacts.ts`'s {@link EVERY_ENVIRONMENT_WITHOUT_ENV}). A line about one environment names it.
|
|
200
|
+
*/
|
|
201
|
+
export declare function actsOnEveryEnvironment(path: string): boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Whether `path` acts on every environment, production among them, on every run, with no `--env` to narrow it —
|
|
204
|
+
* `pithy secrets provision` (`commandFacts.ts`'s {@link EVERY_ENVIRONMENT_ALWAYS}). A line naming it is a person's
|
|
205
|
+
* decision, always.
|
|
206
|
+
*/
|
|
207
|
+
export declare function actsOnEveryEnvironmentAlways(path: string): boolean;
|
|
208
|
+
/**
|
|
209
|
+
* **A command line that runs as written**, from a literal path: the tree checks its command, its flags and the
|
|
210
|
+
* flags it needs at compile time, and {@link commandFaults} checks the rest when it is built.
|
|
211
|
+
*/
|
|
212
|
+
export declare function pithyCommand<P extends BuildablePath>(path: P, ...parts: PartsArgument<P>): Command;
|
|
213
|
+
/** The parts of a line a deployed Worker builds: no `--env`, which it reads as data and names through {@link remedy}. */
|
|
214
|
+
export interface RuntimeParts extends LooseParts {
|
|
215
|
+
readonly flags?: LooseParts["flags"] & {
|
|
216
|
+
readonly "--env"?: never;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* **The same, from a path that is a value** — `` `${capability} provision` ``. Nothing is checked at compile
|
|
221
|
+
* time, so everything is checked here, and a path the CLI does not have is refused rather than spelled.
|
|
222
|
+
*
|
|
223
|
+
* Whether a command is a person's decision never turns on a value here. A runtime line names no `--env`: a deployed
|
|
224
|
+
* Worker's environment is data, and whether its command is a person's decision depends on it, so that line is
|
|
225
|
+
* {@link remedy}'s. Nor does any line name the `--env` of a command a production run makes a person's decision —
|
|
226
|
+
* `provision`, `seed`, `vector provision`. Each is refused for every value, so no environment is a surprise.
|
|
227
|
+
*/
|
|
228
|
+
export declare function pithyCommandOf(path: string, parts: RuntimeParts, options: {
|
|
229
|
+
readonly runtime: true;
|
|
230
|
+
}): Command;
|
|
231
|
+
export declare function pithyCommandOf(path: string, parts?: LooseParts): Command;
|
|
232
|
+
/**
|
|
233
|
+
* **A command a person decides on, said as one.** For a fault whose only remedy destroys something, or runs
|
|
234
|
+
* against production: the sentence names the command, says what it does and that the choice is a person's, and
|
|
235
|
+
* leaves every confirmation flag for them to add. It returns a sentence, never a {@link Command}, so it cannot
|
|
236
|
+
* become a `fix`.
|
|
237
|
+
*
|
|
238
|
+
* It takes a command that destroys something, or a line naming a flag that does — shape, never a value. A command
|
|
239
|
+
* whose production run is a person's decision is a decision only for a production `--env`, which is data, so that
|
|
240
|
+
* line is {@link remedy}'s, which decides by the value; here it is refused for every value.
|
|
241
|
+
*/
|
|
242
|
+
export declare function personDecision(path: PersonPath, parts: LooseParts, options?: Pick<FaultOptions, "productionEnvironments" | "runtime">): string;
|
|
243
|
+
/** A command that destroys something, or one declaring a flag that does: what {@link personDecision} takes. */
|
|
244
|
+
export type PersonPath = {
|
|
245
|
+
[P in CommandPath]: P extends DestructivePath ? P : [Extract<FlagName<P>, DestructiveFlag>] extends [never] ? never : P;
|
|
246
|
+
}[CommandPath];
|
|
247
|
+
/** **A remedy**: the command a `fix` carries when one may, and the words a line says about it. */
|
|
248
|
+
export interface Remedy {
|
|
249
|
+
/** The command, when it runs as written and a `fix` may carry it. `undefined` for a person's decision. */
|
|
250
|
+
readonly fix: Command | undefined;
|
|
251
|
+
/** The words: `wrap` around the command, or — for a person's decision — that sentence in its place. */
|
|
252
|
+
say(wrap: (command: string) => string): string;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* **The remedy a line names**, decided by the command rather than by its site: a command a remedy may name is a
|
|
256
|
+
* `fix` and goes into the sentence; one that destroys something or runs against production is a person's
|
|
257
|
+
* decision, said as one, with no `fix`. A command that does not run is refused either way.
|
|
258
|
+
*/
|
|
259
|
+
export declare function remedy(path: string, parts?: LooseParts, options?: Pick<FaultOptions, "productionEnvironments" | "runtime">): Remedy;
|
|
260
|
+
declare const CALLER: unique symbol;
|
|
261
|
+
/**
|
|
262
|
+
* **The caller's own run (#711): the one input every rerun and every fix naming it again is built from.**
|
|
263
|
+
*
|
|
264
|
+
* Each review round of #711 found another run again that dropped what the caller gave — `--json`, `--worker`,
|
|
265
|
+
* `--dry-run`, a `--set`, an option the Worker's config already answers — because each was rebuilt at its own site
|
|
266
|
+
* from whatever flags that site held. So a rerun builder ({@link rerunCommand}, {@link rerunRemedy}, `pithy add`'s
|
|
267
|
+
* `addAgain`) takes this and nothing else, and this is made only by {@link callerOf}, from a run's parsed arguments.
|
|
268
|
+
* Branded, so loose flags do not type-check where one is wanted; `packages/cli/src/ci/runnableCommands.test.ts`
|
|
269
|
+
* fails on a cast to it and on a {@link callerOf} handed anything but a run's own arguments.
|
|
270
|
+
*/
|
|
271
|
+
export interface Caller<P extends CommandPath = CommandPath> {
|
|
272
|
+
/** The command the run was. */
|
|
273
|
+
readonly path: P;
|
|
274
|
+
/** Its parsed arguments, keyed as the parser keys them (`worker`, `routing-zone`, `dry-run`). */
|
|
275
|
+
readonly given: Readonly<Record<string, unknown>>;
|
|
276
|
+
/** Its `--set` values, by key, and every option it settled by a prompt ({@link settledBy}). */
|
|
277
|
+
readonly set: Readonly<Record<string, string>>;
|
|
278
|
+
/**
|
|
279
|
+
* The options the Worker's config already answers, for a command that reads one (`pithy add`): settled, so a
|
|
280
|
+
* rerun names no `--set` for them, which could contradict the config.
|
|
281
|
+
*/
|
|
282
|
+
readonly answered: ReadonlySet<string>;
|
|
283
|
+
readonly [CALLER]: true;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* **A run's own context**, from its parsed arguments. `set` is the raw `--set` entries where the parser keeps only the
|
|
287
|
+
* last (`collectSetFlags`); absent, they are read from `given.set`. `answered` is what the Worker's config states.
|
|
288
|
+
*/
|
|
289
|
+
export declare function callerOf<P extends CommandPath>(path: P, given: Readonly<Record<string, unknown>>, context?: {
|
|
290
|
+
readonly set?: readonly string[] | undefined;
|
|
291
|
+
readonly answered?: Iterable<string> | undefined;
|
|
292
|
+
}): Caller<P>;
|
|
293
|
+
/** The same caller, with what the run settled on its way — a prompt's answers — carried as `--set` too. */
|
|
294
|
+
export declare function settledBy<P extends CommandPath>(caller: Caller<P>, values: Readonly<Record<string, string>>): Caller<P>;
|
|
295
|
+
/** The same caller, with the options its Worker's config answers. */
|
|
296
|
+
export declare function answeredBy<P extends CommandPath>(caller: Caller<P>, keys: Iterable<string>): Caller<P>;
|
|
297
|
+
/** What a run-again is told beyond the arguments the run was given. */
|
|
298
|
+
export interface RerunOptions {
|
|
299
|
+
/** The positional arguments, as the command takes them. */
|
|
300
|
+
readonly args?: readonly string[];
|
|
301
|
+
/** What the run resolved for itself — the Worker it picked when it was not named — or `undefined` to drop one. */
|
|
302
|
+
readonly flags?: LooseParts["flags"];
|
|
303
|
+
/** The project's `seed.productionEnvironments`, beyond the ones every project has. */
|
|
304
|
+
readonly productionEnvironments?: readonly string[] | undefined;
|
|
305
|
+
}
|
|
306
|
+
/** A command whose run again is always a command: it destroys nothing, and no production run of it is a person's. */
|
|
307
|
+
export type RerunPath = Exclude<CommandPath, DestructivePath | ProductionPath>;
|
|
308
|
+
/**
|
|
309
|
+
* **The caller's own command, to run again (#711)**, as a {@link Command} — rebuilt from the run's {@link Caller}, so
|
|
310
|
+
* a rerun asks for nothing the first run did not. `options.flags` adds what the run resolved for itself (the Worker it
|
|
311
|
+
* picked) or drops a flag whose work is done (`undefined`).
|
|
312
|
+
*
|
|
313
|
+
* It takes no command whose run again could be a person's decision — one that destroys something, or one a
|
|
314
|
+
* production `--env` makes a person's: that run again is {@link rerunRemedy}'s, which says so rather than throwing.
|
|
315
|
+
*/
|
|
316
|
+
export declare function rerunCommand(caller: Caller<RerunPath>, options?: Omit<RerunOptions, "productionEnvironments">): Command;
|
|
317
|
+
/**
|
|
318
|
+
* **The caller's own command, as a line names it to run again (#711)** — the {@link Remedy} for
|
|
319
|
+
* {@link rerunCommand}'s parts, for any command. A run again that a production `--env` makes a person's decision is
|
|
320
|
+
* said as one, with no `fix`, rather than thrown after the run did its work. `say` hands `wrap` the command in code
|
|
321
|
+
* and — where the run was given a credential by flag, which no line repeats — the flags to pass again beside it.
|
|
322
|
+
*/
|
|
323
|
+
export declare function rerunRemedy(caller: Caller, options?: RerunOptions): Remedy;
|
|
324
|
+
/**
|
|
325
|
+
* **A command named in prose, not given to run** — "as `pithy add` wrote it". Checked to exist, and spelled with
|
|
326
|
+
* nothing after it. A line that tells somebody to run a command builds it with {@link pithyCommand}.
|
|
327
|
+
*/
|
|
328
|
+
export declare function commandName(path: CommandPath | GroupPath): string;
|
|
329
|
+
/** {@link commandName}, for a path that is a value: refused when the CLI has no such command or group. */
|
|
330
|
+
export declare function commandNameOf(path: string): string;
|
|
331
|
+
/**
|
|
332
|
+
* {@link commandNameOf}, for a path as a parser walked it, word by word. Empty is the root, which is the CLI
|
|
333
|
+
* itself rather than a command, and is named as such.
|
|
334
|
+
*/
|
|
335
|
+
export declare function commandNameOfPath(path: readonly string[]): string;
|
|
336
|
+
/**
|
|
337
|
+
* **The CLI at a version** — `pithy 0.9.5`, as a line names a release rather than a command. A line never spells
|
|
338
|
+
* `pithy` before an interpolation: the gate reads that as a command it cannot check.
|
|
339
|
+
*/
|
|
340
|
+
export declare function cliAtVersion(version: string): string;
|
|
341
|
+
/**
|
|
342
|
+
* **How each package manager runs a project's own binary**: `bun x`, `pnpm exec`, `yarn`, `npx`. The one table
|
|
343
|
+
* both a spawned process (`packages/cli/src/project/packageManager.ts`'s `execArgs`) and a line naming one read.
|
|
344
|
+
*/
|
|
345
|
+
export declare const PACKAGE_EXEC: {
|
|
346
|
+
readonly bun: readonly ["bun", "x"];
|
|
347
|
+
readonly pnpm: readonly ["pnpm", "exec"];
|
|
348
|
+
readonly yarn: readonly ["yarn"];
|
|
349
|
+
readonly npm: readonly ["npx"];
|
|
350
|
+
};
|
|
351
|
+
/** A package manager a project runs its own binaries through. */
|
|
352
|
+
export type ExecPackageManager = keyof typeof PACKAGE_EXEC;
|
|
353
|
+
/**
|
|
354
|
+
* **A built command, run through the project's own package manager** — the `pithy` the project installed, never
|
|
355
|
+
* a global one, which may be an older CLI than the line is about. Takes a {@link Command}, so what follows the
|
|
356
|
+
* prefix was built and runs as written.
|
|
357
|
+
*/
|
|
358
|
+
export declare function viaPackageManager(pm: ExecPackageManager, command: Command): Command;
|
|
359
|
+
/** `a`, `a or b`, `a, b or c`, each in code: commands a reader picks one of. */
|
|
360
|
+
export declare function commandAlternatives(commands: readonly string[]): string;
|
|
361
|
+
/** `a`, `a and b`, `a, b and c`, each in code: commands that all run. */
|
|
362
|
+
export declare function commandSequence(commands: readonly string[]): string;
|
|
363
|
+
export {};
|
|
364
|
+
//# sourceMappingURL=command.d.ts.map
|