@penvhq/provider-github 0.11.0 → 0.13.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/index.cjs +14801 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +338 -12
- package/dist/index.d.ts +338 -12
- package/dist/index.js +14804 -30
- package/dist/index.js.map +1 -1
- package/package.json +14 -4
- package/penv.d.ts +23 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,341 @@
|
|
|
1
|
-
|
|
1
|
+
/** Identifies one parameter, independent of scope. `redis/password`. */
|
|
2
|
+
interface ParameterRef {
|
|
3
|
+
/** Namespace folder segments. `[]` for a root parameter. */
|
|
4
|
+
readonly namespace: readonly string[];
|
|
5
|
+
/** The parameter's own name. `password`. */
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The provider config types penv knows about, keyed by package name. Empty here,
|
|
10
|
+
* deliberately: core owns the `Provider` contract and must not know which
|
|
11
|
+
* implementations exist. Each provider package augments this interface with its
|
|
12
|
+
* own config shape under its own name —
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* declare module "@penvhq/core" {
|
|
16
|
+
* interface ProviderConfigMap {
|
|
17
|
+
* "@penvhq/provider-vault": VaultProviderConfig;
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* — so the compile-time union is exactly the set of providers the project has
|
|
23
|
+
* installed, and {@link defineConfig} can hold a known `type`'s fields to the
|
|
24
|
+
* provider's own declaration while leaving an unknown `type` the open base shape.
|
|
25
|
+
*/
|
|
26
|
+
interface ProviderConfigMap {
|
|
27
|
+
}
|
|
28
|
+
/** The provider package names whose config types are installed and merged in. */
|
|
29
|
+
type KnownProviderType = keyof ProviderConfigMap & string;
|
|
30
|
+
interface ProviderConfig {
|
|
31
|
+
/**
|
|
32
|
+
* The provider package's fully-qualified name — `"@penvhq/provider-vault"`.
|
|
33
|
+
* The name is the import specifier: penv resolves it from the project's own
|
|
34
|
+
* `node_modules`, so declaring a provider and installing its package are the
|
|
35
|
+
* same decision stated twice, and the config never needs a second field to
|
|
36
|
+
* say where the implementation lives.
|
|
37
|
+
*/
|
|
38
|
+
readonly type: KnownProviderType | (string & {});
|
|
39
|
+
/**
|
|
40
|
+
* The place inside the provider that penv maps the tree onto. The format is
|
|
41
|
+
* the provider's own — a Vault KV base path, a Kubernetes
|
|
42
|
+
* `namespace/secretName` — and its package's config type documents it; the
|
|
43
|
+
* field name never changes between providers.
|
|
44
|
+
*/
|
|
45
|
+
readonly location?: string;
|
|
46
|
+
/** Fields beyond `location` belong to the provider's own config type. */
|
|
47
|
+
readonly [key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* What penv hands a provider package's `penvProviderFactory` to build a provider
|
|
51
|
+
* for one project. Declared here because it is the seam every provider package
|
|
52
|
+
* builds against — the CLI supplies it, the package consumes it, and neither
|
|
53
|
+
* imports the other's internals.
|
|
54
|
+
*/
|
|
55
|
+
interface ProviderFactoryContext {
|
|
56
|
+
/**
|
|
57
|
+
* The project root — the directory holding `penv.config.ts` — absolute. It is
|
|
58
|
+
* the project rather than any directory inside it because where penv keeps its
|
|
59
|
+
* own state is penv's business: a provider that needs a path derives it from
|
|
60
|
+
* the root, and one that needs none is unaffected when the layout moves.
|
|
61
|
+
*/
|
|
62
|
+
readonly root: string;
|
|
63
|
+
/**
|
|
64
|
+
* Required because a provider parses environment segments, and a segment is an
|
|
65
|
+
* environment only if the config declares it — never inferred from the store.
|
|
66
|
+
*/
|
|
67
|
+
readonly config: PenvConfig;
|
|
68
|
+
/**
|
|
69
|
+
* The one environment's own `providers.*` entry, when building its declared
|
|
70
|
+
* source of truth. Carries provider-side settings — the `location` above all —
|
|
71
|
+
* that the config authored, never inferred.
|
|
72
|
+
*/
|
|
73
|
+
readonly providerConfig?: ProviderConfig;
|
|
74
|
+
/**
|
|
75
|
+
* The environment this provider is the source of truth *for*, when that is
|
|
76
|
+
* what is being built.
|
|
77
|
+
*/
|
|
78
|
+
readonly environment?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The schema's inferred shape, registered by the schema module so the config
|
|
82
|
+
* file can be typed against it. Empty here, deliberately — core must not depend
|
|
83
|
+
* on Zod or know any one project's schema. The scaffolded `penv.schema.ts`
|
|
84
|
+
* augments it with the *inferred* shape (computed where Zod lives), beside the
|
|
85
|
+
* `z.object` it registers — the thin `.penv/env.ts` wrapper only re-exports that
|
|
86
|
+
* shape and loads it:
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* declare module "@penvhq/core" {
|
|
90
|
+
* interface PenvSchemaShape {
|
|
91
|
+
* readonly shape: z.infer<typeof schema>;
|
|
92
|
+
* }
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* The augmentation is type-only and erased at runtime, so nothing cycles: the
|
|
97
|
+
* config never imports the schema module, yet `override` keys autocomplete from
|
|
98
|
+
* it whenever both files sit in one TypeScript program. A project that never
|
|
99
|
+
* registers a shape keeps the open `string` keys.
|
|
100
|
+
*/
|
|
101
|
+
interface PenvSchemaShape {
|
|
102
|
+
}
|
|
103
|
+
/** camelCase → kebab-case at the type level, mirroring `kebabSegment` exactly. */
|
|
104
|
+
type KebabCase<S extends string> = S extends `${infer Head}${infer Tail}` ? Head extends Uppercase<Head> ? Head extends Lowercase<Head> ? `${Head}${KebabCase<Tail>}` : `-${Lowercase<Head>}${KebabCase<Tail>}` : `${Head}${KebabCase<Tail>}` : S;
|
|
105
|
+
type ParameterIdsOf<T> = {
|
|
106
|
+
[K in keyof T & string]: NonNullable<T[K]> extends readonly unknown[] ? KebabCase<K> : NonNullable<T[K]> extends Date ? KebabCase<K> : NonNullable<T[K]> extends Record<string, unknown> ? `${KebabCase<K>}/${ParameterIdsOf<NonNullable<T[K]>>}` : KebabCase<K>;
|
|
107
|
+
}[keyof T & string];
|
|
108
|
+
/** The registered shape, or `never` when no schema module has registered one. */
|
|
109
|
+
type RegisteredShape = PenvSchemaShape extends {
|
|
110
|
+
readonly shape: infer S;
|
|
111
|
+
} ? S : never;
|
|
112
|
+
/**
|
|
113
|
+
* The `override` block's key type: every parameter id the registered schema
|
|
114
|
+
* implies — so a typo is a compile error — or plain `string` when no shape is
|
|
115
|
+
* registered.
|
|
116
|
+
*/
|
|
117
|
+
type OverrideKey = [RegisteredShape] extends [never] ? string : ParameterIdsOf<RegisteredShape>;
|
|
118
|
+
/** The `override` block: parameter id → the exact variable a consumer expects. */
|
|
119
|
+
type OverrideBlock = Readonly<Partial<Record<OverrideKey, string>>>;
|
|
120
|
+
/**
|
|
121
|
+
* What a provider's store can honestly do — the declaration that replaced the
|
|
122
|
+
* old sink/provider split. The distinction the split guarded is real (GitHub
|
|
123
|
+
* Actions Secrets never returns a value) and lives here now, in the contract,
|
|
124
|
+
* instead of in a second config key the user had to learn.
|
|
125
|
+
*/
|
|
126
|
+
interface ProviderCapabilities {
|
|
127
|
+
/**
|
|
128
|
+
* What the store holds. `records`: penv records verbatim — opaque envelope
|
|
129
|
+
* strings at every scope, meta as a sibling record — the shape the behavioural
|
|
130
|
+
* contract suite gates. `projection`: a resolved projection — generated
|
|
131
|
+
* variable names, both `.local` scopes skipped, plaintext for the destination
|
|
132
|
+
* to re-seal — the shape a CI secret store consumes.
|
|
133
|
+
*/
|
|
134
|
+
readonly holds: "records" | "projection";
|
|
135
|
+
/**
|
|
136
|
+
* Whether stored values can be read back. GitHub's API returns names and
|
|
137
|
+
* timestamps, never values, so it declares `false` — and `pull` materialises
|
|
138
|
+
* names and meta while `doctor` reports value drift as unknown, never as
|
|
139
|
+
* clean.
|
|
140
|
+
*/
|
|
141
|
+
readonly readsValues: boolean;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Where one environment's encryption key comes from. Declared, never guessed: a
|
|
145
|
+
* key source penv picked for you is a key you did not choose.
|
|
146
|
+
*/
|
|
147
|
+
interface KeyConfig {
|
|
148
|
+
readonly source: "env" | "keychain";
|
|
149
|
+
/**
|
|
150
|
+
* Names the key. Written into every value file sealed under it, so it must
|
|
151
|
+
* outlive any one machine — and cannot contain `:`, which separates the
|
|
152
|
+
* envelope's fields.
|
|
153
|
+
*/
|
|
154
|
+
readonly id: string;
|
|
155
|
+
}
|
|
156
|
+
interface PenvConfig {
|
|
157
|
+
/**
|
|
158
|
+
* The whitelist of valid environment names — the only source of truth for
|
|
159
|
+
* what counts as an environment. Segments are matched against this list,
|
|
160
|
+
* never inferred from folders or filenames.
|
|
161
|
+
*/
|
|
162
|
+
readonly environments: readonly string[];
|
|
163
|
+
readonly providers: Readonly<Record<string, ProviderConfig>>;
|
|
164
|
+
/**
|
|
165
|
+
* The environment a command acts on when `--env` is absent and nothing in the
|
|
166
|
+
* environment says otherwise. It must be one of {@link environments}.
|
|
167
|
+
*
|
|
168
|
+
* A declared decision, not inference: invariant 10 is untouched, because the
|
|
169
|
+
* name still comes from this file rather than from a branch, a folder, or
|
|
170
|
+
* `NODE_ENV`. It exists so the daily command is `penv run -- pnpm dev` instead
|
|
171
|
+
* of a flag retyped all day. CI names `--env` anyway — a pipeline that leans on
|
|
172
|
+
* this key is one config edit away from deploying the wrong environment.
|
|
173
|
+
*/
|
|
174
|
+
readonly defaultEnvironment?: string;
|
|
175
|
+
/**
|
|
176
|
+
* Where the module holding the schema lives, relative to this config.
|
|
177
|
+
* Defaults to `.penv/env.ts`.
|
|
178
|
+
*
|
|
179
|
+
* A path rather than a convention because the file is the user's (invariant 2)
|
|
180
|
+
* — a file penv insists on owning the location of is not fully theirs, and
|
|
181
|
+
* `src/env.ts` is where most projects would put it. Nothing downstream moves
|
|
182
|
+
* when it does: consumers import `@env`, and the alias is what penv writes.
|
|
183
|
+
*/
|
|
184
|
+
readonly schemaFile?: string;
|
|
185
|
+
/**
|
|
186
|
+
* The variable-name prefixes a framework inlines into its client bundle —
|
|
187
|
+
* `NEXT_PUBLIC_`, `VITE_`.
|
|
188
|
+
*
|
|
189
|
+
* penv does not enforce these; the framework already does. Declaring them is
|
|
190
|
+
* what lets `doctor` catch the one mistake neither penv nor the framework can
|
|
191
|
+
* catch alone: a parameter meta declares `secret` whose name makes the
|
|
192
|
+
* framework ship it to a browser. To the framework the prefix *is* the intent,
|
|
193
|
+
* so only penv — holding both the policy and the name — can see the
|
|
194
|
+
* contradiction.
|
|
195
|
+
*/
|
|
196
|
+
readonly publicPrefixes?: readonly string[];
|
|
197
|
+
/**
|
|
198
|
+
* Overrides the generated variable for a parameter, when a consumer demands a
|
|
199
|
+
* name the default transform would not produce — the WorkOS SDK reading
|
|
200
|
+
* `NEXT_PUBLIC_WORKOS_REDIRECT_URI`, a deploy target expecting `DATABASE_URL`
|
|
201
|
+
* spelled its way. One override bends the name for every consumer at once:
|
|
202
|
+
* `generate`, `push`, and the ambient mirror all read it. Collision-checked.
|
|
203
|
+
*
|
|
204
|
+
* Keys are parameter ids (`workos/redirect-uri`). When the schema module
|
|
205
|
+
* registers its shape (see {@link PenvSchemaShape}), the keys narrow to the
|
|
206
|
+
* parameters the schema actually declares — a typo'd id becomes a compile
|
|
207
|
+
* error instead of an override that silently never applies.
|
|
208
|
+
*/
|
|
209
|
+
readonly override?: OverrideBlock;
|
|
210
|
+
/**
|
|
211
|
+
* Where each environment's encryption key lives. An environment with no entry
|
|
212
|
+
* has no key source, which is not the same as having no key — see `keys.ts`.
|
|
213
|
+
*/
|
|
214
|
+
readonly keys?: Readonly<Record<string, KeyConfig>>;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Which destination store a value lands in. The two members are penv's own
|
|
218
|
+
* precedence axis wearing the destination's names: the unscoped default is the
|
|
219
|
+
* value every context falls back to (`repository`), an environment-scoped value
|
|
220
|
+
* belongs to exactly one (`environment`). GitHub resolves the two in penv's own
|
|
221
|
+
* order — environment over repository — so the cascade is reproduced by the
|
|
222
|
+
* destination's native mechanism rather than flattened at the boundary.
|
|
223
|
+
*/
|
|
224
|
+
type SecretScope = {
|
|
225
|
+
readonly kind: "repository";
|
|
226
|
+
} | {
|
|
227
|
+
readonly kind: "environment";
|
|
228
|
+
readonly environment: string;
|
|
229
|
+
};
|
|
230
|
+
/** One secret as a value-withholding store reports it: a name and when it last changed, never a value. */
|
|
231
|
+
interface ProjectionSecret {
|
|
232
|
+
readonly name: string;
|
|
233
|
+
/** The destination's last-modified time, ISO 8601. Compared against penv's last-push time to catch a hand-edit. */
|
|
234
|
+
readonly updatedAt: string;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* A provider whose store holds a resolved *projection* rather than penv records
|
|
238
|
+
* — generated variable names, both `.local` scopes skipped, plaintext the
|
|
239
|
+
* destination re-seals under its own custody. It declares
|
|
240
|
+
* `capabilities.holds: "projection"` and satisfies this contract instead of the
|
|
241
|
+
* seven-method record contract, which its store cannot honestly implement: a
|
|
242
|
+
* `read` that can never return a value would make every downstream check read
|
|
243
|
+
* an unreadable store as an empty one.
|
|
244
|
+
*
|
|
245
|
+
* penv resolves the tree; the projection receives it. `push` speaks this
|
|
246
|
+
* surface; `pull` materialises what `list` can honestly give — names and
|
|
247
|
+
* timestamps, never values.
|
|
248
|
+
*/
|
|
249
|
+
interface ProjectionProvider {
|
|
250
|
+
readonly type: string;
|
|
251
|
+
readonly capabilities: ProviderCapabilities & {
|
|
252
|
+
readonly holds: "projection";
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Confirms the destination is reachable before the first push. Every name is
|
|
256
|
+
* already judged up front, so a push never places half its secrets and then
|
|
257
|
+
* hits a reserved name; this closes the other pre-push gap — the destination
|
|
258
|
+
* being unreachable. GitHub through `gh` checks it is installed, authenticated,
|
|
259
|
+
* and can reach this repository's secrets, and refuses loudly rather than
|
|
260
|
+
* falling back. Resolves when it is safe to push.
|
|
261
|
+
*/
|
|
262
|
+
verify(): Promise<void>;
|
|
263
|
+
/**
|
|
264
|
+
* Sends one value to the destination, which seals it under its own key. The
|
|
265
|
+
* value crosses in plaintext because a CI runner holds no penv key — penv's
|
|
266
|
+
* encryption stops at the projection and the destination's custody takes over.
|
|
267
|
+
*/
|
|
268
|
+
push(name: string, value: string, scope: SecretScope): Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* The names the destination holds at a scope, each with its last-modified
|
|
271
|
+
* time. Listing names is the one read a value-withholding store allows.
|
|
272
|
+
*/
|
|
273
|
+
list(scope: SecretScope): Promise<ProjectionSecret[]>;
|
|
274
|
+
/**
|
|
275
|
+
* The destination's own name grammar, judged before the first PUT. The rules
|
|
276
|
+
* are the destination's (GitHub's reserved `GITHUB_` prefix, its charset, its
|
|
277
|
+
* case-insensitivity), so the provider owns the check — the CLI only insists
|
|
278
|
+
* it runs before anything is pushed, never mid-push.
|
|
279
|
+
*/
|
|
280
|
+
checkNames?(refs: readonly ParameterRef[], config: PenvConfig): PenvErrorLike[];
|
|
281
|
+
/**
|
|
282
|
+
* Whether the destination-side target an environment's push lands in exists
|
|
283
|
+
* yet — a GitHub deployment environment, say. Implemented only where the
|
|
284
|
+
* destination has such a notion.
|
|
285
|
+
*/
|
|
286
|
+
targetExists?(environment: string): Promise<boolean>;
|
|
287
|
+
/**
|
|
288
|
+
* Creates the destination-side target for an environment. Never called
|
|
289
|
+
* without an explicit go-ahead: the CLI prompts (or `--yes` pre-approves) and
|
|
290
|
+
* only then asks the provider, which stays non-interactive. Creation on an
|
|
291
|
+
* explicit answer, never a guess — a typo'd environment name must not summon
|
|
292
|
+
* infrastructure.
|
|
293
|
+
*/
|
|
294
|
+
ensureTarget?(environment: string): Promise<void>;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* The error shape {@link ProjectionProvider.checkNames} returns — structurally
|
|
298
|
+
* `PenvError`, stated as an interface so the contract does not force provider
|
|
299
|
+
* packages to subclass core's error class. It is also what a refusal thrown by a
|
|
300
|
+
* self-contained extension arrives as: `isPenvErrorLike` is the test penv runs
|
|
301
|
+
* on one, since the class it was built from is the extension's own copy.
|
|
302
|
+
*/
|
|
303
|
+
interface PenvErrorLike extends Error {
|
|
304
|
+
readonly code: string;
|
|
305
|
+
readonly remedy?: string | undefined;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Named errors. Every message names the parameter and environment, says what is
|
|
310
|
+
* wrong, and says how to fix it. Never `Something went wrong`.
|
|
311
|
+
*
|
|
312
|
+
* A penv error also renders itself the way the CLI renders one. The CLI can
|
|
313
|
+
* format what it catches, but the refusals thrown by the application's bridge
|
|
314
|
+
* are caught by nobody — an app started without `penv run` prints them through
|
|
315
|
+
* Node's default uncaught-exception handler, which prints `stack`. So `stack`
|
|
316
|
+
* carries the refusal first and the remedy behind the same arrow every command
|
|
317
|
+
* prints, and the frames below it are the caller's, not penv's.
|
|
318
|
+
*/
|
|
319
|
+
|
|
320
|
+
declare class PenvError extends Error {
|
|
321
|
+
readonly name: string;
|
|
322
|
+
/** A stable, machine-readable discriminator. */
|
|
323
|
+
readonly code: string;
|
|
324
|
+
/** What the user should do about it. */
|
|
325
|
+
readonly remedy: string | undefined;
|
|
326
|
+
/** The message alone, without the remedy the constructor folds into it. */
|
|
327
|
+
readonly summary: string;
|
|
328
|
+
constructor(code: string, message: string, remedy?: string);
|
|
329
|
+
/**
|
|
330
|
+
* Drops every frame from `below` upward, so the stack shows where the
|
|
331
|
+
* application called in rather than the path penv took inside itself. The
|
|
332
|
+
* caller names its own entry point; nothing here guesses which files are
|
|
333
|
+
* penv's.
|
|
334
|
+
*/
|
|
335
|
+
hideFramesAbove(below: (...args: never[]) => unknown): this;
|
|
336
|
+
/** The refusal as every penv command prints it: the message, then the remedy. */
|
|
337
|
+
toString(): string;
|
|
338
|
+
}
|
|
2
339
|
|
|
3
340
|
/**
|
|
4
341
|
* The provider's own errors, extending penv's base so they print with a remedy and
|
|
@@ -45,17 +382,6 @@ declare class GithubUnavailableError extends PenvError {
|
|
|
45
382
|
* learns GitHub vocabulary and the provider never parses config.
|
|
46
383
|
*/
|
|
47
384
|
|
|
48
|
-
declare module "@penvhq/core" {
|
|
49
|
-
interface ProviderConfigMap {
|
|
50
|
-
"@penvhq/provider-github": {
|
|
51
|
-
/**
|
|
52
|
-
* The repository penv maps the projection onto — `owner/repo`. Left
|
|
53
|
-
* unset, `gh` resolves it from the working directory.
|
|
54
|
-
*/
|
|
55
|
-
readonly location?: string;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
385
|
/** Builds the GitHub provider for one environment's declared destination. */
|
|
60
386
|
declare function penvProviderFactory(context: ProviderFactoryContext): ProjectionProvider;
|
|
61
387
|
|