@penvhq/launcher 0.9.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.
@@ -0,0 +1,601 @@
1
+ import { PenvError, ManifestEngine } from '@penvhq/core';
2
+
3
+ /**
4
+ * Every byte penv downloads comes through here.
5
+ *
6
+ * One method, so the test suite hands the store a fake and the whole launcher
7
+ * runs with no network at all — and so the "CI never downloads" guarantee is a
8
+ * property of one call site rather than of every place a URL is built.
9
+ */
10
+ interface Fetcher {
11
+ /** The bytes at `url`, or a thrown error saying what the registry did. */
12
+ get(url: string): Promise<Uint8Array>;
13
+ }
14
+ /** The real one: `fetch`, and a thrown error for anything that is not 200. */
15
+ declare function httpFetcher(): Fetcher;
16
+
17
+ /**
18
+ * The terminal, as three questions and two streams.
19
+ *
20
+ * It lives apart from the protocol because `penv add` asks the only question
21
+ * penv cannot answer for anyone: why a stranger's code is trusted. `confirm` is
22
+ * a decision penv could describe; `ask` is a sentence only a person can write.
23
+ */
24
+ interface LauncherIo {
25
+ out(line: string): void;
26
+ err(line: string): void;
27
+ /** Whether a human is at the other end of the streams. */
28
+ readonly interactive: boolean;
29
+ confirm(question: string): Promise<boolean>;
30
+ /** A free-text answer, trimmed. Empty means the person declined to write one. */
31
+ ask(question: string): Promise<string>;
32
+ }
33
+
34
+ /**
35
+ * `penv add <package>` — the one command that decides to trust something.
36
+ *
37
+ * It belongs to the launcher rather than the engine because everything it does
38
+ * is the launcher's: resolve an exact version, verify the bytes, install them
39
+ * into `$PENV_HOME`, and write the manifest that pins them. No engine is needed
40
+ * for any of that, and the engine importing the store would close a cycle.
41
+ *
42
+ * The trust model exists for strangers, and only strangers pay it. An
43
+ * `@penvhq/*` package is resolved, verified and recorded without one question;
44
+ * a public third-party package waits out a minimum age and commits a block
45
+ * saying who published it and why a person trusted it; a package from a private
46
+ * registry commits the registry and the acknowledgement. Credentials are never
47
+ * part of any of it — `.npmrc` owns those.
48
+ *
49
+ * Nothing here puts adapter code on any startup path. What lands in the project
50
+ * is two committed files with no runtime in them: the manifest entry, and a
51
+ * type-only declaration.
52
+ *
53
+ * Because those two files are committed, `add` is a decision and needs a person:
54
+ * `--no-download` and a run with nobody at it are both refused before the first
55
+ * request, and CI gets `penv install`, which installs what the manifest already
56
+ * pins rather than choosing what it should.
57
+ */
58
+
59
+ interface AddOptions {
60
+ /** The tokens after `add`. */
61
+ readonly argv: readonly string[];
62
+ /** The project root — the directory holding `.penv/`. */
63
+ readonly root: string;
64
+ readonly manifestFile: string;
65
+ readonly home: string;
66
+ readonly io: LauncherIo;
67
+ readonly fetcher: Fetcher;
68
+ /** The launcher's `--no-download`: this run reaches no registry at all. */
69
+ readonly noDownload?: boolean;
70
+ /** True on a CI runner, which may have a terminal and still have nobody at it. */
71
+ readonly ci?: boolean;
72
+ /** Injected so the age gate is testable without waiting seven days. */
73
+ readonly now?: () => Date;
74
+ }
75
+ interface AddResult {
76
+ /** The engine command the provider declares, when its offer was accepted. */
77
+ readonly onboard: readonly string[] | undefined;
78
+ }
79
+ declare function add(options: AddOptions): Promise<AddResult>;
80
+
81
+ /**
82
+ * The one-line edit `add` offers to `penv.config.ts`.
83
+ *
84
+ * The file is the user's, so it is scanned rather than parsed and re-emitted:
85
+ * reformatting someone's config — dropping its comments, resorting its keys — to
86
+ * change one string is not the edit that was offered. The scanner understands
87
+ * exactly one thing, the `providers` block, and answers "I do not know this
88
+ * file" for anything else rather than guessing at a rewrite.
89
+ */
90
+ /** One environment's entry in the `providers` block. */
91
+ interface ProviderEntry {
92
+ readonly environment: string;
93
+ /** The package it names today, when the entry declares a `type` string. */
94
+ readonly type: string | undefined;
95
+ }
96
+ /** Every environment the `providers` block names, in the order the file declares them. */
97
+ declare function readProviderEntries(source: string): ProviderEntry[] | undefined;
98
+ /** The same text with one environment pointed at `type`, or `undefined` if it cannot be. */
99
+ declare function setProviderType(source: string, environment: string, type: string): string | undefined;
100
+
101
+ /**
102
+ * The committed, type-only declaration an added extension contributes.
103
+ *
104
+ * The adapter lives in `$PENV_HOME` and is loaded only for an explicit provider
105
+ * operation, so the project can never import it — which would leave
106
+ * `penv.config.ts` untyped for exactly the providers it names. This closes that
107
+ * gap the only way a committed file can: by carrying the provider's own config
108
+ * declaration as text, checked to reach for nothing the project does not have.
109
+ *
110
+ * A package points at that declaration with `penv.types` in its `package.json`;
111
+ * one that ships none gets the open base shape under its own name, which is
112
+ * still enough for the `type` field to be checked against something real.
113
+ */
114
+ /** What an extension's `package.json` tells `add`, and nothing more. */
115
+ interface ExtensionPackage {
116
+ /** A path inside the package to a self-contained declaration file. */
117
+ readonly types: string | undefined;
118
+ /** The engine command that finishes setting this provider up, e.g. `cloud login`. */
119
+ readonly onboard: string | undefined;
120
+ }
121
+ /**
122
+ * The `penv` block of an installed package. Advisory, so a package without one —
123
+ * or with an unreadable `package.json` — declares nothing rather than failing an
124
+ * install that already succeeded.
125
+ */
126
+ declare function readExtensionPackage(dir: string): ExtensionPackage;
127
+ interface DeclarationSubject {
128
+ readonly name: string;
129
+ readonly version: string;
130
+ /** Recorded in the header: what npm knew about where these bytes came from. */
131
+ readonly attested: boolean;
132
+ }
133
+ /** The declaration's text: the package's own, or the open shape under its name. */
134
+ declare function renderDeclaration(subject: DeclarationSubject, shipped?: {
135
+ readonly file: string;
136
+ readonly source: string;
137
+ }): string;
138
+ /** Where one extension's declaration lives, relative to the project root, POSIX. */
139
+ declare function declarationPath(name: string): string;
140
+ interface WriteDeclarationOptions extends DeclarationSubject {
141
+ readonly root: string;
142
+ /** The installed package directory, read for the declaration it ships. */
143
+ readonly dir: string;
144
+ readonly types: string | undefined;
145
+ }
146
+ /** Writes the declaration and answers with the path a message prints. */
147
+ declare function writeDeclaration(options: WriteDeclarationOptions): string;
148
+
149
+ /**
150
+ * `$PENV_HOME` — the one directory the launcher owns.
151
+ *
152
+ * Engines and extensions are addressed by exact name and exact version, so a
153
+ * machine holds every version any of its projects pins at once and no project's
154
+ * command is ever answered by another project's bytes.
155
+ */
156
+ /** A read-only view of the process environment. */
157
+ type Environment = Readonly<Record<string, string | undefined>>;
158
+ /** The variable that moves the store off `~/.penv`. */
159
+ declare const PENV_HOME_VAR = "PENV_HOME";
160
+ /** The two things the store holds, and the directory each lives under. */
161
+ type PackageKind = "engines" | "extensions";
162
+ /** How the launcher was installed, recorded by the installer that did it. */
163
+ declare const HOME_META_FILE = "meta.json";
164
+ /** Written beside an installed package: the SSRI of the tarball it came from. */
165
+ declare const INTEGRITY_FILE = ".penv-integrity";
166
+ /** The update command for a launcher whose installer recorded nothing. */
167
+ declare const NPM_UPDATE_COMMAND = "npm install -g @penvhq/launcher";
168
+ /** The store, from the environment. `~/.penv` unless `$PENV_HOME` says otherwise. */
169
+ declare function penvHome(env: Environment): string;
170
+ /**
171
+ * Where one exact version lives.
172
+ *
173
+ * The manifest's grammar already refuses a name or a version that could climb
174
+ * out of the store, so the containment check is the second lock rather than the
175
+ * first: this function is also reached from `penv add`, where the name is
176
+ * whatever the user typed.
177
+ *
178
+ * Containment is measured against the bucket, not against `$PENV_HOME`. A name
179
+ * of `../extensions/x` stays inside the store while landing an engine among the
180
+ * extensions, and a store where the two are not separated is a store where the
181
+ * kind a caller asked for is not the kind it gets.
182
+ */
183
+ declare function packageDir(home: string, kind: PackageKind, name: string, version: string): string;
184
+ /**
185
+ * The command that updates this launcher.
186
+ *
187
+ * Advisory, so it never throws: a store with no `meta.json`, or one holding
188
+ * something unreadable, falls back to the npm form rather than turning a
189
+ * manifest-format refusal into a second failure about a metadata file.
190
+ */
191
+ declare function launcherUpdateCommand(home: string): string;
192
+
193
+ /**
194
+ * Handing the command over.
195
+ *
196
+ * The engine is a child process, not an import: it is a different version of
197
+ * penv than the launcher, and it has to be able to be. What crosses is the
198
+ * argument list exactly as typed, the three streams, the exit code, and the
199
+ * signal that ended it — nothing is parsed, rewritten, or summarized on the way.
200
+ */
201
+
202
+ interface Delegation {
203
+ /** The executable to run — node, for a JS engine entry. */
204
+ readonly command: string;
205
+ readonly args: readonly string[];
206
+ readonly cwd: string;
207
+ readonly env: Environment;
208
+ }
209
+ /** Runs the child and answers with the exit code the caller should exit with. */
210
+ type Spawner = (delegation: Delegation) => Promise<number>;
211
+ declare function nodeSpawner(): Spawner;
212
+
213
+ /**
214
+ * Turning an installed directory into something to run.
215
+ *
216
+ * The engine that ships with the launcher and the engine a project pins are the
217
+ * same kind of thing — a package directory with a `bin` — so there is one
218
+ * resolver and one spawn path, and `penv init` outside a project takes exactly
219
+ * the route `penv get` takes inside one.
220
+ */
221
+ /** The bin name the engine publishes. `penv` itself belongs to the launcher. */
222
+ declare const ENGINE_BIN = "penv-engine";
223
+ interface Engine {
224
+ readonly name: string;
225
+ readonly version: string;
226
+ readonly dir: string;
227
+ /** The JS file to hand to node, absolute. */
228
+ readonly entry: string;
229
+ }
230
+ /**
231
+ * The engine installed at `dir`, or the refusal that says it is not runnable.
232
+ *
233
+ * `bin` comes out of the package's own `package.json`, so it is checked for
234
+ * containment like every other path the launcher resolves from something it did
235
+ * not write: a `bin` of `../../x.js` names a file penv would hand to node from
236
+ * outside the package the manifest pinned.
237
+ */
238
+ declare function engineAt(dir: string, name: string, version: string): Engine;
239
+ /**
240
+ * The engine that shipped with this launcher — the one that runs `init` in a
241
+ * directory that is not a project yet.
242
+ *
243
+ * It is resolved rather than bundled: a launcher installed from npm gets the
244
+ * engine as a dependency, which is what makes it a package directory with a
245
+ * `bin` like every other engine in the store.
246
+ */
247
+ declare function bundledEngine(): Engine;
248
+
249
+ /**
250
+ * What the launcher refuses, and the one command that clears each refusal.
251
+ *
252
+ * Every one of these is a wrong-bytes or no-bytes answer to the same question —
253
+ * which penv is this project's — so they all name the package, the version, and
254
+ * a single next command. Only the two engine-pin refusals mention the
255
+ * launcher/engine split, because a launcher that cannot record which engine it
256
+ * ran is the one failure that cannot be described without it; the rest name a
257
+ * package and a command, and leave penv looking like one program.
258
+ */
259
+
260
+ /** The command that materializes everything the manifest pins. */
261
+ declare const INSTALL_COMMAND = "penv install";
262
+ /** How long a package outside the official scope must have existed. */
263
+ declare const MIN_PACKAGE_AGE_DAYS = 7;
264
+ /** The flag that overrides {@link MIN_PACKAGE_AGE_DAYS}, and only that. */
265
+ declare const TRUST_YOUNG_FLAG = "--trust-young";
266
+ /** The command was typed outside any penv project. */
267
+ declare class NoProjectError extends PenvError {
268
+ readonly name = "NoProjectError";
269
+ constructor(cwd: string);
270
+ }
271
+ /** The pinned bytes are not on this machine, and this run does not download. */
272
+ declare class PackageMissingError extends PenvError {
273
+ readonly name = "PackageMissingError";
274
+ constructor(name: string, version: string, home: string);
275
+ }
276
+ /** The bytes on this machine are not the bytes the manifest pins. */
277
+ declare class PackageCorruptError extends PenvError {
278
+ readonly name = "PackageCorruptError";
279
+ constructor(name: string, version: string, dir: string);
280
+ }
281
+ /** The download was offered and declined. */
282
+ declare class InstallDeclinedError extends PenvError {
283
+ readonly name = "InstallDeclinedError";
284
+ constructor(name: string, version: string);
285
+ }
286
+ /** The registry could not be reached, or answered with something other than the tarball. */
287
+ declare class DownloadFailedError extends PenvError {
288
+ readonly name = "DownloadFailedError";
289
+ constructor(name: string, version: string, url: string, detail: string);
290
+ }
291
+ /** The registry served bytes the manifest does not pin. Nothing is installed. */
292
+ declare class DownloadIntegrityError extends PenvError {
293
+ readonly name = "DownloadIntegrityError";
294
+ constructor(name: string, version: string, url: string);
295
+ }
296
+ /** An archive entry penv will not write to disk. */
297
+ declare class ArchiveError extends PenvError {
298
+ readonly name = "ArchiveError";
299
+ constructor(name: string, version: string, entry: string);
300
+ }
301
+ /** `penv add` with nothing to add. */
302
+ declare class AddSubjectError extends PenvError {
303
+ readonly name = "AddSubjectError";
304
+ constructor();
305
+ }
306
+ /** A package name npm would not recognise. */
307
+ declare class AddPackageNameError extends PenvError {
308
+ readonly name = "AddPackageNameError";
309
+ constructor(spec: string);
310
+ }
311
+ /** A flag `penv add` does not have. */
312
+ declare class AddFlagError extends PenvError {
313
+ readonly name = "AddFlagError";
314
+ constructor(flag: string);
315
+ }
316
+ /** `--registry` with something that is not an https origin. */
317
+ declare class AddRegistryError extends PenvError {
318
+ readonly name = "AddRegistryError";
319
+ constructor(value: string);
320
+ }
321
+ /** penv's own packages, offered by a registry that is not npmjs. */
322
+ declare class OfficialRegistryError extends PenvError {
323
+ readonly name = "OfficialRegistryError";
324
+ constructor(name: string, registry: string);
325
+ }
326
+ /** The registry could not be read at all. */
327
+ declare class RegistryUnreadableError extends PenvError {
328
+ readonly name = "RegistryUnreadableError";
329
+ constructor(name: string, url: string, detail: string);
330
+ }
331
+ /** The registry has no such package. */
332
+ declare class PackageUnknownError extends PenvError {
333
+ readonly name = "PackageUnknownError";
334
+ constructor(name: string, url: string);
335
+ }
336
+ /** The package exists; the version asked for does not. */
337
+ declare class VersionUnknownError extends PenvError {
338
+ readonly name = "VersionUnknownError";
339
+ constructor(name: string, version: string, url: string);
340
+ }
341
+ /** The registry answered, but without a fact the manifest has to record. */
342
+ declare class ReleaseIncompleteError extends PenvError {
343
+ readonly name = "ReleaseIncompleteError";
344
+ constructor(name: string, version: string, url: string, missing: string);
345
+ }
346
+ /** A third-party package younger than the age gate, with no override. */
347
+ declare class PackageTooYoungError extends PenvError {
348
+ readonly name = "PackageTooYoungError";
349
+ constructor(name: string, version: string, publishedAt: string);
350
+ }
351
+ /** `penv add` needs the registry, and `--no-download` says this run has no network. */
352
+ declare class AddNoDownloadError extends PenvError {
353
+ readonly name = "AddNoDownloadError";
354
+ constructor(name: string);
355
+ }
356
+ /**
357
+ * `penv add` on a machine with nobody at it.
358
+ *
359
+ * It is refused everywhere, not only for the packages that pay the trust
360
+ * ceremony: what `add` writes is two committed files, and a pipeline that
361
+ * rewrites the manifest it was handed is a pipeline choosing which bytes the
362
+ * project runs. `penv install` is the command CI has, and it installs exactly
363
+ * what a person already decided.
364
+ */
365
+ declare class AddNotInteractiveError extends PenvError {
366
+ readonly name = "AddNotInteractiveError";
367
+ constructor(name: string);
368
+ }
369
+ /** The trust ceremony was declined. Nothing was installed or recorded. */
370
+ declare class TrustDeclinedError extends PenvError {
371
+ readonly name = "TrustDeclinedError";
372
+ constructor(name: string, version: string);
373
+ }
374
+ /** A third-party trust block with nobody named in it. */
375
+ declare class TrustPublisherMissingError extends PenvError {
376
+ readonly name = "TrustPublisherMissingError";
377
+ constructor(name: string, version: string);
378
+ }
379
+ /** The trust block's one human field came back empty. */
380
+ declare class TrustReasonMissingError extends PenvError {
381
+ readonly name = "TrustReasonMissingError";
382
+ constructor(name: string, version: string);
383
+ }
384
+ /** `penv.types` names a file the published package does not contain. */
385
+ declare class DeclarationMissingError extends PenvError {
386
+ readonly name = "DeclarationMissingError";
387
+ constructor(name: string, file: string);
388
+ }
389
+ /** The declaration a package ships reaches for something the project does not have. */
390
+ declare class DeclarationNotSelfContainedError extends PenvError {
391
+ readonly name = "DeclarationNotSelfContainedError";
392
+ constructor(name: string, file: string, specifier: string);
393
+ }
394
+ /** A launcher built from source, asked to record which bytes it just ran. */
395
+ declare class EnginePinUnreleasedError extends PenvError {
396
+ readonly name = "EnginePinUnreleasedError";
397
+ constructor();
398
+ }
399
+ /** The pin embedded at release time and the engine beside it are different versions. */
400
+ declare class EnginePinMismatchError extends PenvError {
401
+ readonly name = "EnginePinMismatchError";
402
+ constructor(pinned: string, ran: string);
403
+ }
404
+ /** An installed directory with nothing runnable in it. */
405
+ declare class EngineEntryError extends PenvError {
406
+ readonly name = "EngineEntryError";
407
+ constructor(name: string, version: string, dir: string);
408
+ }
409
+
410
+ /**
411
+ * The one hash penv checks: npm's SSRI, over the tarball bytes.
412
+ *
413
+ * The manifest pins the same string npm recorded, so the value compared here is
414
+ * the value a reviewer approved in the diff — not a digest penv invented.
415
+ */
416
+ /** The SSRI of some bytes, in the form the manifest pins. */
417
+ declare function integrityOf(bytes: Uint8Array): string;
418
+
419
+ /**
420
+ * The launcher protocol.
421
+ *
422
+ * One question is asked on every invocation — which penv is this project's — and
423
+ * everything here is the answer to it: find the manifest, read only the format
424
+ * it declares, prove the pinned bytes are on this machine, hand the command
425
+ * over. The launcher parses `--no-download` and `--version` and nothing else;
426
+ * every other token is the engine's business and crosses untouched.
427
+ *
428
+ * Downloading is the one behavior that differs by where penv is running. CI and
429
+ * production never download during a run — they refuse and name the command that
430
+ * installs — because a production start that is also a network event is a
431
+ * production start that can fail for a reason nobody chose.
432
+ */
433
+
434
+ interface LauncherOptions {
435
+ /** The command line, minus the executable — `process.argv.slice(2)`. */
436
+ readonly argv: readonly string[];
437
+ readonly cwd: string;
438
+ readonly env: Environment;
439
+ readonly io: LauncherIo;
440
+ readonly fetcher: Fetcher;
441
+ readonly spawn: Spawner;
442
+ /** The engine that shipped with this launcher, resolved only when it is needed. */
443
+ readonly bundledEngine: () => Engine;
444
+ /** That engine's published identity, embedded at release time. */
445
+ readonly bundledPin: ManifestEngine;
446
+ }
447
+ declare function runLauncher(options: LauncherOptions): Promise<number>;
448
+
449
+ /**
450
+ * The published identity of the engine that ships with this launcher.
451
+ *
452
+ * A manifest pins bytes, and bytes are named by the SSRI npm recorded for the
453
+ * tarball — which nothing can compute from an installed directory, and which
454
+ * `init` may not go and ask for, because adoption is offline. So the launcher
455
+ * carries the answer: the release pipeline publishes `@penvhq/cli`, reads that
456
+ * integrity from the registry, writes it here, and only then publishes `penv`.
457
+ * The pin and the engine beside it describe one release or the launcher refuses
458
+ * to use either.
459
+ *
460
+ * The value checked into this repository is a placeholder that no registry could
461
+ * ever serve, and {@link assertReleasePin} is the gate that keeps it out of a
462
+ * published launcher and out of anyone's project.
463
+ */
464
+
465
+ /** Deliberately not an SSRI: nothing can install, or verify, what it names. */
466
+ declare const DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
467
+ /** The version that goes with it, equally unpublishable. */
468
+ declare const DEV_PIN_VERSION = "0.0.0-dev";
469
+ /** Rewritten by the release step, after `@penvhq/cli` is on the registry. */
470
+ declare const BUNDLED_ENGINE_PIN: ManifestEngine;
471
+ /** The release gate: a launcher built from source has nothing to pin. */
472
+ declare function assertReleasePin(pin: ManifestEngine): void;
473
+ /** The pin for the engine that just ran, or the refusal that says there is none. */
474
+ declare function releaseEnginePin(pin: ManifestEngine, ranVersion: string): ManifestEngine;
475
+
476
+ /**
477
+ * Finding the project a command was typed in.
478
+ *
479
+ * The manifest is the marker, not `penv.config.ts`: the launcher's whole job is
480
+ * to run the engine the project pins, and the manifest is the file that pins it.
481
+ * A checkout whose `node_modules` has never been installed still answers.
482
+ */
483
+ interface Project {
484
+ /** The directory holding `.penv/`. */
485
+ readonly root: string;
486
+ /** The manifest, absolute. */
487
+ readonly manifestFile: string;
488
+ }
489
+ /** The nearest project at or above `cwd`, or `undefined` outside one. */
490
+ declare function findProject(cwd: string): Project | undefined;
491
+ /**
492
+ * The project a delegated `init` or `migrate` left behind, recognised by the
493
+ * state directory rather than the manifest it does not have yet.
494
+ *
495
+ * A command that previewed and wrote nothing leaves none, which is what keeps a
496
+ * `penv migrate` typed in an ordinary directory from being handed a manifest.
497
+ */
498
+ declare function findAdoptedRoot(cwd: string): string | undefined;
499
+
500
+ /**
501
+ * What the registry says about one release.
502
+ *
503
+ * Everything `add` decides from — the exact version behind `latest`, the
504
+ * integrity of the bytes, when it was published, who published it, whether npm
505
+ * holds a provenance attestation — is metadata, so it arrives through the same
506
+ * fetcher the tarball does. One network seam, and a test suite that serves both
507
+ * from memory.
508
+ */
509
+
510
+ /** One published version, reduced to the facts a trust decision is made on. */
511
+ interface Release {
512
+ readonly name: string;
513
+ readonly version: string;
514
+ readonly integrity: string;
515
+ /** ISO 8601, as the registry's `time` map records it. */
516
+ readonly publishedAt: string;
517
+ /** The npm account credited with the publish, when the registry names one. */
518
+ readonly publisher: string | undefined;
519
+ /** Whether npm holds a provenance attestation for these exact bytes. */
520
+ readonly attested: boolean;
521
+ }
522
+ interface ReleaseQuery {
523
+ readonly name: string;
524
+ /** Absent means whatever `latest` points at today. */
525
+ readonly version?: string;
526
+ /** Only when the package comes from somewhere other than npmjs. */
527
+ readonly registry?: string;
528
+ readonly fetcher: Fetcher;
529
+ }
530
+ /** The packument's address, which an exact name can be built rather than searched. */
531
+ declare function packumentUrl(registry: string | undefined, name: string): string;
532
+ /** The one release `add` is about to decide on, or why the registry could not say. */
533
+ declare function fetchRelease(query: ReleaseQuery): Promise<Release>;
534
+
535
+ /**
536
+ * The store: what is installed, and how something absent gets installed.
537
+ *
538
+ * An installed package carries the SSRI of the tarball it came from, written
539
+ * beside it at install time, so every later run compares the manifest's pin
540
+ * against a recorded answer instead of re-hashing a directory that would never
541
+ * hash to an npm integrity anyway. Three states, and only three: the bytes the
542
+ * manifest pins, no bytes, or bytes that are not the ones pinned.
543
+ */
544
+
545
+ /** One exact thing the manifest names. */
546
+ interface Pin {
547
+ readonly name: string;
548
+ readonly version: string;
549
+ readonly integrity: string;
550
+ /** Only when the package comes from somewhere other than npmjs. */
551
+ readonly registry?: string;
552
+ }
553
+ /** Where penv looks when a pin names no registry. */
554
+ declare const DEFAULT_REGISTRY = "https://registry.npmjs.org";
555
+ type InstallState = "installed" | "absent" | "corrupt";
556
+ interface Installation {
557
+ readonly dir: string;
558
+ readonly state: InstallState;
559
+ }
560
+ /** npm's tarball address, which an exact version can be built rather than looked up. */
561
+ declare function tarballUrl(pin: Pin): string;
562
+ /** What this machine holds for one pin. */
563
+ declare function inspectInstall(home: string, kind: PackageKind, pin: Pin): Installation;
564
+ interface InstallOptions {
565
+ readonly home: string;
566
+ readonly kind: PackageKind;
567
+ readonly pin: Pin;
568
+ readonly fetcher: Fetcher;
569
+ }
570
+ /**
571
+ * Downloads one pin, verifies it, and installs it — in that order, and never a
572
+ * different one.
573
+ *
574
+ * The extraction happens in a staging directory and arrives by rename, so an
575
+ * interrupted install leaves nothing that a later run could read as installed.
576
+ */
577
+ declare function installPin(options: InstallOptions): Promise<string>;
578
+
579
+ /**
580
+ * The npm tarball reader.
581
+ *
582
+ * An npm package is a gzipped ustar archive whose every path begins `package/`,
583
+ * so this reads exactly that and refuses everything else: no symlinks, no
584
+ * hardlinks, no absolute paths, no `..`, nothing outside `package/`. The
585
+ * checksum in each header is not verified because the SSRI over the whole
586
+ * tarball already was, before a single block was read.
587
+ */
588
+ /** One regular file, at its path relative to the package root. */
589
+ interface TarEntry {
590
+ readonly path: string;
591
+ readonly bytes: Uint8Array;
592
+ }
593
+ /** The package a refusal names. */
594
+ interface ArchiveSubject {
595
+ readonly name: string;
596
+ readonly version: string;
597
+ }
598
+ /** Every regular file in an npm tarball, `package/` stripped. */
599
+ declare function readTarball(gzipped: Uint8Array, subject: ArchiveSubject): TarEntry[];
600
+
601
+ export { AddFlagError, AddNoDownloadError, AddNotInteractiveError, type AddOptions, AddPackageNameError, AddRegistryError, type AddResult, AddSubjectError, ArchiveError, type ArchiveSubject, BUNDLED_ENGINE_PIN, DEFAULT_REGISTRY, DEV_PIN_INTEGRITY, DEV_PIN_VERSION, DeclarationMissingError, DeclarationNotSelfContainedError, type DeclarationSubject, type Delegation, DownloadFailedError, DownloadIntegrityError, ENGINE_BIN, type Engine, EngineEntryError, EnginePinMismatchError, EnginePinUnreleasedError, type Environment, type ExtensionPackage, type Fetcher, HOME_META_FILE, INSTALL_COMMAND, INTEGRITY_FILE, InstallDeclinedError, type InstallOptions, type InstallState, type Installation, type LauncherIo, type LauncherOptions, MIN_PACKAGE_AGE_DAYS, NPM_UPDATE_COMMAND, NoProjectError, OfficialRegistryError, PENV_HOME_VAR, PackageCorruptError, type PackageKind, PackageMissingError, PackageTooYoungError, PackageUnknownError, type Pin, type Project, type ProviderEntry, RegistryUnreadableError, type Release, ReleaseIncompleteError, type ReleaseQuery, type Spawner, TRUST_YOUNG_FLAG, type TarEntry, TrustDeclinedError, TrustPublisherMissingError, TrustReasonMissingError, VersionUnknownError, add, assertReleasePin, bundledEngine, declarationPath, engineAt, fetchRelease, findAdoptedRoot, findProject, httpFetcher, inspectInstall, installPin, integrityOf, launcherUpdateCommand, nodeSpawner, packageDir, packumentUrl, penvHome, readExtensionPackage, readProviderEntries, readTarball, releaseEnginePin, renderDeclaration, runLauncher, setProviderType, tarballUrl, writeDeclaration };