@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.
- package/LICENSE +21 -0
- package/dist/bin.cjs +1519 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +55 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-2AF67CKY.js +1552 -0
- package/dist/chunk-2AF67CKY.js.map +1 -0
- package/dist/index.cjs +1628 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +601 -0
- package/dist/index.d.ts +601 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1628 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
AddFlagError: () => AddFlagError,
|
|
24
|
+
AddNoDownloadError: () => AddNoDownloadError,
|
|
25
|
+
AddNotInteractiveError: () => AddNotInteractiveError,
|
|
26
|
+
AddPackageNameError: () => AddPackageNameError,
|
|
27
|
+
AddRegistryError: () => AddRegistryError,
|
|
28
|
+
AddSubjectError: () => AddSubjectError,
|
|
29
|
+
ArchiveError: () => ArchiveError,
|
|
30
|
+
BUNDLED_ENGINE_PIN: () => BUNDLED_ENGINE_PIN,
|
|
31
|
+
DEFAULT_REGISTRY: () => DEFAULT_REGISTRY,
|
|
32
|
+
DEV_PIN_INTEGRITY: () => DEV_PIN_INTEGRITY,
|
|
33
|
+
DEV_PIN_VERSION: () => DEV_PIN_VERSION,
|
|
34
|
+
DeclarationMissingError: () => DeclarationMissingError,
|
|
35
|
+
DeclarationNotSelfContainedError: () => DeclarationNotSelfContainedError,
|
|
36
|
+
DownloadFailedError: () => DownloadFailedError,
|
|
37
|
+
DownloadIntegrityError: () => DownloadIntegrityError,
|
|
38
|
+
ENGINE_BIN: () => ENGINE_BIN,
|
|
39
|
+
EngineEntryError: () => EngineEntryError,
|
|
40
|
+
EnginePinMismatchError: () => EnginePinMismatchError,
|
|
41
|
+
EnginePinUnreleasedError: () => EnginePinUnreleasedError,
|
|
42
|
+
HOME_META_FILE: () => HOME_META_FILE,
|
|
43
|
+
INSTALL_COMMAND: () => INSTALL_COMMAND,
|
|
44
|
+
INTEGRITY_FILE: () => INTEGRITY_FILE,
|
|
45
|
+
InstallDeclinedError: () => InstallDeclinedError,
|
|
46
|
+
MIN_PACKAGE_AGE_DAYS: () => MIN_PACKAGE_AGE_DAYS,
|
|
47
|
+
NPM_UPDATE_COMMAND: () => NPM_UPDATE_COMMAND,
|
|
48
|
+
NoProjectError: () => NoProjectError,
|
|
49
|
+
OfficialRegistryError: () => OfficialRegistryError,
|
|
50
|
+
PENV_HOME_VAR: () => PENV_HOME_VAR,
|
|
51
|
+
PackageCorruptError: () => PackageCorruptError,
|
|
52
|
+
PackageMissingError: () => PackageMissingError,
|
|
53
|
+
PackageTooYoungError: () => PackageTooYoungError,
|
|
54
|
+
PackageUnknownError: () => PackageUnknownError,
|
|
55
|
+
RegistryUnreadableError: () => RegistryUnreadableError,
|
|
56
|
+
ReleaseIncompleteError: () => ReleaseIncompleteError,
|
|
57
|
+
TRUST_YOUNG_FLAG: () => TRUST_YOUNG_FLAG,
|
|
58
|
+
TrustDeclinedError: () => TrustDeclinedError,
|
|
59
|
+
TrustPublisherMissingError: () => TrustPublisherMissingError,
|
|
60
|
+
TrustReasonMissingError: () => TrustReasonMissingError,
|
|
61
|
+
VersionUnknownError: () => VersionUnknownError,
|
|
62
|
+
add: () => add,
|
|
63
|
+
assertReleasePin: () => assertReleasePin,
|
|
64
|
+
bundledEngine: () => bundledEngine,
|
|
65
|
+
declarationPath: () => declarationPath,
|
|
66
|
+
engineAt: () => engineAt,
|
|
67
|
+
fetchRelease: () => fetchRelease,
|
|
68
|
+
findAdoptedRoot: () => findAdoptedRoot,
|
|
69
|
+
findProject: () => findProject,
|
|
70
|
+
httpFetcher: () => httpFetcher,
|
|
71
|
+
inspectInstall: () => inspectInstall,
|
|
72
|
+
installPin: () => installPin,
|
|
73
|
+
integrityOf: () => integrityOf,
|
|
74
|
+
launcherUpdateCommand: () => launcherUpdateCommand,
|
|
75
|
+
nodeSpawner: () => nodeSpawner,
|
|
76
|
+
packageDir: () => packageDir,
|
|
77
|
+
packumentUrl: () => packumentUrl,
|
|
78
|
+
penvHome: () => penvHome,
|
|
79
|
+
readExtensionPackage: () => readExtensionPackage,
|
|
80
|
+
readProviderEntries: () => readProviderEntries,
|
|
81
|
+
readTarball: () => readTarball,
|
|
82
|
+
releaseEnginePin: () => releaseEnginePin,
|
|
83
|
+
renderDeclaration: () => renderDeclaration,
|
|
84
|
+
runLauncher: () => runLauncher,
|
|
85
|
+
setProviderType: () => setProviderType,
|
|
86
|
+
tarballUrl: () => tarballUrl,
|
|
87
|
+
writeDeclaration: () => writeDeclaration
|
|
88
|
+
});
|
|
89
|
+
module.exports = __toCommonJS(src_exports);
|
|
90
|
+
|
|
91
|
+
// src/add.ts
|
|
92
|
+
var import_node_fs4 = require("fs");
|
|
93
|
+
var import_node_path4 = require("path");
|
|
94
|
+
var import_core5 = require("@penvhq/core");
|
|
95
|
+
|
|
96
|
+
// src/config-edit.ts
|
|
97
|
+
var QUOTES = /* @__PURE__ */ new Set(['"', "'", "`"]);
|
|
98
|
+
function skipTrivia(source, index) {
|
|
99
|
+
let i = index;
|
|
100
|
+
for (; ; ) {
|
|
101
|
+
const ch = source.charAt(i);
|
|
102
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
103
|
+
i += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (ch === "/" && source.charAt(i + 1) === "/") {
|
|
107
|
+
const end = source.indexOf("\n", i);
|
|
108
|
+
i = end === -1 ? source.length : end + 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (ch === "/" && source.charAt(i + 1) === "*") {
|
|
112
|
+
const end = source.indexOf("*/", i + 2);
|
|
113
|
+
i = end === -1 ? source.length : end + 2;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function skipString(source, index) {
|
|
120
|
+
const quote = source.charAt(index);
|
|
121
|
+
let i = index + 1;
|
|
122
|
+
while (i < source.length) {
|
|
123
|
+
const ch = source.charAt(i);
|
|
124
|
+
if (ch === "\\") {
|
|
125
|
+
i += 2;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (ch === quote) {
|
|
129
|
+
return i + 1;
|
|
130
|
+
}
|
|
131
|
+
i += 1;
|
|
132
|
+
}
|
|
133
|
+
return source.length;
|
|
134
|
+
}
|
|
135
|
+
function matchBrace(source, open) {
|
|
136
|
+
let depth = 0;
|
|
137
|
+
let i = open;
|
|
138
|
+
while (i < source.length) {
|
|
139
|
+
const ch = source.charAt(i);
|
|
140
|
+
if (QUOTES.has(ch)) {
|
|
141
|
+
i = skipString(source, i);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const skipped = skipTrivia(source, i);
|
|
145
|
+
if (skipped !== i) {
|
|
146
|
+
i = skipped;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (ch === "{" || ch === "[" || ch === "(") {
|
|
150
|
+
depth += 1;
|
|
151
|
+
} else if (ch === "}" || ch === "]" || ch === ")") {
|
|
152
|
+
depth -= 1;
|
|
153
|
+
if (depth === 0) {
|
|
154
|
+
return i + 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
i += 1;
|
|
158
|
+
}
|
|
159
|
+
return -1;
|
|
160
|
+
}
|
|
161
|
+
function readKey(source, index) {
|
|
162
|
+
const ch = source.charAt(index);
|
|
163
|
+
if (ch === '"' || ch === "'") {
|
|
164
|
+
const end = skipString(source, index);
|
|
165
|
+
return { key: source.slice(index + 1, end - 1), end };
|
|
166
|
+
}
|
|
167
|
+
const match = /^[A-Za-z_$][A-Za-z0-9_$]*/.exec(source.slice(index));
|
|
168
|
+
return match === null ? void 0 : { key: match[0], end: index + match[0].length };
|
|
169
|
+
}
|
|
170
|
+
function typeSlotIn(source, open, close) {
|
|
171
|
+
let i = skipTrivia(source, open + 1);
|
|
172
|
+
while (i < close - 1) {
|
|
173
|
+
const key = readKey(source, i);
|
|
174
|
+
if (key === void 0) {
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
const colon = skipTrivia(source, key.end);
|
|
178
|
+
if (source.charAt(colon) !== ":") {
|
|
179
|
+
return void 0;
|
|
180
|
+
}
|
|
181
|
+
const valueStart = skipTrivia(source, colon + 1);
|
|
182
|
+
const ch = source.charAt(valueStart);
|
|
183
|
+
let valueEnd;
|
|
184
|
+
if (QUOTES.has(ch)) {
|
|
185
|
+
valueEnd = skipString(source, valueStart);
|
|
186
|
+
if (key.key === "type" && ch !== "`") {
|
|
187
|
+
return {
|
|
188
|
+
start: valueStart,
|
|
189
|
+
end: valueEnd,
|
|
190
|
+
value: source.slice(valueStart + 1, valueEnd - 1)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
} else if (ch === "{" || ch === "[" || ch === "(") {
|
|
194
|
+
valueEnd = matchBrace(source, valueStart);
|
|
195
|
+
if (valueEnd === -1) {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
const comma = /[,}]/.exec(source.slice(valueStart));
|
|
200
|
+
valueEnd = comma === null ? close - 1 : valueStart + comma.index;
|
|
201
|
+
}
|
|
202
|
+
i = skipTrivia(source, valueEnd);
|
|
203
|
+
if (source.charAt(i) === ",") {
|
|
204
|
+
i = skipTrivia(source, i + 1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return void 0;
|
|
208
|
+
}
|
|
209
|
+
function scan(source) {
|
|
210
|
+
const found = /(?:^|[\s{,;])providers\s*:\s*\{/.exec(source);
|
|
211
|
+
if (found === null) {
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
const open = found.index + found[0].length - 1;
|
|
215
|
+
const close = matchBrace(source, open);
|
|
216
|
+
if (close === -1) {
|
|
217
|
+
return void 0;
|
|
218
|
+
}
|
|
219
|
+
const entries = [];
|
|
220
|
+
let i = skipTrivia(source, open + 1);
|
|
221
|
+
while (i < close - 1) {
|
|
222
|
+
const key = readKey(source, i);
|
|
223
|
+
if (key === void 0) {
|
|
224
|
+
return void 0;
|
|
225
|
+
}
|
|
226
|
+
const colon = skipTrivia(source, key.end);
|
|
227
|
+
if (source.charAt(colon) !== ":") {
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
const entryOpen = skipTrivia(source, colon + 1);
|
|
231
|
+
if (source.charAt(entryOpen) !== "{") {
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
const entryClose = matchBrace(source, entryOpen);
|
|
235
|
+
if (entryClose === -1) {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
const slot = typeSlotIn(source, entryOpen, entryClose);
|
|
239
|
+
entries.push({ environment: key.key, type: slot?.value, slot });
|
|
240
|
+
i = skipTrivia(source, entryClose);
|
|
241
|
+
if (source.charAt(i) === ",") {
|
|
242
|
+
i = skipTrivia(source, i + 1);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return entries;
|
|
246
|
+
}
|
|
247
|
+
function readProviderEntries(source) {
|
|
248
|
+
return scan(source)?.map(({ environment, type }) => ({ environment, type }));
|
|
249
|
+
}
|
|
250
|
+
function setProviderType(source, environment, type) {
|
|
251
|
+
const entry = scan(source)?.find((candidate) => candidate.environment === environment);
|
|
252
|
+
if (entry?.slot === void 0) {
|
|
253
|
+
return void 0;
|
|
254
|
+
}
|
|
255
|
+
return `${source.slice(0, entry.slot.start)}${JSON.stringify(type)}${source.slice(entry.slot.end)}`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/declaration.ts
|
|
259
|
+
var import_node_fs = require("fs");
|
|
260
|
+
var import_node_path = require("path");
|
|
261
|
+
var import_core2 = require("@penvhq/core");
|
|
262
|
+
|
|
263
|
+
// src/errors.ts
|
|
264
|
+
var import_core = require("@penvhq/core");
|
|
265
|
+
var INSTALL_COMMAND = "penv install";
|
|
266
|
+
var MIN_PACKAGE_AGE_DAYS = 7;
|
|
267
|
+
var TRUST_YOUNG_FLAG = "--trust-young";
|
|
268
|
+
function addCommand(name, version, extra) {
|
|
269
|
+
const spec = version === void 0 ? name : `${name}@${version}`;
|
|
270
|
+
return `penv add ${spec}${extra === void 0 ? "" : ` ${extra}`}`;
|
|
271
|
+
}
|
|
272
|
+
var NoProjectError = class extends import_core.PenvError {
|
|
273
|
+
name = "NoProjectError";
|
|
274
|
+
constructor(cwd) {
|
|
275
|
+
super(
|
|
276
|
+
"PENV_NO_PROJECT",
|
|
277
|
+
`penv found no ${import_core.MANIFEST_PATH} in ${cwd} or any parent directory`,
|
|
278
|
+
"Run `penv init` here to adopt this project."
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var PackageMissingError = class extends import_core.PenvError {
|
|
283
|
+
name = "PackageMissingError";
|
|
284
|
+
constructor(name, version, home) {
|
|
285
|
+
super(
|
|
286
|
+
"PENV_PACKAGE_MISSING",
|
|
287
|
+
`${name} ${version} is not installed in ${home}, and this run does not download`,
|
|
288
|
+
`Run \`${INSTALL_COMMAND}\` \u2014 CI and production install the versions the manifest pins before the command that needs them.`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
var PackageCorruptError = class extends import_core.PenvError {
|
|
293
|
+
name = "PackageCorruptError";
|
|
294
|
+
constructor(name, version, dir) {
|
|
295
|
+
super(
|
|
296
|
+
"PENV_PACKAGE_CORRUPT",
|
|
297
|
+
`${name} ${version} in ${dir} is not the bytes ${import_core.MANIFEST_PATH} pins`,
|
|
298
|
+
`Delete ${dir} and run \`${INSTALL_COMMAND}\` \u2014 penv runs the exact bytes the manifest names, or nothing.`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
var InstallDeclinedError = class extends import_core.PenvError {
|
|
303
|
+
name = "InstallDeclinedError";
|
|
304
|
+
constructor(name, version) {
|
|
305
|
+
super(
|
|
306
|
+
"PENV_INSTALL_DECLINED",
|
|
307
|
+
`${name} ${version} was not downloaded`,
|
|
308
|
+
`Run \`${INSTALL_COMMAND}\` when you want penv to fetch the versions this project pins.`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var DownloadFailedError = class extends import_core.PenvError {
|
|
313
|
+
name = "DownloadFailedError";
|
|
314
|
+
constructor(name, version, url, detail) {
|
|
315
|
+
super(
|
|
316
|
+
"PENV_DOWNLOAD_FAILED",
|
|
317
|
+
`Downloading ${name} ${version} from ${url} failed: ${detail}`,
|
|
318
|
+
`Run \`${INSTALL_COMMAND}\` again when the registry is reachable.`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var DownloadIntegrityError = class extends import_core.PenvError {
|
|
323
|
+
name = "DownloadIntegrityError";
|
|
324
|
+
constructor(name, version, url) {
|
|
325
|
+
super(
|
|
326
|
+
"PENV_DOWNLOAD_INTEGRITY",
|
|
327
|
+
`${name} ${version} from ${url} is not the bytes ${import_core.MANIFEST_PATH} pins`,
|
|
328
|
+
`Check the registry \u2014 the bytes it served are not the ones the pin was reviewed against, so penv installed nothing.`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var ArchiveError = class extends import_core.PenvError {
|
|
333
|
+
name = "ArchiveError";
|
|
334
|
+
constructor(name, version, entry) {
|
|
335
|
+
super(
|
|
336
|
+
"PENV_ARCHIVE",
|
|
337
|
+
`The ${name} ${version} archive holds \`${entry}\`, which penv will not extract`,
|
|
338
|
+
"Check the registry \u2014 penv extracts regular files under `package/` and nothing else."
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var AddSubjectError = class extends import_core.PenvError {
|
|
343
|
+
name = "AddSubjectError";
|
|
344
|
+
constructor() {
|
|
345
|
+
super(
|
|
346
|
+
"PENV_ADD_SUBJECT",
|
|
347
|
+
"`penv add` names no package",
|
|
348
|
+
"Run `penv add @penvhq/provider-vault`, or any provider package name \u2014 optionally with `@<version>` to pin one other than the latest."
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
var AddPackageNameError = class extends import_core.PenvError {
|
|
353
|
+
name = "AddPackageNameError";
|
|
354
|
+
constructor(spec) {
|
|
355
|
+
super(
|
|
356
|
+
"PENV_ADD_PACKAGE_NAME",
|
|
357
|
+
`\`${spec}\` is not an npm package name`,
|
|
358
|
+
"Name the package exactly as npm does, e.g. `penv add @acme/provider-consul@1.4.2`."
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
var AddFlagError = class extends import_core.PenvError {
|
|
363
|
+
name = "AddFlagError";
|
|
364
|
+
constructor(flag) {
|
|
365
|
+
super(
|
|
366
|
+
"PENV_ADD_FLAG",
|
|
367
|
+
`\`penv add\` does not understand \`${flag}\``,
|
|
368
|
+
`Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\` or \`--registry <url>\` \u2014 those are the two it takes.`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var AddRegistryError = class extends import_core.PenvError {
|
|
373
|
+
name = "AddRegistryError";
|
|
374
|
+
constructor(value) {
|
|
375
|
+
super(
|
|
376
|
+
"PENV_ADD_REGISTRY",
|
|
377
|
+
`\`--registry ${value}\` is not an https URL`,
|
|
378
|
+
"Write the registry's origin, e.g. `--registry https://npm.acme.internal`. Over plain http the integrity check proves only that you got the bytes an attacker on the network chose."
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var OfficialRegistryError = class extends import_core.PenvError {
|
|
383
|
+
name = "OfficialRegistryError";
|
|
384
|
+
constructor(name, registry) {
|
|
385
|
+
super(
|
|
386
|
+
"PENV_OFFICIAL_REGISTRY",
|
|
387
|
+
`${name} was asked for from ${registry}, and \`${import_core.OFFICIAL_SCOPE}*\` packages come from npmjs`,
|
|
388
|
+
`Run \`${addCommand(name)}\` without \`--registry\`. The official scope is the one penv adds without a trust question, so penv will not take it from somewhere else.`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var RegistryUnreadableError = class extends import_core.PenvError {
|
|
393
|
+
name = "RegistryUnreadableError";
|
|
394
|
+
constructor(name, url, detail) {
|
|
395
|
+
super(
|
|
396
|
+
"PENV_REGISTRY_UNREADABLE",
|
|
397
|
+
`Reading ${name} from ${url} failed: ${detail}`,
|
|
398
|
+
`Run \`${addCommand(name)}\` again when the registry is reachable.`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
var PackageUnknownError = class extends import_core.PenvError {
|
|
403
|
+
name = "PackageUnknownError";
|
|
404
|
+
constructor(name, url) {
|
|
405
|
+
super(
|
|
406
|
+
"PENV_PACKAGE_UNKNOWN",
|
|
407
|
+
`${url} publishes no versions of ${name}`,
|
|
408
|
+
"Check the package name against the registry \u2014 penv adds a package that exists or nothing."
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
var VersionUnknownError = class extends import_core.PenvError {
|
|
413
|
+
name = "VersionUnknownError";
|
|
414
|
+
constructor(name, version, url) {
|
|
415
|
+
super(
|
|
416
|
+
"PENV_VERSION_UNKNOWN",
|
|
417
|
+
`${url} publishes no ${version} of ${name}`,
|
|
418
|
+
`Run \`${addCommand(name)}\` to take the version \`latest\` points at.`
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
var ReleaseIncompleteError = class extends import_core.PenvError {
|
|
423
|
+
name = "ReleaseIncompleteError";
|
|
424
|
+
constructor(name, version, url, missing) {
|
|
425
|
+
super(
|
|
426
|
+
"PENV_RELEASE_INCOMPLETE",
|
|
427
|
+
`${url} records no ${missing} for ${name} ${version}`,
|
|
428
|
+
`Check the registry \u2014 the manifest pins ${missing} for every package, so penv records what the registry states or refuses to record it at all.`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
var PackageTooYoungError = class extends import_core.PenvError {
|
|
433
|
+
name = "PackageTooYoungError";
|
|
434
|
+
constructor(name, version, publishedAt) {
|
|
435
|
+
super(
|
|
436
|
+
"PENV_PACKAGE_TOO_YOUNG",
|
|
437
|
+
`${name} ${version} was published ${publishedAt}, and penv waits ${MIN_PACKAGE_AGE_DAYS} days before adding a package outside \`${import_core.OFFICIAL_SCOPE}*\``,
|
|
438
|
+
`Run \`${addCommand(name, version, TRUST_YOUNG_FLAG)}\` to add it anyway and record why. The wait is there because a hijacked publish is usually caught within days.`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var ManifestEntriesUnreadableError = class extends import_core.PenvError {
|
|
443
|
+
name = "ManifestEntriesUnreadableError";
|
|
444
|
+
constructor(names) {
|
|
445
|
+
const one = names.length === 1;
|
|
446
|
+
super(
|
|
447
|
+
"PENV_MANIFEST_ENTRIES_UNREADABLE",
|
|
448
|
+
`${import_core.MANIFEST_PATH} holds ${one ? "an extension entry" : "extension entries"} penv cannot read: ${names.join(", ")}`,
|
|
449
|
+
one ? `Run \`${addCommand(names[0] ?? "")}\` to rewrite that entry \u2014 it resolves the package again and records what the registry states.` : `Restore it with \`git checkout ${import_core.MANIFEST_PATH}\`. Each \`penv add\` rewrites only its own entry, so more than one broken entry is not something one of them can fix.`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
var AddNoDownloadError = class extends import_core.PenvError {
|
|
454
|
+
name = "AddNoDownloadError";
|
|
455
|
+
constructor(name) {
|
|
456
|
+
super(
|
|
457
|
+
"PENV_ADD_NO_DOWNLOAD",
|
|
458
|
+
`Adding ${name} means reading the registry for the version and integrity to pin, and \`--no-download\` says this run does not`,
|
|
459
|
+
`Run \`${addCommand(name)}\` without \`--no-download\`. Nothing was fetched or written.`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
var AddNotInteractiveError = class extends import_core.PenvError {
|
|
464
|
+
name = "AddNotInteractiveError";
|
|
465
|
+
constructor(name) {
|
|
466
|
+
super(
|
|
467
|
+
"PENV_ADD_NOT_INTERACTIVE",
|
|
468
|
+
`Adding ${name} rewrites ${import_core.MANIFEST_PATH}, and this run has nobody to decide that`,
|
|
469
|
+
`Run \`${addCommand(name)}\` from a terminal and commit what it writes. In CI, run \`${INSTALL_COMMAND}\` \u2014 it installs the versions the committed manifest already pins.`
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
var TrustDeclinedError = class extends import_core.PenvError {
|
|
474
|
+
name = "TrustDeclinedError";
|
|
475
|
+
constructor(name, version) {
|
|
476
|
+
super(
|
|
477
|
+
"PENV_TRUST_DECLINED",
|
|
478
|
+
`${name} ${version} was not trusted, so penv installed and recorded nothing`,
|
|
479
|
+
`Run \`${addCommand(name, version)}\` again when you have reviewed what it does.`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
var TrustPublisherMissingError = class extends import_core.PenvError {
|
|
484
|
+
name = "TrustPublisherMissingError";
|
|
485
|
+
constructor(name, version) {
|
|
486
|
+
super(
|
|
487
|
+
"PENV_TRUST_PUBLISHER_MISSING",
|
|
488
|
+
`The publisher of ${name} ${version} was left empty, and the registry names none either`,
|
|
489
|
+
`Run \`${addCommand(name, version)}\` again and name the npm account you checked \u2014 a third-party trust block records who was trusted, not only that someone was.`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
var TrustReasonMissingError = class extends import_core.PenvError {
|
|
494
|
+
name = "TrustReasonMissingError";
|
|
495
|
+
constructor(name, version) {
|
|
496
|
+
super(
|
|
497
|
+
"PENV_TRUST_REASON_MISSING",
|
|
498
|
+
`The reason for trusting ${name} ${version} was left empty`,
|
|
499
|
+
`Run \`${addCommand(name, version)}\` again and write one line on what you checked \u2014 the next reviewer reads that line, not the diff.`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
var DeclarationMissingError = class extends import_core.PenvError {
|
|
504
|
+
name = "DeclarationMissingError";
|
|
505
|
+
constructor(name, file) {
|
|
506
|
+
super(
|
|
507
|
+
"PENV_DECLARATION_MISSING",
|
|
508
|
+
`${name} declares its types at \`${file}\`, and the published package has no such file`,
|
|
509
|
+
`Report it to ${name}. penv commits the declaration a provider ships, so it will not invent one that claims to describe this provider's configuration.`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var DeclarationNotSelfContainedError = class extends import_core.PenvError {
|
|
514
|
+
name = "DeclarationNotSelfContainedError";
|
|
515
|
+
constructor(name, file, specifier) {
|
|
516
|
+
super(
|
|
517
|
+
"PENV_DECLARATION_NOT_SELF_CONTAINED",
|
|
518
|
+
`The declaration ${name} ships at \`${file}\` imports \`${specifier}\``,
|
|
519
|
+
`Report it to ${name}. What penv commits to ${import_core.EXTENSIONS_PATH} is types and nothing else, so it can only carry a declaration that stands on its own.`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
var EnginePinUnreleasedError = class extends import_core.PenvError {
|
|
524
|
+
name = "EnginePinUnreleasedError";
|
|
525
|
+
constructor() {
|
|
526
|
+
super(
|
|
527
|
+
"PENV_ENGINE_PIN_UNRELEASED",
|
|
528
|
+
`This penv was built from source, so it carries no published integrity for ${import_core.ENGINE_PACKAGE} to write into ${import_core.MANIFEST_PATH}`,
|
|
529
|
+
"Install penv from npm with `npm install -g @penvhq/launcher` and run the command again \u2014 a released launcher ships the integrity of the engine it ships."
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
var EnginePinMismatchError = class extends import_core.PenvError {
|
|
534
|
+
name = "EnginePinMismatchError";
|
|
535
|
+
constructor(pinned, ran) {
|
|
536
|
+
super(
|
|
537
|
+
"PENV_ENGINE_PIN_MISMATCH",
|
|
538
|
+
`This penv carries the integrity of ${import_core.ENGINE_PACKAGE} ${pinned} and just ran ${ran}, so it cannot record which bytes scaffolded this project`,
|
|
539
|
+
"Reinstall the launcher with `npm install -g @penvhq/launcher` \u2014 its pin and its engine ship together."
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
var EngineEntryError = class extends import_core.PenvError {
|
|
544
|
+
name = "EngineEntryError";
|
|
545
|
+
constructor(name, version, dir) {
|
|
546
|
+
super(
|
|
547
|
+
"PENV_ENGINE_ENTRY",
|
|
548
|
+
`${name} ${version} in ${dir} declares no bin penv can run`,
|
|
549
|
+
`Delete ${dir} and run \`${INSTALL_COMMAND}\` to install it again.`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
// src/declaration.ts
|
|
555
|
+
var AUGMENTATION_TARGET = "@penvhq/core";
|
|
556
|
+
var MODULE_SPECIFIER = /(?:\bfrom|\bmodule|\bimport|\brequire)\s*\(?\s*(["'])([^"']*)\1/g;
|
|
557
|
+
function field(source, key) {
|
|
558
|
+
return typeof source === "object" && source !== null && Object.hasOwn(source, key) ? source[key] : void 0;
|
|
559
|
+
}
|
|
560
|
+
function text(value) {
|
|
561
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
562
|
+
}
|
|
563
|
+
function readExtensionPackage(dir) {
|
|
564
|
+
let parsed;
|
|
565
|
+
try {
|
|
566
|
+
parsed = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(dir, "package.json"), "utf8"));
|
|
567
|
+
} catch {
|
|
568
|
+
return { types: void 0, onboard: void 0 };
|
|
569
|
+
}
|
|
570
|
+
const penv = field(parsed, "penv");
|
|
571
|
+
return { types: text(field(penv, "types")), onboard: text(field(penv, "onboard")) };
|
|
572
|
+
}
|
|
573
|
+
function header(subject) {
|
|
574
|
+
const provenance = subject.attested ? "npm records a provenance attestation for it" : "npm records no provenance attestation for it";
|
|
575
|
+
return `// Written by \`penv add ${subject.name}\`, and committed.
|
|
576
|
+
// ${subject.name} ${subject.version} \u2014 ${provenance}.
|
|
577
|
+
//
|
|
578
|
+
// Types only: this declares the shape of the provider's \`penv.config.ts\`
|
|
579
|
+
// entry. It holds no adapter code, no credentials, and no values.
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
function openShape(name) {
|
|
583
|
+
return `import type { ProviderConfig } from "${AUGMENTATION_TARGET}";
|
|
584
|
+
|
|
585
|
+
declare module "${AUGMENTATION_TARGET}" {
|
|
586
|
+
interface ProviderConfigMap {
|
|
587
|
+
${JSON.stringify(name)}: ProviderConfig & { readonly type: ${JSON.stringify(name)} };
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
`;
|
|
591
|
+
}
|
|
592
|
+
function assertSelfContained(name, file, source) {
|
|
593
|
+
MODULE_SPECIFIER.lastIndex = 0;
|
|
594
|
+
for (; ; ) {
|
|
595
|
+
const match = MODULE_SPECIFIER.exec(source);
|
|
596
|
+
if (match === null) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const specifier = match[2] ?? "";
|
|
600
|
+
if (specifier !== AUGMENTATION_TARGET) {
|
|
601
|
+
throw new DeclarationNotSelfContainedError(name, file, specifier);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
function renderDeclaration(subject, shipped) {
|
|
606
|
+
if (shipped === void 0) {
|
|
607
|
+
return `${header(subject)}
|
|
608
|
+
${openShape(subject.name)}`;
|
|
609
|
+
}
|
|
610
|
+
assertSelfContained(subject.name, shipped.file, shipped.source);
|
|
611
|
+
const body = shipped.source.replace(/^/, "").trimEnd();
|
|
612
|
+
return `${header(subject)}
|
|
613
|
+
${body}
|
|
614
|
+
`;
|
|
615
|
+
}
|
|
616
|
+
function readShipped(name, dir, declared) {
|
|
617
|
+
const root = (0, import_node_path.resolve)(dir);
|
|
618
|
+
const file = (0, import_node_path.resolve)(root, ...declared.split("/"));
|
|
619
|
+
if (!file.startsWith(root + import_node_path.sep)) {
|
|
620
|
+
throw new DeclarationMissingError(name, declared);
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
return (0, import_node_fs.readFileSync)(file, "utf8");
|
|
624
|
+
} catch {
|
|
625
|
+
throw new DeclarationMissingError(name, declared);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function declarationPath(name) {
|
|
629
|
+
return `${import_core2.EXTENSIONS_PATH}/${name}.d.ts`;
|
|
630
|
+
}
|
|
631
|
+
function writeDeclaration(options) {
|
|
632
|
+
const relative2 = declarationPath(options.name);
|
|
633
|
+
const file = (0, import_node_path.join)(options.root, ...relative2.split("/"));
|
|
634
|
+
let shipped;
|
|
635
|
+
if (options.types !== void 0) {
|
|
636
|
+
shipped = {
|
|
637
|
+
file: options.types,
|
|
638
|
+
source: readShipped(options.name, options.dir, options.types)
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
const text4 = renderDeclaration(options, shipped);
|
|
642
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(file), { recursive: true });
|
|
643
|
+
(0, import_node_fs.writeFileSync)(file, text4);
|
|
644
|
+
return relative2;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/store.ts
|
|
648
|
+
var import_node_fs3 = require("fs");
|
|
649
|
+
var import_node_path3 = require("path");
|
|
650
|
+
|
|
651
|
+
// src/home.ts
|
|
652
|
+
var import_node_fs2 = require("fs");
|
|
653
|
+
var import_node_os = require("os");
|
|
654
|
+
var import_node_path2 = require("path");
|
|
655
|
+
var import_core3 = require("@penvhq/core");
|
|
656
|
+
var PENV_HOME_VAR = "PENV_HOME";
|
|
657
|
+
var HOME_META_FILE = "meta.json";
|
|
658
|
+
var INTEGRITY_FILE = ".penv-integrity";
|
|
659
|
+
var NPM_UPDATE_COMMAND = "npm install -g @penvhq/launcher";
|
|
660
|
+
function penvHome(env) {
|
|
661
|
+
const declared = env[PENV_HOME_VAR];
|
|
662
|
+
if (declared !== void 0 && declared.trim() !== "") {
|
|
663
|
+
return (0, import_node_path2.resolve)(declared);
|
|
664
|
+
}
|
|
665
|
+
return (0, import_node_path2.join)((0, import_node_os.homedir)(), ".penv");
|
|
666
|
+
}
|
|
667
|
+
function packageDir(home, kind, name, version) {
|
|
668
|
+
const bucket = (0, import_node_path2.resolve)(home, kind);
|
|
669
|
+
const dir = (0, import_node_path2.resolve)(bucket, ...name.split("/"), version);
|
|
670
|
+
if (!dir.startsWith(bucket + import_node_path2.sep)) {
|
|
671
|
+
throw new import_core3.PenvError(
|
|
672
|
+
"PENV_HOME_ESCAPE",
|
|
673
|
+
`\`${name}\` at \`${version}\` resolves to ${dir}, which is outside ${bucket}`,
|
|
674
|
+
"Name the package exactly as npm does, e.g. `@penvhq/provider-vault`."
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
return dir;
|
|
678
|
+
}
|
|
679
|
+
function launcherUpdateCommand(home) {
|
|
680
|
+
let meta;
|
|
681
|
+
try {
|
|
682
|
+
meta = JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(home, HOME_META_FILE), "utf8"));
|
|
683
|
+
} catch {
|
|
684
|
+
return NPM_UPDATE_COMMAND;
|
|
685
|
+
}
|
|
686
|
+
const command = meta?.updateCommand;
|
|
687
|
+
if (typeof command === "string" && command.trim() !== "") {
|
|
688
|
+
return command.trim();
|
|
689
|
+
}
|
|
690
|
+
return NPM_UPDATE_COMMAND;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/integrity.ts
|
|
694
|
+
var import_node_crypto = require("crypto");
|
|
695
|
+
function integrityOf(bytes) {
|
|
696
|
+
return `sha512-${(0, import_node_crypto.createHash)("sha512").update(bytes).digest("base64")}`;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/tar.ts
|
|
700
|
+
var import_node_zlib = require("zlib");
|
|
701
|
+
var BLOCK = 512;
|
|
702
|
+
var NAME = { start: 0, length: 100 };
|
|
703
|
+
var SIZE = { start: 124, length: 12 };
|
|
704
|
+
var TYPE_FLAG = 156;
|
|
705
|
+
var PREFIX = { start: 345, length: 155 };
|
|
706
|
+
var ROOT = "package/";
|
|
707
|
+
function text2(block, start, length) {
|
|
708
|
+
const field2 = block.subarray(start, start + length);
|
|
709
|
+
const end = field2.indexOf(0);
|
|
710
|
+
return new TextDecoder().decode(end === -1 ? field2 : field2.subarray(0, end)).trim();
|
|
711
|
+
}
|
|
712
|
+
function octal(block, start, length) {
|
|
713
|
+
const value = text2(block, start, length);
|
|
714
|
+
return value === "" ? 0 : Number.parseInt(value, 8);
|
|
715
|
+
}
|
|
716
|
+
function paxPath(data) {
|
|
717
|
+
for (const record2 of new TextDecoder().decode(data).split("\n")) {
|
|
718
|
+
const match = /^\d+ path=(.*)$/.exec(record2);
|
|
719
|
+
if (match?.[1] !== void 0) {
|
|
720
|
+
return match[1];
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
function safePath(raw, subject) {
|
|
726
|
+
if (!raw.startsWith(ROOT)) {
|
|
727
|
+
throw new ArchiveError(subject.name, subject.version, raw);
|
|
728
|
+
}
|
|
729
|
+
const path = raw.slice(ROOT.length);
|
|
730
|
+
const segments = path.split("/");
|
|
731
|
+
if (path === "" || path.includes("\\") || path.startsWith("/") || /^[A-Za-z]:/.test(path) || segments.some((segment) => segment === ".." || segment === "")) {
|
|
732
|
+
throw new ArchiveError(subject.name, subject.version, raw);
|
|
733
|
+
}
|
|
734
|
+
return path;
|
|
735
|
+
}
|
|
736
|
+
function readTarball(gzipped, subject) {
|
|
737
|
+
const archive = new Uint8Array((0, import_node_zlib.gunzipSync)(gzipped));
|
|
738
|
+
const entries = [];
|
|
739
|
+
let override;
|
|
740
|
+
for (let offset = 0; offset + BLOCK <= archive.length; offset += BLOCK) {
|
|
741
|
+
const header2 = archive.subarray(offset, offset + BLOCK);
|
|
742
|
+
const name = text2(header2, NAME.start, NAME.length);
|
|
743
|
+
if (name === "") {
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
const size = octal(header2, SIZE.start, SIZE.length);
|
|
747
|
+
const dataStart = offset + BLOCK;
|
|
748
|
+
if (!Number.isSafeInteger(size) || size < 0 || dataStart + size > archive.length) {
|
|
749
|
+
throw new ArchiveError(subject.name, subject.version, name);
|
|
750
|
+
}
|
|
751
|
+
const flag = String.fromCharCode(header2[TYPE_FLAG] ?? 0);
|
|
752
|
+
const data = archive.subarray(dataStart, dataStart + size);
|
|
753
|
+
offset += Math.ceil(size / BLOCK) * BLOCK;
|
|
754
|
+
if (flag === "x" || flag === "g") {
|
|
755
|
+
override = paxPath(data) ?? override;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
const prefix = text2(header2, PREFIX.start, PREFIX.length);
|
|
759
|
+
const raw = override ?? (prefix === "" ? name : `${prefix}/${name}`);
|
|
760
|
+
override = void 0;
|
|
761
|
+
if (flag === "5") {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (flag !== "0" && flag !== "\0") {
|
|
765
|
+
throw new ArchiveError(subject.name, subject.version, raw);
|
|
766
|
+
}
|
|
767
|
+
entries.push({ path: safePath(raw, subject), bytes: new Uint8Array(data) });
|
|
768
|
+
}
|
|
769
|
+
return entries;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// src/store.ts
|
|
773
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
774
|
+
function tarballUrl(pin) {
|
|
775
|
+
const registry = (pin.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
776
|
+
const basename = pin.name.slice(pin.name.lastIndexOf("/") + 1);
|
|
777
|
+
return `${registry}/${pin.name}/-/${basename}-${pin.version}.tgz`;
|
|
778
|
+
}
|
|
779
|
+
function inspectInstall(home, kind, pin) {
|
|
780
|
+
const dir = packageDir(home, kind, pin.name, pin.version);
|
|
781
|
+
if (!(0, import_node_fs3.existsSync)(dir)) {
|
|
782
|
+
return { dir, state: "absent" };
|
|
783
|
+
}
|
|
784
|
+
let recorded;
|
|
785
|
+
try {
|
|
786
|
+
recorded = (0, import_node_fs3.readFileSync)((0, import_node_path3.join)(dir, INTEGRITY_FILE), "utf8").trim();
|
|
787
|
+
} catch {
|
|
788
|
+
return { dir, state: "corrupt" };
|
|
789
|
+
}
|
|
790
|
+
return { dir, state: recorded === pin.integrity ? "installed" : "corrupt" };
|
|
791
|
+
}
|
|
792
|
+
async function installPin(options) {
|
|
793
|
+
const { home, kind, pin, fetcher } = options;
|
|
794
|
+
const dir = packageDir(home, kind, pin.name, pin.version);
|
|
795
|
+
const url = tarballUrl(pin);
|
|
796
|
+
let bytes;
|
|
797
|
+
try {
|
|
798
|
+
bytes = await fetcher.get(url);
|
|
799
|
+
} catch (cause) {
|
|
800
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
801
|
+
throw new DownloadFailedError(pin.name, pin.version, url, detail);
|
|
802
|
+
}
|
|
803
|
+
if (integrityOf(bytes) !== pin.integrity) {
|
|
804
|
+
throw new DownloadIntegrityError(pin.name, pin.version, url);
|
|
805
|
+
}
|
|
806
|
+
const entries = readTarball(bytes, pin);
|
|
807
|
+
const parent = (0, import_node_path3.dirname)(dir);
|
|
808
|
+
(0, import_node_fs3.mkdirSync)(parent, { recursive: true });
|
|
809
|
+
const staging = (0, import_node_fs3.mkdtempSync)((0, import_node_path3.join)(parent, `.${pin.version}-`));
|
|
810
|
+
try {
|
|
811
|
+
for (const entry of entries) {
|
|
812
|
+
const file = (0, import_node_path3.join)(staging, ...entry.path.split("/"));
|
|
813
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(file), { recursive: true });
|
|
814
|
+
(0, import_node_fs3.writeFileSync)(file, entry.bytes);
|
|
815
|
+
}
|
|
816
|
+
(0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(staging, INTEGRITY_FILE), `${pin.integrity}
|
|
817
|
+
`);
|
|
818
|
+
(0, import_node_fs3.rmSync)(dir, { recursive: true, force: true });
|
|
819
|
+
(0, import_node_fs3.renameSync)(staging, dir);
|
|
820
|
+
} finally {
|
|
821
|
+
(0, import_node_fs3.rmSync)(staging, { recursive: true, force: true });
|
|
822
|
+
}
|
|
823
|
+
return dir;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/registry.ts
|
|
827
|
+
function record(value) {
|
|
828
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
829
|
+
}
|
|
830
|
+
function at(source, key) {
|
|
831
|
+
return source !== void 0 && Object.hasOwn(source, key) ? source[key] : void 0;
|
|
832
|
+
}
|
|
833
|
+
function text3(value) {
|
|
834
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
835
|
+
}
|
|
836
|
+
function packumentUrl(registry, name) {
|
|
837
|
+
return `${(registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "")}/${name}`;
|
|
838
|
+
}
|
|
839
|
+
function publisherOf(release) {
|
|
840
|
+
const npmUser = text3(at(record(release._npmUser), "name"));
|
|
841
|
+
if (npmUser !== void 0) {
|
|
842
|
+
return npmUser;
|
|
843
|
+
}
|
|
844
|
+
const maintainers = release.maintainers;
|
|
845
|
+
if (!Array.isArray(maintainers)) {
|
|
846
|
+
return void 0;
|
|
847
|
+
}
|
|
848
|
+
return text3(at(record(maintainers[0]), "name"));
|
|
849
|
+
}
|
|
850
|
+
async function fetchRelease(query) {
|
|
851
|
+
const url = packumentUrl(query.registry, query.name);
|
|
852
|
+
let bytes;
|
|
853
|
+
try {
|
|
854
|
+
bytes = await query.fetcher.get(url);
|
|
855
|
+
} catch (cause) {
|
|
856
|
+
throw new RegistryUnreadableError(
|
|
857
|
+
query.name,
|
|
858
|
+
url,
|
|
859
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
let parsed;
|
|
863
|
+
try {
|
|
864
|
+
parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
865
|
+
} catch (cause) {
|
|
866
|
+
throw new RegistryUnreadableError(
|
|
867
|
+
query.name,
|
|
868
|
+
url,
|
|
869
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
const packument = record(parsed);
|
|
873
|
+
const versions = record(at(packument, "versions"));
|
|
874
|
+
if (versions === void 0) {
|
|
875
|
+
throw new PackageUnknownError(query.name, url);
|
|
876
|
+
}
|
|
877
|
+
const tags = record(at(packument, "dist-tags"));
|
|
878
|
+
const asked = query.version ?? text3(at(tags, "latest"));
|
|
879
|
+
if (asked === void 0) {
|
|
880
|
+
throw new PackageUnknownError(query.name, url);
|
|
881
|
+
}
|
|
882
|
+
const version = record(at(versions, asked)) === void 0 ? text3(at(tags, asked)) ?? asked : asked;
|
|
883
|
+
const release = record(at(versions, version));
|
|
884
|
+
if (release === void 0) {
|
|
885
|
+
throw new VersionUnknownError(query.name, asked, url);
|
|
886
|
+
}
|
|
887
|
+
const dist = record(at(release, "dist"));
|
|
888
|
+
const integrity = text3(at(dist, "integrity"));
|
|
889
|
+
const publishedAt = text3(at(record(at(packument, "time")), version));
|
|
890
|
+
if (integrity === void 0) {
|
|
891
|
+
throw new ReleaseIncompleteError(query.name, version, url, "integrity");
|
|
892
|
+
}
|
|
893
|
+
if (publishedAt === void 0 || Number.isNaN(Date.parse(publishedAt))) {
|
|
894
|
+
throw new ReleaseIncompleteError(query.name, version, url, "publish time");
|
|
895
|
+
}
|
|
896
|
+
return {
|
|
897
|
+
name: query.name,
|
|
898
|
+
version,
|
|
899
|
+
integrity,
|
|
900
|
+
publishedAt,
|
|
901
|
+
publisher: publisherOf(release),
|
|
902
|
+
attested: record(at(dist, "attestations")) !== void 0
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/repair.ts
|
|
907
|
+
var import_core4 = require("@penvhq/core");
|
|
908
|
+
function isPlainObject(value) {
|
|
909
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
910
|
+
}
|
|
911
|
+
function parseWith(base, extensions) {
|
|
912
|
+
return (0, import_core4.parseManifest)(JSON.stringify({ ...base, extensions }));
|
|
913
|
+
}
|
|
914
|
+
function readManifestForRepair(text4) {
|
|
915
|
+
let parsed;
|
|
916
|
+
try {
|
|
917
|
+
parsed = JSON.parse(text4);
|
|
918
|
+
} catch {
|
|
919
|
+
return { manifest: (0, import_core4.parseManifest)(text4), broken: [] };
|
|
920
|
+
}
|
|
921
|
+
const root = isPlainObject(parsed) ? parsed : void 0;
|
|
922
|
+
const declared = root?.extensions;
|
|
923
|
+
if (root === void 0 || !isPlainObject(declared)) {
|
|
924
|
+
return { manifest: (0, import_core4.parseManifest)(text4), broken: [] };
|
|
925
|
+
}
|
|
926
|
+
const { extensions: _, ...base } = root;
|
|
927
|
+
parseWith(base, {});
|
|
928
|
+
const kept = {};
|
|
929
|
+
const broken = [];
|
|
930
|
+
for (const [name, entry] of Object.entries(declared)) {
|
|
931
|
+
try {
|
|
932
|
+
parseWith(base, { [name]: entry });
|
|
933
|
+
kept[name] = entry;
|
|
934
|
+
} catch {
|
|
935
|
+
broken.push(name);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return { manifest: parseWith(base, kept), broken: broken.sort() };
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// src/add.ts
|
|
942
|
+
var PACKAGE_NAME = /^(?:@[a-z0-9~-][a-z0-9._~-]*\/)?[a-z0-9~-][a-z0-9._~-]*$/;
|
|
943
|
+
var REGISTRY_FLAG = "--registry";
|
|
944
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
945
|
+
function normalizeRegistry(raw) {
|
|
946
|
+
let url;
|
|
947
|
+
try {
|
|
948
|
+
url = new URL(raw);
|
|
949
|
+
} catch {
|
|
950
|
+
throw new AddRegistryError(raw);
|
|
951
|
+
}
|
|
952
|
+
if (url.protocol !== "https:") {
|
|
953
|
+
throw new AddRegistryError(raw);
|
|
954
|
+
}
|
|
955
|
+
return url.origin === new URL(DEFAULT_REGISTRY).origin ? void 0 : raw.replace(/\/+$/, "");
|
|
956
|
+
}
|
|
957
|
+
function splitSpec(spec) {
|
|
958
|
+
const at2 = spec.lastIndexOf("@");
|
|
959
|
+
if (at2 <= 0) {
|
|
960
|
+
return { name: spec, version: void 0 };
|
|
961
|
+
}
|
|
962
|
+
return { name: spec.slice(0, at2), version: spec.slice(at2 + 1) };
|
|
963
|
+
}
|
|
964
|
+
function parseRequest(argv) {
|
|
965
|
+
let spec;
|
|
966
|
+
let registry;
|
|
967
|
+
let trustYoung = false;
|
|
968
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
969
|
+
const token = argv[index] ?? "";
|
|
970
|
+
if (token === TRUST_YOUNG_FLAG) {
|
|
971
|
+
trustYoung = true;
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
if (token === REGISTRY_FLAG) {
|
|
975
|
+
const value = argv[index + 1];
|
|
976
|
+
if (value === void 0 || value.startsWith("-")) {
|
|
977
|
+
throw new AddFlagError(REGISTRY_FLAG);
|
|
978
|
+
}
|
|
979
|
+
registry = normalizeRegistry(value);
|
|
980
|
+
index += 1;
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if (token.startsWith("-")) {
|
|
984
|
+
throw new AddFlagError(token);
|
|
985
|
+
}
|
|
986
|
+
if (spec !== void 0) {
|
|
987
|
+
throw new AddSubjectError();
|
|
988
|
+
}
|
|
989
|
+
spec = token;
|
|
990
|
+
}
|
|
991
|
+
if (spec === void 0 || spec === "") {
|
|
992
|
+
throw new AddSubjectError();
|
|
993
|
+
}
|
|
994
|
+
const { name, version } = splitSpec(spec);
|
|
995
|
+
if (!PACKAGE_NAME.test(name) || version === "") {
|
|
996
|
+
throw new AddPackageNameError(spec);
|
|
997
|
+
}
|
|
998
|
+
return { name, version, registry, trustYoung };
|
|
999
|
+
}
|
|
1000
|
+
function tierOf(request) {
|
|
1001
|
+
if (request.name.startsWith(import_core5.OFFICIAL_SCOPE)) {
|
|
1002
|
+
if (request.registry !== void 0) {
|
|
1003
|
+
throw new OfficialRegistryError(request.name, request.registry);
|
|
1004
|
+
}
|
|
1005
|
+
return "official";
|
|
1006
|
+
}
|
|
1007
|
+
return request.registry === void 0 ? "third-party" : "private";
|
|
1008
|
+
}
|
|
1009
|
+
function showRelease(io, release, registry) {
|
|
1010
|
+
io.out(` publisher ${release.publisher ?? "not stated by the registry"}`);
|
|
1011
|
+
io.out(` published ${release.publishedAt}`);
|
|
1012
|
+
io.out(` integrity ${release.integrity}`);
|
|
1013
|
+
if (registry !== void 0) {
|
|
1014
|
+
io.out(` registry ${registry}`);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
async function askReason(io, release) {
|
|
1018
|
+
const reason = (await io.ask("Why do you trust it? One line, for the next reviewer.")).trim();
|
|
1019
|
+
if (reason === "") {
|
|
1020
|
+
throw new TrustReasonMissingError(release.name, release.version);
|
|
1021
|
+
}
|
|
1022
|
+
return reason;
|
|
1023
|
+
}
|
|
1024
|
+
async function trustFor(tier, release, request, io, now) {
|
|
1025
|
+
if (tier === "official") {
|
|
1026
|
+
return void 0;
|
|
1027
|
+
}
|
|
1028
|
+
if (tier === "third-party") {
|
|
1029
|
+
const age = now.getTime() - Date.parse(release.publishedAt);
|
|
1030
|
+
if (age < MIN_PACKAGE_AGE_DAYS * DAY_MS && !request.trustYoung) {
|
|
1031
|
+
throw new PackageTooYoungError(release.name, release.version, release.publishedAt);
|
|
1032
|
+
}
|
|
1033
|
+
io.out(
|
|
1034
|
+
`${release.name} ${release.version} is outside \`${import_core5.OFFICIAL_SCOPE}*\`, so the manifest records who you trusted and why.`
|
|
1035
|
+
);
|
|
1036
|
+
showRelease(io, release, void 0);
|
|
1037
|
+
if (!await io.confirm(`Trust ${release.name} ${release.version}?`)) {
|
|
1038
|
+
throw new TrustDeclinedError(release.name, release.version);
|
|
1039
|
+
}
|
|
1040
|
+
const publisher = release.publisher ?? (await io.ask("Who publishes it? The npm account name.")).trim();
|
|
1041
|
+
if (publisher === "") {
|
|
1042
|
+
throw new TrustPublisherMissingError(release.name, release.version);
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
tier: "third-party",
|
|
1046
|
+
publisher,
|
|
1047
|
+
publishedAt: release.publishedAt,
|
|
1048
|
+
acknowledgedAt: now.toISOString(),
|
|
1049
|
+
reason: await askReason(io, release)
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
io.out(
|
|
1053
|
+
`${release.name} ${release.version} comes from a private registry. The manifest records the registry and your acknowledgement; your \`.npmrc\` keeps the credentials.`
|
|
1054
|
+
);
|
|
1055
|
+
showRelease(io, release, request.registry);
|
|
1056
|
+
if (!await io.confirm(`Trust ${release.name} ${release.version}?`)) {
|
|
1057
|
+
throw new TrustDeclinedError(release.name, release.version);
|
|
1058
|
+
}
|
|
1059
|
+
return {
|
|
1060
|
+
tier: "private",
|
|
1061
|
+
acknowledgedAt: now.toISOString(),
|
|
1062
|
+
reason: await askReason(io, release)
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
function recordExtension(manifestFile, name, entry) {
|
|
1066
|
+
const { manifest, broken } = readManifestForRepair((0, import_node_fs4.readFileSync)(manifestFile, "utf8"));
|
|
1067
|
+
const others = broken.filter((other) => other !== name);
|
|
1068
|
+
if (others.length > 0) {
|
|
1069
|
+
throw new ManifestEntriesUnreadableError(others);
|
|
1070
|
+
}
|
|
1071
|
+
const next = {
|
|
1072
|
+
...manifest,
|
|
1073
|
+
extensions: { ...manifest.extensions, [name]: entry }
|
|
1074
|
+
};
|
|
1075
|
+
return (0, import_core5.serializeManifest)(next);
|
|
1076
|
+
}
|
|
1077
|
+
async function offerConfigEdit(options, name) {
|
|
1078
|
+
const { io, root } = options;
|
|
1079
|
+
const configFile = (0, import_core5.findConfigFile)(root);
|
|
1080
|
+
const line = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
|
|
1081
|
+
if (configFile === void 0) {
|
|
1082
|
+
io.out(line);
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const shown = (0, import_node_path4.relative)(root, configFile).split(import_node_path4.sep).join("/");
|
|
1086
|
+
let current = (0, import_node_fs4.readFileSync)(configFile, "utf8");
|
|
1087
|
+
const entries = readProviderEntries(current);
|
|
1088
|
+
if (entries === void 0 || entries.length === 0) {
|
|
1089
|
+
io.out(line);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (entries.every((entry) => entry.type === name)) {
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
if (!io.interactive) {
|
|
1096
|
+
io.out(line);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
for (const entry of entries) {
|
|
1100
|
+
if (entry.type === name) {
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
if (!await io.confirm(`Point \`${entry.environment}\` at ${name} in ${shown}?`)) {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
const next = setProviderType(current, entry.environment, name);
|
|
1107
|
+
if (next === void 0) {
|
|
1108
|
+
io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${entry.environment}\` in ${shown}.`);
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
current = next;
|
|
1112
|
+
(0, import_node_fs4.writeFileSync)(configFile, current);
|
|
1113
|
+
io.out(`\u2713 ${shown} points \`${entry.environment}\` at ${name}`);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
async function offerOnboarding(io, name, onboard) {
|
|
1117
|
+
if (onboard === void 0) {
|
|
1118
|
+
return void 0;
|
|
1119
|
+
}
|
|
1120
|
+
const args = onboard.split(/\s+/).filter((token) => token !== "");
|
|
1121
|
+
if (args.length === 0) {
|
|
1122
|
+
return void 0;
|
|
1123
|
+
}
|
|
1124
|
+
const command = `penv ${args.join(" ")}`;
|
|
1125
|
+
if (io.interactive && await io.confirm(`Run \`${command}\` now?`)) {
|
|
1126
|
+
return args;
|
|
1127
|
+
}
|
|
1128
|
+
io.out(`Run \`${command}\` to finish setting ${name} up.`);
|
|
1129
|
+
return void 0;
|
|
1130
|
+
}
|
|
1131
|
+
async function add(options) {
|
|
1132
|
+
const { io, fetcher, home, root, manifestFile } = options;
|
|
1133
|
+
const request = parseRequest(options.argv);
|
|
1134
|
+
const tier = tierOf(request);
|
|
1135
|
+
const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
1136
|
+
if (options.noDownload === true) {
|
|
1137
|
+
throw new AddNoDownloadError(request.name);
|
|
1138
|
+
}
|
|
1139
|
+
if (options.ci === true || !io.interactive) {
|
|
1140
|
+
throw new AddNotInteractiveError(request.name);
|
|
1141
|
+
}
|
|
1142
|
+
const release = await fetchRelease({
|
|
1143
|
+
name: request.name,
|
|
1144
|
+
...request.version === void 0 ? {} : { version: request.version },
|
|
1145
|
+
...request.registry === void 0 ? {} : { registry: request.registry },
|
|
1146
|
+
fetcher
|
|
1147
|
+
});
|
|
1148
|
+
const trust = await trustFor(tier, release, request, io, now);
|
|
1149
|
+
const entry = {
|
|
1150
|
+
version: release.version,
|
|
1151
|
+
integrity: release.integrity,
|
|
1152
|
+
...request.registry === void 0 ? {} : { registry: request.registry },
|
|
1153
|
+
...trust === void 0 ? {} : { trust }
|
|
1154
|
+
};
|
|
1155
|
+
const manifestText = recordExtension(manifestFile, release.name, entry);
|
|
1156
|
+
const dir = await installPin({
|
|
1157
|
+
home,
|
|
1158
|
+
kind: "extensions",
|
|
1159
|
+
pin: {
|
|
1160
|
+
name: release.name,
|
|
1161
|
+
version: release.version,
|
|
1162
|
+
integrity: release.integrity,
|
|
1163
|
+
...request.registry === void 0 ? {} : { registry: request.registry }
|
|
1164
|
+
},
|
|
1165
|
+
fetcher
|
|
1166
|
+
});
|
|
1167
|
+
const installed = readExtensionPackage(dir);
|
|
1168
|
+
const declaration = writeDeclaration({
|
|
1169
|
+
root,
|
|
1170
|
+
dir,
|
|
1171
|
+
name: release.name,
|
|
1172
|
+
version: release.version,
|
|
1173
|
+
attested: release.attested,
|
|
1174
|
+
types: installed.types
|
|
1175
|
+
});
|
|
1176
|
+
(0, import_node_fs4.writeFileSync)(manifestFile, manifestText);
|
|
1177
|
+
const provenance = release.attested ? "npm records a provenance attestation" : "npm records no provenance attestation";
|
|
1178
|
+
io.out(`\u2713 ${release.name} ${release.version} installed \u2014 ${provenance}`);
|
|
1179
|
+
io.out(`\u2713 ${import_core5.MANIFEST_PATH} pins it`);
|
|
1180
|
+
io.out(`\u2713 ${declaration} declares its config type`);
|
|
1181
|
+
await offerConfigEdit(options, release.name);
|
|
1182
|
+
return { onboard: await offerOnboarding(io, release.name, installed.onboard) };
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/delegate.ts
|
|
1186
|
+
var import_node_child_process = require("child_process");
|
|
1187
|
+
var import_node_os2 = require("os");
|
|
1188
|
+
var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
|
|
1189
|
+
function signalNumber(signal) {
|
|
1190
|
+
const signals = import_node_os2.constants.signals;
|
|
1191
|
+
return signals[signal] ?? 0;
|
|
1192
|
+
}
|
|
1193
|
+
function nodeSpawner() {
|
|
1194
|
+
return (delegation) => new Promise((settle, fail) => {
|
|
1195
|
+
const child = (0, import_node_child_process.spawn)(delegation.command, [...delegation.args], {
|
|
1196
|
+
cwd: delegation.cwd,
|
|
1197
|
+
env: { ...delegation.env },
|
|
1198
|
+
stdio: "inherit"
|
|
1199
|
+
});
|
|
1200
|
+
const handlers = FORWARDED.map((signal) => {
|
|
1201
|
+
const handler = () => {
|
|
1202
|
+
child.kill(signal);
|
|
1203
|
+
};
|
|
1204
|
+
process.on(signal, handler);
|
|
1205
|
+
return { signal, handler };
|
|
1206
|
+
});
|
|
1207
|
+
const release = () => {
|
|
1208
|
+
for (const { signal, handler } of handlers) {
|
|
1209
|
+
process.off(signal, handler);
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
child.on("error", (error) => {
|
|
1213
|
+
release();
|
|
1214
|
+
fail(error);
|
|
1215
|
+
});
|
|
1216
|
+
child.on("exit", (code, signal) => {
|
|
1217
|
+
release();
|
|
1218
|
+
if (signal !== null) {
|
|
1219
|
+
process.kill(process.pid, signal);
|
|
1220
|
+
settle(128 + signalNumber(signal));
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
settle(code ?? 0);
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// src/engine.ts
|
|
1229
|
+
var import_node_fs5 = require("fs");
|
|
1230
|
+
var import_node_module = require("module");
|
|
1231
|
+
var import_node_path5 = require("path");
|
|
1232
|
+
var import_core6 = require("@penvhq/core");
|
|
1233
|
+
var import_meta = {};
|
|
1234
|
+
var ENGINE_BIN = "penv-engine";
|
|
1235
|
+
function readPackageManifest(dir) {
|
|
1236
|
+
try {
|
|
1237
|
+
return JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path5.join)(dir, "package.json"), "utf8"));
|
|
1238
|
+
} catch {
|
|
1239
|
+
return void 0;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
function binPath(bin) {
|
|
1243
|
+
if (typeof bin === "string") {
|
|
1244
|
+
return bin;
|
|
1245
|
+
}
|
|
1246
|
+
if (typeof bin !== "object" || bin === null) {
|
|
1247
|
+
return void 0;
|
|
1248
|
+
}
|
|
1249
|
+
const entries = Object.entries(bin).filter(
|
|
1250
|
+
(entry) => typeof entry[1] === "string"
|
|
1251
|
+
);
|
|
1252
|
+
const named = entries.find(([name]) => name === ENGINE_BIN);
|
|
1253
|
+
const chosen = named ?? (entries.length === 1 ? entries[0] : void 0);
|
|
1254
|
+
return chosen?.[1];
|
|
1255
|
+
}
|
|
1256
|
+
function engineAt(dir, name, version) {
|
|
1257
|
+
const manifest = readPackageManifest(dir);
|
|
1258
|
+
const bin = manifest === void 0 ? void 0 : binPath(manifest.bin);
|
|
1259
|
+
const root = (0, import_node_path5.resolve)(dir);
|
|
1260
|
+
const entry = bin === void 0 ? void 0 : (0, import_node_path5.resolve)(root, bin);
|
|
1261
|
+
if (entry === void 0 || !entry.startsWith(root + import_node_path5.sep) || !(0, import_node_fs5.existsSync)(entry)) {
|
|
1262
|
+
throw new EngineEntryError(name, version, dir);
|
|
1263
|
+
}
|
|
1264
|
+
return { name, version, dir, entry };
|
|
1265
|
+
}
|
|
1266
|
+
function bundledEngine() {
|
|
1267
|
+
const require2 = (0, import_node_module.createRequire)(import_meta.url);
|
|
1268
|
+
let manifestFile;
|
|
1269
|
+
try {
|
|
1270
|
+
manifestFile = require2.resolve(`${import_core6.ENGINE_PACKAGE}/package.json`);
|
|
1271
|
+
} catch {
|
|
1272
|
+
throw new import_core6.PenvError(
|
|
1273
|
+
"PENV_NO_BUNDLED_ENGINE",
|
|
1274
|
+
`This penv installation is missing ${import_core6.ENGINE_PACKAGE}, the engine it runs \`init\` with`,
|
|
1275
|
+
"Reinstall the launcher with `npm install -g @penvhq/launcher`."
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
const dir = (0, import_node_path5.dirname)(manifestFile);
|
|
1279
|
+
const version = readPackageManifest(dir)?.version;
|
|
1280
|
+
return engineAt(dir, import_core6.ENGINE_PACKAGE, typeof version === "string" ? version : "unknown");
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// src/fetcher.ts
|
|
1284
|
+
function httpFetcher() {
|
|
1285
|
+
return {
|
|
1286
|
+
async get(url) {
|
|
1287
|
+
const response = await fetch(url);
|
|
1288
|
+
if (!response.ok) {
|
|
1289
|
+
throw new Error(`the registry answered ${response.status} ${response.statusText}`);
|
|
1290
|
+
}
|
|
1291
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// src/launcher.ts
|
|
1297
|
+
var import_node_fs7 = require("fs");
|
|
1298
|
+
var import_node_path7 = require("path");
|
|
1299
|
+
var import_core9 = require("@penvhq/core");
|
|
1300
|
+
|
|
1301
|
+
// src/pins.ts
|
|
1302
|
+
var import_core7 = require("@penvhq/core");
|
|
1303
|
+
var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
|
|
1304
|
+
var DEV_PIN_VERSION = "0.0.0-dev";
|
|
1305
|
+
var BUNDLED_ENGINE_PIN = {
|
|
1306
|
+
package: import_core7.ENGINE_PACKAGE,
|
|
1307
|
+
version: DEV_PIN_VERSION,
|
|
1308
|
+
integrity: DEV_PIN_INTEGRITY
|
|
1309
|
+
};
|
|
1310
|
+
function assertReleasePin(pin) {
|
|
1311
|
+
if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
|
|
1312
|
+
throw new EnginePinUnreleasedError();
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
function releaseEnginePin(pin, ranVersion) {
|
|
1316
|
+
assertReleasePin(pin);
|
|
1317
|
+
if (pin.version !== ranVersion) {
|
|
1318
|
+
throw new EnginePinMismatchError(pin.version, ranVersion);
|
|
1319
|
+
}
|
|
1320
|
+
return pin;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// src/project.ts
|
|
1324
|
+
var import_node_fs6 = require("fs");
|
|
1325
|
+
var import_node_path6 = require("path");
|
|
1326
|
+
var import_core8 = require("@penvhq/core");
|
|
1327
|
+
var MANIFEST_SEGMENTS = import_core8.MANIFEST_PATH.split("/");
|
|
1328
|
+
function findUp(cwd, matches) {
|
|
1329
|
+
let dir = (0, import_node_path6.resolve)(cwd);
|
|
1330
|
+
for (; ; ) {
|
|
1331
|
+
if (matches(dir)) {
|
|
1332
|
+
return dir;
|
|
1333
|
+
}
|
|
1334
|
+
const parent = (0, import_node_path6.dirname)(dir);
|
|
1335
|
+
if (parent === dir) {
|
|
1336
|
+
return void 0;
|
|
1337
|
+
}
|
|
1338
|
+
dir = parent;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function findProject(cwd) {
|
|
1342
|
+
const root = findUp(cwd, (dir) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(dir, ...MANIFEST_SEGMENTS)));
|
|
1343
|
+
return root === void 0 ? void 0 : { root, manifestFile: (0, import_node_path6.join)(root, ...MANIFEST_SEGMENTS) };
|
|
1344
|
+
}
|
|
1345
|
+
function findAdoptedRoot(cwd) {
|
|
1346
|
+
return findUp(cwd, (dir) => (0, import_node_fs6.existsSync)((0, import_core8.stateDir)(dir)));
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// src/launcher.ts
|
|
1350
|
+
var INSTALL = "install";
|
|
1351
|
+
var ADD = "add";
|
|
1352
|
+
var VERSION_FLAGS = /* @__PURE__ */ new Set(["--version", "-v"]);
|
|
1353
|
+
var HELP_FLAGS = /* @__PURE__ */ new Set(["--help", "-h"]);
|
|
1354
|
+
var NO_DOWNLOAD = "--no-download";
|
|
1355
|
+
var ADOPTS = /* @__PURE__ */ new Set(["init", "migrate"]);
|
|
1356
|
+
function splitLauncherFlags(argv) {
|
|
1357
|
+
let index = 0;
|
|
1358
|
+
let noDownload = false;
|
|
1359
|
+
while (argv[index] === NO_DOWNLOAD) {
|
|
1360
|
+
noDownload = true;
|
|
1361
|
+
index += 1;
|
|
1362
|
+
}
|
|
1363
|
+
return { noDownload, forwarded: argv.slice(index) };
|
|
1364
|
+
}
|
|
1365
|
+
function invokedCommand(argv) {
|
|
1366
|
+
const parts = argv.map((token) => /\s/.test(token) ? JSON.stringify(token) : token);
|
|
1367
|
+
return ["penv", ...parts].join(" ");
|
|
1368
|
+
}
|
|
1369
|
+
function isCi(env) {
|
|
1370
|
+
const ci = env.CI;
|
|
1371
|
+
return ci !== void 0 && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
|
|
1372
|
+
}
|
|
1373
|
+
function enginePinOf(manifest) {
|
|
1374
|
+
return {
|
|
1375
|
+
name: manifest.engine.package,
|
|
1376
|
+
version: manifest.engine.version,
|
|
1377
|
+
integrity: manifest.engine.integrity
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
function extensionPinsOf(manifest) {
|
|
1381
|
+
return Object.entries(manifest.extensions).map(([name, entry]) => ({
|
|
1382
|
+
name,
|
|
1383
|
+
version: entry.version,
|
|
1384
|
+
integrity: entry.integrity,
|
|
1385
|
+
...entry.registry === void 0 ? {} : { registry: entry.registry }
|
|
1386
|
+
}));
|
|
1387
|
+
}
|
|
1388
|
+
function pinsOf(manifest) {
|
|
1389
|
+
return [
|
|
1390
|
+
{ kind: "engines", pin: enginePinOf(manifest) },
|
|
1391
|
+
...extensionPinsOf(manifest).map((pin) => ({ kind: "extensions", pin }))
|
|
1392
|
+
];
|
|
1393
|
+
}
|
|
1394
|
+
function reportable(error, home, argv) {
|
|
1395
|
+
return error instanceof import_core9.UnsupportedManifestFormatError ? error.withLauncherUpdate({
|
|
1396
|
+
updateCommand: launcherUpdateCommand(home),
|
|
1397
|
+
invokedCommand: invokedCommand(argv)
|
|
1398
|
+
}) : error;
|
|
1399
|
+
}
|
|
1400
|
+
function readManifest(manifestFile, home, argv) {
|
|
1401
|
+
try {
|
|
1402
|
+
return (0, import_core9.parseManifest)((0, import_node_fs7.readFileSync)(manifestFile, "utf8"));
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
throw reportable(error, home, argv);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
function readManifestToRepair(manifestFile, home, argv) {
|
|
1408
|
+
try {
|
|
1409
|
+
return readManifestForRepair((0, import_node_fs7.readFileSync)(manifestFile, "utf8"));
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
throw reportable(error, home, argv);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
function report(error, io) {
|
|
1415
|
+
if (error instanceof import_core9.PenvError && error.remedy !== void 0) {
|
|
1416
|
+
const suffix = `
|
|
1417
|
+
${error.remedy}`;
|
|
1418
|
+
const message = error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
|
|
1419
|
+
io.err(`\u2717 ${message}`);
|
|
1420
|
+
io.err(` \u2192 ${error.remedy}`);
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
io.err(`\u2717 ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
1424
|
+
}
|
|
1425
|
+
async function runLauncher(options) {
|
|
1426
|
+
try {
|
|
1427
|
+
return await launch(options);
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
report(error, options.io);
|
|
1430
|
+
return 1;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
async function launch(options) {
|
|
1434
|
+
const { argv, cwd, env, io } = options;
|
|
1435
|
+
const { noDownload, forwarded } = splitLauncherFlags(argv);
|
|
1436
|
+
const first = forwarded[0];
|
|
1437
|
+
const home = penvHome(env);
|
|
1438
|
+
const project = findProject(cwd);
|
|
1439
|
+
if (project === void 0) {
|
|
1440
|
+
if (first !== void 0 && VERSION_FLAGS.has(first)) {
|
|
1441
|
+
io.out(`penv ${options.bundledEngine().version}`);
|
|
1442
|
+
return 0;
|
|
1443
|
+
}
|
|
1444
|
+
if (first === void 0 || HELP_FLAGS.has(first)) {
|
|
1445
|
+
return delegate(options, options.bundledEngine(), forwarded, home, cwd);
|
|
1446
|
+
}
|
|
1447
|
+
if (ADOPTS.has(first)) {
|
|
1448
|
+
return adopt(options, forwarded, home, cwd);
|
|
1449
|
+
}
|
|
1450
|
+
throw new NoProjectError(cwd);
|
|
1451
|
+
}
|
|
1452
|
+
if (first === INSTALL) {
|
|
1453
|
+
const { manifest: manifest2, broken } = readManifestToRepair(project.manifestFile, home, argv);
|
|
1454
|
+
return install(options, pinsOf(manifest2), home, broken);
|
|
1455
|
+
}
|
|
1456
|
+
if (first === ADD) {
|
|
1457
|
+
return addExtension(options, project, home, forwarded.slice(1), noDownload);
|
|
1458
|
+
}
|
|
1459
|
+
const manifest = readManifest(project.manifestFile, home, argv);
|
|
1460
|
+
if (first !== void 0 && VERSION_FLAGS.has(first)) {
|
|
1461
|
+
io.out(`penv ${manifest.engine.version}`);
|
|
1462
|
+
return 0;
|
|
1463
|
+
}
|
|
1464
|
+
const enginePin = enginePinOf(manifest);
|
|
1465
|
+
const engineDir = await ensure(options, "engines", enginePin, home, noDownload);
|
|
1466
|
+
for (const pin of extensionPinsOf(manifest)) {
|
|
1467
|
+
await ensure(options, "extensions", pin, home, noDownload);
|
|
1468
|
+
}
|
|
1469
|
+
const engine = engineAt(engineDir, enginePin.name, enginePin.version);
|
|
1470
|
+
return delegate(options, engine, forwarded, home, cwd);
|
|
1471
|
+
}
|
|
1472
|
+
async function adopt(options, forwarded, home, cwd) {
|
|
1473
|
+
const engine = options.bundledEngine();
|
|
1474
|
+
const code = await delegate(options, engine, forwarded, home, cwd);
|
|
1475
|
+
if (code !== 0) {
|
|
1476
|
+
return code;
|
|
1477
|
+
}
|
|
1478
|
+
const root = findAdoptedRoot(cwd);
|
|
1479
|
+
if (root === void 0) {
|
|
1480
|
+
return 0;
|
|
1481
|
+
}
|
|
1482
|
+
const manifestFile = (0, import_node_path7.join)(root, ...import_core9.MANIFEST_PATH.split("/"));
|
|
1483
|
+
if ((0, import_node_fs7.existsSync)(manifestFile)) {
|
|
1484
|
+
return 0;
|
|
1485
|
+
}
|
|
1486
|
+
const pin = releaseEnginePin(options.bundledPin, engine.version);
|
|
1487
|
+
(0, import_node_fs7.writeFileSync)(
|
|
1488
|
+
manifestFile,
|
|
1489
|
+
(0, import_core9.serializeManifest)({ format: import_core9.MANIFEST_FORMAT, engine: pin, extensions: {} })
|
|
1490
|
+
);
|
|
1491
|
+
options.io.out(`\u2713 ${import_core9.MANIFEST_PATH} pins ${pin.package} ${pin.version}`);
|
|
1492
|
+
return 0;
|
|
1493
|
+
}
|
|
1494
|
+
async function ensure(options, kind, pin, home, noDownload) {
|
|
1495
|
+
const { io, env, fetcher } = options;
|
|
1496
|
+
const { dir, state } = inspectInstall(home, kind, pin);
|
|
1497
|
+
if (state === "installed") {
|
|
1498
|
+
return dir;
|
|
1499
|
+
}
|
|
1500
|
+
if (state === "corrupt") {
|
|
1501
|
+
throw new PackageCorruptError(pin.name, pin.version, dir);
|
|
1502
|
+
}
|
|
1503
|
+
if (noDownload || isCi(env) || !io.interactive) {
|
|
1504
|
+
throw new PackageMissingError(pin.name, pin.version, home);
|
|
1505
|
+
}
|
|
1506
|
+
const consented = await io.confirm(
|
|
1507
|
+
`penv needs ${pin.name} ${pin.version} for this project. Download and verify it now?`
|
|
1508
|
+
);
|
|
1509
|
+
if (!consented) {
|
|
1510
|
+
throw new InstallDeclinedError(pin.name, pin.version);
|
|
1511
|
+
}
|
|
1512
|
+
return installPin({ home, kind, pin, fetcher });
|
|
1513
|
+
}
|
|
1514
|
+
async function install(options, pins, home, broken) {
|
|
1515
|
+
for (const { kind, pin } of pins) {
|
|
1516
|
+
const { dir, state } = inspectInstall(home, kind, pin);
|
|
1517
|
+
if (state === "corrupt") {
|
|
1518
|
+
throw new PackageCorruptError(pin.name, pin.version, dir);
|
|
1519
|
+
}
|
|
1520
|
+
if (state === "installed") {
|
|
1521
|
+
options.io.out(`\u2713 ${pin.name} ${pin.version} already installed`);
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
await installPin({ home, kind, pin, fetcher: options.fetcher });
|
|
1525
|
+
options.io.out(`\u2713 ${pin.name} ${pin.version} installed`);
|
|
1526
|
+
}
|
|
1527
|
+
if (broken.length > 0) {
|
|
1528
|
+
report(new ManifestEntriesUnreadableError(broken), options.io);
|
|
1529
|
+
return 1;
|
|
1530
|
+
}
|
|
1531
|
+
return 0;
|
|
1532
|
+
}
|
|
1533
|
+
async function addExtension(options, project, home, argv, noDownload) {
|
|
1534
|
+
const { onboard } = await add({
|
|
1535
|
+
argv,
|
|
1536
|
+
root: project.root,
|
|
1537
|
+
manifestFile: project.manifestFile,
|
|
1538
|
+
home,
|
|
1539
|
+
io: options.io,
|
|
1540
|
+
fetcher: options.fetcher,
|
|
1541
|
+
noDownload,
|
|
1542
|
+
ci: isCi(options.env)
|
|
1543
|
+
});
|
|
1544
|
+
if (onboard === void 0) {
|
|
1545
|
+
return 0;
|
|
1546
|
+
}
|
|
1547
|
+
const enginePin = enginePinOf(readManifest(project.manifestFile, home, options.argv));
|
|
1548
|
+
const dir = await ensure(options, "engines", enginePin, home, noDownload);
|
|
1549
|
+
const engine = engineAt(dir, enginePin.name, enginePin.version);
|
|
1550
|
+
return delegate(options, engine, onboard, home, options.cwd);
|
|
1551
|
+
}
|
|
1552
|
+
function delegate(options, engine, forwarded, home, cwd) {
|
|
1553
|
+
return options.spawn({
|
|
1554
|
+
command: process.execPath,
|
|
1555
|
+
args: [engine.entry, ...forwarded],
|
|
1556
|
+
cwd,
|
|
1557
|
+
env: { ...options.env, [PENV_HOME_VAR]: home }
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1561
|
+
0 && (module.exports = {
|
|
1562
|
+
AddFlagError,
|
|
1563
|
+
AddNoDownloadError,
|
|
1564
|
+
AddNotInteractiveError,
|
|
1565
|
+
AddPackageNameError,
|
|
1566
|
+
AddRegistryError,
|
|
1567
|
+
AddSubjectError,
|
|
1568
|
+
ArchiveError,
|
|
1569
|
+
BUNDLED_ENGINE_PIN,
|
|
1570
|
+
DEFAULT_REGISTRY,
|
|
1571
|
+
DEV_PIN_INTEGRITY,
|
|
1572
|
+
DEV_PIN_VERSION,
|
|
1573
|
+
DeclarationMissingError,
|
|
1574
|
+
DeclarationNotSelfContainedError,
|
|
1575
|
+
DownloadFailedError,
|
|
1576
|
+
DownloadIntegrityError,
|
|
1577
|
+
ENGINE_BIN,
|
|
1578
|
+
EngineEntryError,
|
|
1579
|
+
EnginePinMismatchError,
|
|
1580
|
+
EnginePinUnreleasedError,
|
|
1581
|
+
HOME_META_FILE,
|
|
1582
|
+
INSTALL_COMMAND,
|
|
1583
|
+
INTEGRITY_FILE,
|
|
1584
|
+
InstallDeclinedError,
|
|
1585
|
+
MIN_PACKAGE_AGE_DAYS,
|
|
1586
|
+
NPM_UPDATE_COMMAND,
|
|
1587
|
+
NoProjectError,
|
|
1588
|
+
OfficialRegistryError,
|
|
1589
|
+
PENV_HOME_VAR,
|
|
1590
|
+
PackageCorruptError,
|
|
1591
|
+
PackageMissingError,
|
|
1592
|
+
PackageTooYoungError,
|
|
1593
|
+
PackageUnknownError,
|
|
1594
|
+
RegistryUnreadableError,
|
|
1595
|
+
ReleaseIncompleteError,
|
|
1596
|
+
TRUST_YOUNG_FLAG,
|
|
1597
|
+
TrustDeclinedError,
|
|
1598
|
+
TrustPublisherMissingError,
|
|
1599
|
+
TrustReasonMissingError,
|
|
1600
|
+
VersionUnknownError,
|
|
1601
|
+
add,
|
|
1602
|
+
assertReleasePin,
|
|
1603
|
+
bundledEngine,
|
|
1604
|
+
declarationPath,
|
|
1605
|
+
engineAt,
|
|
1606
|
+
fetchRelease,
|
|
1607
|
+
findAdoptedRoot,
|
|
1608
|
+
findProject,
|
|
1609
|
+
httpFetcher,
|
|
1610
|
+
inspectInstall,
|
|
1611
|
+
installPin,
|
|
1612
|
+
integrityOf,
|
|
1613
|
+
launcherUpdateCommand,
|
|
1614
|
+
nodeSpawner,
|
|
1615
|
+
packageDir,
|
|
1616
|
+
packumentUrl,
|
|
1617
|
+
penvHome,
|
|
1618
|
+
readExtensionPackage,
|
|
1619
|
+
readProviderEntries,
|
|
1620
|
+
readTarball,
|
|
1621
|
+
releaseEnginePin,
|
|
1622
|
+
renderDeclaration,
|
|
1623
|
+
runLauncher,
|
|
1624
|
+
setProviderType,
|
|
1625
|
+
tarballUrl,
|
|
1626
|
+
writeDeclaration
|
|
1627
|
+
});
|
|
1628
|
+
//# sourceMappingURL=index.cjs.map
|