@n-seiji/nuthatch 0.1.5 → 0.2.1
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/README.ja.md +31 -21
- package/README.md +31 -21
- package/dist/clean-rn2476my.js +217 -0
- package/dist/cli-26d2drt6.js +30 -0
- package/dist/cli-77yanq37.js +21 -0
- package/dist/cli-9n04gsq1.js +108 -0
- package/dist/cli-a56b2ar0.js +20 -0
- package/dist/cli-b0tk5wcx.js +86 -0
- package/dist/cli-btqjpf1q.js +35 -0
- package/dist/cli-dbzx9nzg.js +40 -0
- package/dist/cli-fhta3tr8.js +126 -0
- package/dist/cli-hz0r7zw8.js +194 -0
- package/dist/cli-ph0gp56k.js +54 -0
- package/dist/cli-pick-yjd4qpd9.js +1444 -0
- package/dist/cli-s5qcjcwk.js +25 -0
- package/dist/cli-sre95k9x.js +87 -0
- package/dist/cli-update-x0r4b4xk.js +639 -0
- package/dist/cli-wcnyhk4w.js +124 -0
- package/dist/cli-y8vsqre6.js +97 -0
- package/dist/cli.js +245 -5868
- package/{shell/init.zsh → dist/init-167d1bbs.js} +22 -4
- package/dist/jump-1zkraat1.js +108 -0
- package/dist/ls-3hyjf60d.js +34 -0
- package/dist/render-ls-x4hpnsvy.js +149 -0
- package/dist/rm-eg3j8ae1.js +7 -0
- package/dist/root-49vcb8x2.js +7 -0
- package/package.json +4 -6
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRunningAsCompiledBinary,
|
|
3
|
+
wantsJson,
|
|
4
|
+
reportFatalError,
|
|
5
|
+
VERSION
|
|
6
|
+
} from "./cli-sre95k9x.js";
|
|
7
|
+
import {
|
|
8
|
+
messageOf
|
|
9
|
+
} from "./cli-ph0gp56k.js";
|
|
10
|
+
import {
|
|
11
|
+
reportResult
|
|
12
|
+
} from "./cli-dbzx9nzg.js";
|
|
13
|
+
import {
|
|
14
|
+
EXIT_GENERAL_ERROR,
|
|
15
|
+
EXIT_USAGE_ERROR,
|
|
16
|
+
EXIT_SAFE_REJECTION,
|
|
17
|
+
ok,
|
|
18
|
+
fail
|
|
19
|
+
} from "./cli-77yanq37.js";
|
|
20
|
+
import {
|
|
21
|
+
parentDir
|
|
22
|
+
} from "./cli-s5qcjcwk.js";
|
|
23
|
+
|
|
24
|
+
// src/domain/install-method.ts
|
|
25
|
+
var RELEASE_ASSETS = new Map([
|
|
26
|
+
["darwin-arm64", "hop-darwin-arm64"],
|
|
27
|
+
["darwin-x64", "hop-darwin-x64"],
|
|
28
|
+
["linux-x64", "hop-linux-x64"]
|
|
29
|
+
]);
|
|
30
|
+
var MANAGED_TREES = [
|
|
31
|
+
"/Cellar/",
|
|
32
|
+
"/Caskroom/",
|
|
33
|
+
"/nix/store/",
|
|
34
|
+
"/aquaproj-aqua/",
|
|
35
|
+
"/aqua/pkgs/",
|
|
36
|
+
"/.proto/",
|
|
37
|
+
"/installs/"
|
|
38
|
+
];
|
|
39
|
+
var MISE_TREE = "/mise/installs/";
|
|
40
|
+
var NODE_MODULES = "/node_modules/";
|
|
41
|
+
var detectInstallMethod = (facts) => {
|
|
42
|
+
const path = facts.compiled ? facts.executablePath : facts.scriptPath;
|
|
43
|
+
if (path === null) {
|
|
44
|
+
return { kind: "other" };
|
|
45
|
+
}
|
|
46
|
+
if (path.includes(MISE_TREE)) {
|
|
47
|
+
return { kind: "mise" };
|
|
48
|
+
}
|
|
49
|
+
if (!facts.compiled) {
|
|
50
|
+
return { kind: path.includes(NODE_MODULES) ? "package-manager" : "other" };
|
|
51
|
+
}
|
|
52
|
+
const assetName = RELEASE_ASSETS.get(`${facts.platform}-${facts.arch}`);
|
|
53
|
+
if (assetName === undefined || MANAGED_TREES.some((tree) => path.includes(tree))) {
|
|
54
|
+
return { kind: "other" };
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
kind: "standalone",
|
|
58
|
+
binaryPath: path,
|
|
59
|
+
installDir: parentDir(path),
|
|
60
|
+
assetName
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
var suggestedCommand = (kind) => {
|
|
64
|
+
if (kind === "mise") {
|
|
65
|
+
return ["mise", "upgrade"];
|
|
66
|
+
}
|
|
67
|
+
return kind === "package-manager" ? ["npm", "i", "-g", "@n-seiji/nuthatch@latest"] : null;
|
|
68
|
+
};
|
|
69
|
+
var updateHint = (kind) => {
|
|
70
|
+
const command = suggestedCommand(kind)?.join(" ");
|
|
71
|
+
if (kind === "mise") {
|
|
72
|
+
return `Update with mise: ${command}`;
|
|
73
|
+
}
|
|
74
|
+
return kind === "package-manager" ? `Update with your package manager, e.g. ${command}` : "Update hop with the tool you installed it with.";
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/domain/self-update.ts
|
|
78
|
+
var GITHUB_REPO = "n-seiji/nuthatch";
|
|
79
|
+
var GITHUB_LATEST_RELEASE_URL = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
80
|
+
var releaseAssetUrl = (version, assetName) => `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${assetName}`;
|
|
81
|
+
var VERSION_PATTERN = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/u;
|
|
82
|
+
var TAG_PATTERN = /^v(?<version>\d+\.\d+\.\d+)$/u;
|
|
83
|
+
var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/iu;
|
|
84
|
+
var parseVersion = (text) => {
|
|
85
|
+
const groups = VERSION_PATTERN.exec(text)?.groups;
|
|
86
|
+
if (groups?.major === undefined || groups.minor === undefined || groups.patch === undefined) {
|
|
87
|
+
throw new Error(`unrecognized version "${text}" (expected x.y.z)`);
|
|
88
|
+
}
|
|
89
|
+
return [Number(groups.major), Number(groups.minor), Number(groups.patch)];
|
|
90
|
+
};
|
|
91
|
+
var compareVersions = (a, b) => {
|
|
92
|
+
const [aMajor, aMinor, aPatch] = parseVersion(a);
|
|
93
|
+
const [bMajor, bMinor, bPatch] = parseVersion(b);
|
|
94
|
+
return aMajor - bMajor || aMinor - bMinor || aPatch - bPatch;
|
|
95
|
+
};
|
|
96
|
+
var versionFromTag = (tag) => {
|
|
97
|
+
const version = TAG_PATTERN.exec(tag)?.groups?.version;
|
|
98
|
+
if (version === undefined) {
|
|
99
|
+
throw new Error(`unrecognized release tag "${tag}" (expected vX.Y.Z)`);
|
|
100
|
+
}
|
|
101
|
+
return version;
|
|
102
|
+
};
|
|
103
|
+
var parseChecksumFile = (text, assetName) => {
|
|
104
|
+
const [digest, file] = text.trim().split(/\s+/u);
|
|
105
|
+
if (digest === undefined || !SHA256_HEX_PATTERN.test(digest)) {
|
|
106
|
+
return { ok: false, reason: "is malformed" };
|
|
107
|
+
}
|
|
108
|
+
const named = file?.replace(/^\*/u, "");
|
|
109
|
+
if (named !== undefined && named !== assetName && !named.endsWith(`/${assetName}`)) {
|
|
110
|
+
return { ok: false, reason: `is for "${named}", not ${assetName}` };
|
|
111
|
+
}
|
|
112
|
+
return { ok: true, digest: digest.toLowerCase() };
|
|
113
|
+
};
|
|
114
|
+
var isWriteDenied = (error) => error instanceof Error && ("code" in error) && (error.code === "EACCES" || error.code === "EPERM" || error.code === "EROFS");
|
|
115
|
+
|
|
116
|
+
// src/commands/self-update-attempt.ts
|
|
117
|
+
var attempt = async (run) => {
|
|
118
|
+
try {
|
|
119
|
+
return { ok: true, value: await run() };
|
|
120
|
+
} catch (error) {
|
|
121
|
+
return { ok: false, error };
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/commands/self-update-standalone.ts
|
|
126
|
+
var checksumRejection = (port, assetName, checksumText, bytes) => {
|
|
127
|
+
const expected = parseChecksumFile(checksumText, assetName);
|
|
128
|
+
if (!expected.ok) {
|
|
129
|
+
return fail(EXIT_SAFE_REJECTION, `The checksum file ${assetName}.sha256 ${expected.reason}; refusing to install an unverified binary.`);
|
|
130
|
+
}
|
|
131
|
+
const actual = port.sha256Hex(bytes);
|
|
132
|
+
if (actual !== expected.digest) {
|
|
133
|
+
return fail(EXIT_SAFE_REJECTION, `Checksum mismatch for ${assetName} (expected ${expected.digest}, got ${actual}); refusing to install it. Nothing was changed.`);
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
};
|
|
137
|
+
var replaceFailureMessage = (method, error) => isWriteDenied(error) ? `Cannot write ${method.installDir}; reinstall hop somewhere writable (install.sh uses ~/.local/bin).` : `Failed to replace ${method.binaryPath}: ${messageOf(error)}`;
|
|
138
|
+
var replaceStandalone = async (port, term, method, base) => {
|
|
139
|
+
const { assetName, binaryPath } = method;
|
|
140
|
+
const checksumAsset = `${assetName}.sha256`;
|
|
141
|
+
const writable = await attempt(() => port.assertWritableDir(method.installDir));
|
|
142
|
+
if (!writable.ok) {
|
|
143
|
+
return fail(EXIT_GENERAL_ERROR, replaceFailureMessage(method, writable.error));
|
|
144
|
+
}
|
|
145
|
+
term.logStderr(`Downloading ${assetName} v${base.latest}...`);
|
|
146
|
+
const checksum = await attempt(() => port.downloadReleaseText(base.latest, checksumAsset));
|
|
147
|
+
if (!checksum.ok) {
|
|
148
|
+
return fail(EXIT_GENERAL_ERROR, `Failed to download ${checksumAsset}: ${messageOf(checksum.error)}`);
|
|
149
|
+
}
|
|
150
|
+
const binary = await attempt(() => port.downloadReleaseAsset(base.latest, assetName));
|
|
151
|
+
if (!binary.ok) {
|
|
152
|
+
return fail(EXIT_GENERAL_ERROR, `Failed to download ${assetName}: ${messageOf(binary.error)}`);
|
|
153
|
+
}
|
|
154
|
+
const rejection = checksumRejection(port, assetName, checksum.value, binary.value);
|
|
155
|
+
if (rejection !== null) {
|
|
156
|
+
return rejection;
|
|
157
|
+
}
|
|
158
|
+
term.logStderr(`Checksum verified. Replacing ${binaryPath}...`);
|
|
159
|
+
const replaced = await attempt(() => port.replaceExecutable(binaryPath, binary.value));
|
|
160
|
+
if (!replaced.ok) {
|
|
161
|
+
return fail(EXIT_GENERAL_ERROR, replaceFailureMessage(method, replaced.error));
|
|
162
|
+
}
|
|
163
|
+
term.logStderr(`Updated hop ${base.current} -> ${base.latest}.`);
|
|
164
|
+
return ok({ data: { ...base, action: "replaced" } });
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/commands/self-update.ts
|
|
168
|
+
var selfUpdate = async (port, term, options) => {
|
|
169
|
+
const facts = await attempt(() => port.installFacts());
|
|
170
|
+
if (!facts.ok) {
|
|
171
|
+
return fail(EXIT_GENERAL_ERROR, `Cannot update hop: could not tell how it was installed (${messageOf(facts.error)}).`);
|
|
172
|
+
}
|
|
173
|
+
const method = detectInstallMethod(facts.value);
|
|
174
|
+
if (method.kind !== "standalone" && !options.check) {
|
|
175
|
+
return fail(EXIT_GENERAL_ERROR, updateHint(method.kind));
|
|
176
|
+
}
|
|
177
|
+
term.logStderr("Checking for the latest release...");
|
|
178
|
+
const release = await attempt(async () => {
|
|
179
|
+
const latest = await port.latestGithubVersion();
|
|
180
|
+
return {
|
|
181
|
+
latest,
|
|
182
|
+
updateAvailable: compareVersions(latest, options.currentVersion) > 0
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
if (!release.ok) {
|
|
186
|
+
return fail(EXIT_GENERAL_ERROR, `Failed to check for the latest release: ${messageOf(release.error)}`);
|
|
187
|
+
}
|
|
188
|
+
const base = {
|
|
189
|
+
current: options.currentVersion,
|
|
190
|
+
latest: release.value.latest,
|
|
191
|
+
updateAvailable: release.value.updateAvailable,
|
|
192
|
+
method: method.kind,
|
|
193
|
+
action: "none",
|
|
194
|
+
command: null
|
|
195
|
+
};
|
|
196
|
+
if (!base.updateAvailable) {
|
|
197
|
+
term.logStderr(`hop ${base.current} is up to date (latest release: ${base.latest}).`);
|
|
198
|
+
return ok({ data: base });
|
|
199
|
+
}
|
|
200
|
+
const available = `Update available: ${base.current} -> ${base.latest}.`;
|
|
201
|
+
if (method.kind !== "standalone") {
|
|
202
|
+
term.logStderr(`${available} ${updateHint(method.kind)}`);
|
|
203
|
+
const command = suggestedCommand(method.kind);
|
|
204
|
+
return ok({
|
|
205
|
+
data: { ...base, command: command === null ? null : [...command] }
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (options.check) {
|
|
209
|
+
term.logStderr(`${available} "hop --update" replaces ${method.binaryPath}.`);
|
|
210
|
+
return ok({ data: base });
|
|
211
|
+
}
|
|
212
|
+
return replaceStandalone(port, term, method, base);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/infra/install-facts.ts
|
|
216
|
+
import { realpath } from "node:fs/promises";
|
|
217
|
+
var realpathOrSelf = async (path) => {
|
|
218
|
+
try {
|
|
219
|
+
return await realpath(path);
|
|
220
|
+
} catch {
|
|
221
|
+
return path;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
var readInstallFacts = async (source) => ({
|
|
225
|
+
compiled: source.compiled,
|
|
226
|
+
executablePath: source.compiled ? await realpathOrSelf(source.execPath) : null,
|
|
227
|
+
scriptPath: source.compiled || source.argv1 === undefined ? null : await realpathOrSelf(source.argv1),
|
|
228
|
+
platform: source.platform,
|
|
229
|
+
arch: source.arch
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// src/infra/release-binary.ts
|
|
233
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
234
|
+
import { constants } from "node:fs";
|
|
235
|
+
import { access, open, realpath as realpath2, rename, rm } from "node:fs/promises";
|
|
236
|
+
import { basename, dirname, join } from "node:path";
|
|
237
|
+
var EXECUTABLE_MODE = 493;
|
|
238
|
+
var TEMP_SUFFIX_BYTES = 6;
|
|
239
|
+
var sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
240
|
+
var removeQuietly = async (path) => {
|
|
241
|
+
try {
|
|
242
|
+
await rm(path, { force: true });
|
|
243
|
+
} catch {}
|
|
244
|
+
};
|
|
245
|
+
var writeDurably = async (path, bytes) => {
|
|
246
|
+
const handle = await open(path, "wx", EXECUTABLE_MODE);
|
|
247
|
+
try {
|
|
248
|
+
await handle.writeFile(bytes);
|
|
249
|
+
await handle.chmod(EXECUTABLE_MODE);
|
|
250
|
+
await handle.sync();
|
|
251
|
+
} finally {
|
|
252
|
+
await handle.close();
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var replaceExecutable = async (path, bytes) => {
|
|
256
|
+
const target = await realpath2(path);
|
|
257
|
+
const suffix = randomBytes(TEMP_SUFFIX_BYTES).toString("hex");
|
|
258
|
+
const temp = join(dirname(target), `.${basename(target)}.update-${suffix}`);
|
|
259
|
+
try {
|
|
260
|
+
await writeDurably(temp, bytes);
|
|
261
|
+
await rename(temp, target);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
await removeQuietly(temp);
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var assertWritableDir = (dir) => access(dir, constants.W_OK | constants.X_OK);
|
|
268
|
+
|
|
269
|
+
// node_modules/valibot/dist/index.mjs
|
|
270
|
+
var store$4;
|
|
271
|
+
var DEFAULT_CONFIG = {
|
|
272
|
+
lang: undefined,
|
|
273
|
+
message: undefined,
|
|
274
|
+
abortEarly: undefined,
|
|
275
|
+
abortPipeEarly: undefined
|
|
276
|
+
};
|
|
277
|
+
function getGlobalConfig(config$1) {
|
|
278
|
+
if (!config$1 && !store$4)
|
|
279
|
+
return DEFAULT_CONFIG;
|
|
280
|
+
return {
|
|
281
|
+
lang: config$1?.lang ?? store$4?.lang,
|
|
282
|
+
message: config$1?.message,
|
|
283
|
+
abortEarly: config$1?.abortEarly ?? store$4?.abortEarly,
|
|
284
|
+
abortPipeEarly: config$1?.abortPipeEarly ?? store$4?.abortPipeEarly
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
var store$3;
|
|
288
|
+
function getGlobalMessage(lang) {
|
|
289
|
+
return store$3?.get(lang);
|
|
290
|
+
}
|
|
291
|
+
var store$2;
|
|
292
|
+
function getSchemaMessage(lang) {
|
|
293
|
+
return store$2?.get(lang);
|
|
294
|
+
}
|
|
295
|
+
var store$1;
|
|
296
|
+
function getSpecificMessage(reference, lang) {
|
|
297
|
+
return store$1?.get(reference)?.get(lang);
|
|
298
|
+
}
|
|
299
|
+
function _stringify(input) {
|
|
300
|
+
const type = typeof input;
|
|
301
|
+
if (type === "string")
|
|
302
|
+
return `"${input}"`;
|
|
303
|
+
if (type === "number" || type === "bigint" || type === "boolean")
|
|
304
|
+
return `${input}`;
|
|
305
|
+
if (type === "object" || type === "function")
|
|
306
|
+
return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
|
|
307
|
+
return type;
|
|
308
|
+
}
|
|
309
|
+
function _addIssue(context, label, dataset, config$1, other) {
|
|
310
|
+
const input = other && "input" in other ? other.input : dataset.value;
|
|
311
|
+
const expected = other?.expected ?? context.expects ?? null;
|
|
312
|
+
const received = other?.received ?? /* @__PURE__ */ _stringify(input);
|
|
313
|
+
const issue = {
|
|
314
|
+
kind: context.kind,
|
|
315
|
+
type: context.type,
|
|
316
|
+
input,
|
|
317
|
+
expected,
|
|
318
|
+
received,
|
|
319
|
+
message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
|
|
320
|
+
requirement: context.requirement,
|
|
321
|
+
path: other?.path,
|
|
322
|
+
issues: other?.issues,
|
|
323
|
+
lang: config$1.lang,
|
|
324
|
+
abortEarly: config$1.abortEarly,
|
|
325
|
+
abortPipeEarly: config$1.abortPipeEarly
|
|
326
|
+
};
|
|
327
|
+
const isSchema = context.kind === "schema";
|
|
328
|
+
const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage(issue.lang);
|
|
329
|
+
if (message$1 !== undefined)
|
|
330
|
+
issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
|
|
331
|
+
if (isSchema)
|
|
332
|
+
dataset.typed = false;
|
|
333
|
+
if (dataset.issues)
|
|
334
|
+
dataset.issues.push(issue);
|
|
335
|
+
else
|
|
336
|
+
dataset.issues = [issue];
|
|
337
|
+
}
|
|
338
|
+
var _standardCache = /* @__PURE__ */ new WeakMap;
|
|
339
|
+
function _getStandardProps(context) {
|
|
340
|
+
let cached = _standardCache.get(context);
|
|
341
|
+
if (!cached) {
|
|
342
|
+
cached = {
|
|
343
|
+
version: 1,
|
|
344
|
+
vendor: "valibot",
|
|
345
|
+
validate(value$1) {
|
|
346
|
+
return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig());
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
_standardCache.set(context, cached);
|
|
350
|
+
}
|
|
351
|
+
return cached;
|
|
352
|
+
}
|
|
353
|
+
function getFallback(schema, dataset, config$1) {
|
|
354
|
+
return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
|
|
355
|
+
}
|
|
356
|
+
function getDefault(schema, dataset, config$1) {
|
|
357
|
+
return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
|
|
358
|
+
}
|
|
359
|
+
function object(entries$1, message$1) {
|
|
360
|
+
return {
|
|
361
|
+
kind: "schema",
|
|
362
|
+
type: "object",
|
|
363
|
+
reference: object,
|
|
364
|
+
expects: "Object",
|
|
365
|
+
async: false,
|
|
366
|
+
entries: entries$1,
|
|
367
|
+
message: message$1,
|
|
368
|
+
get "~standard"() {
|
|
369
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
370
|
+
},
|
|
371
|
+
"~run"(dataset, config$1) {
|
|
372
|
+
const input = dataset.value;
|
|
373
|
+
if (input && typeof input === "object") {
|
|
374
|
+
dataset.typed = true;
|
|
375
|
+
dataset.value = {};
|
|
376
|
+
for (const key in this.entries) {
|
|
377
|
+
const valueSchema = this.entries[key];
|
|
378
|
+
if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== undefined) {
|
|
379
|
+
const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault(valueSchema);
|
|
380
|
+
const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
|
|
381
|
+
if (valueDataset.issues) {
|
|
382
|
+
const pathItem = {
|
|
383
|
+
type: "object",
|
|
384
|
+
origin: "value",
|
|
385
|
+
input,
|
|
386
|
+
key,
|
|
387
|
+
value: value$1
|
|
388
|
+
};
|
|
389
|
+
for (const issue of valueDataset.issues) {
|
|
390
|
+
if (issue.path)
|
|
391
|
+
issue.path.unshift(pathItem);
|
|
392
|
+
else
|
|
393
|
+
issue.path = [pathItem];
|
|
394
|
+
dataset.issues?.push(issue);
|
|
395
|
+
}
|
|
396
|
+
if (!dataset.issues)
|
|
397
|
+
dataset.issues = valueDataset.issues;
|
|
398
|
+
if (config$1.abortEarly) {
|
|
399
|
+
dataset.typed = false;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (!valueDataset.typed)
|
|
404
|
+
dataset.typed = false;
|
|
405
|
+
dataset.value[key] = valueDataset.value;
|
|
406
|
+
} else if (valueSchema.fallback !== undefined)
|
|
407
|
+
dataset.value[key] = /* @__PURE__ */ getFallback(valueSchema);
|
|
408
|
+
else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
|
|
409
|
+
_addIssue(this, "key", dataset, config$1, {
|
|
410
|
+
input: undefined,
|
|
411
|
+
expected: `"${key}"`,
|
|
412
|
+
path: [{
|
|
413
|
+
type: "object",
|
|
414
|
+
origin: "key",
|
|
415
|
+
input,
|
|
416
|
+
key,
|
|
417
|
+
value: input[key]
|
|
418
|
+
}]
|
|
419
|
+
});
|
|
420
|
+
if (config$1.abortEarly)
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
} else
|
|
425
|
+
_addIssue(this, "type", dataset, config$1);
|
|
426
|
+
return dataset;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function string(message$1) {
|
|
431
|
+
return {
|
|
432
|
+
kind: "schema",
|
|
433
|
+
type: "string",
|
|
434
|
+
reference: string,
|
|
435
|
+
expects: "string",
|
|
436
|
+
async: false,
|
|
437
|
+
message: message$1,
|
|
438
|
+
get "~standard"() {
|
|
439
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
440
|
+
},
|
|
441
|
+
"~run"(dataset, config$1) {
|
|
442
|
+
if (typeof dataset.value === "string")
|
|
443
|
+
dataset.typed = true;
|
|
444
|
+
else
|
|
445
|
+
_addIssue(this, "type", dataset, config$1);
|
|
446
|
+
return dataset;
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function safeParse(schema, input, config$1) {
|
|
451
|
+
const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
|
|
452
|
+
return {
|
|
453
|
+
typed: dataset.typed,
|
|
454
|
+
success: !dataset.issues,
|
|
455
|
+
output: dataset.value,
|
|
456
|
+
issues: dataset.issues
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/infra/capped-body.ts
|
|
461
|
+
var tooLarge = (maxBytes) => new Error(`the response is larger than ${maxBytes} bytes`);
|
|
462
|
+
var concatenate = (chunks, total) => {
|
|
463
|
+
const bytes = new Uint8Array(total);
|
|
464
|
+
let offset = 0;
|
|
465
|
+
for (const chunk of chunks) {
|
|
466
|
+
bytes.set(chunk, offset);
|
|
467
|
+
offset += chunk.length;
|
|
468
|
+
}
|
|
469
|
+
return bytes;
|
|
470
|
+
};
|
|
471
|
+
var readCapped = async (response, maxBytes) => {
|
|
472
|
+
const declared = Math.trunc(Number(response.headers.get("content-length") ?? ""));
|
|
473
|
+
if (declared > maxBytes) {
|
|
474
|
+
await response.body?.cancel();
|
|
475
|
+
throw tooLarge(maxBytes);
|
|
476
|
+
}
|
|
477
|
+
const chunks = [];
|
|
478
|
+
let received = 0;
|
|
479
|
+
for await (const chunk of response.body ?? []) {
|
|
480
|
+
received += chunk.length;
|
|
481
|
+
if (received > maxBytes) {
|
|
482
|
+
throw tooLarge(maxBytes);
|
|
483
|
+
}
|
|
484
|
+
chunks.push(chunk);
|
|
485
|
+
}
|
|
486
|
+
return concatenate(chunks, received);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// src/infra/release-http.ts
|
|
490
|
+
var METADATA_TIMEOUT_MS = 15000;
|
|
491
|
+
var ASSET_TIMEOUT_MS = 300000;
|
|
492
|
+
var METADATA_MAX_BYTES = 1048576;
|
|
493
|
+
var CHECKSUM_MAX_BYTES = 4096;
|
|
494
|
+
var BINARY_MAX_BYTES = 268435456;
|
|
495
|
+
var GITHUB_API_ORIGIN = "https://api.github.com/";
|
|
496
|
+
var HTTP_FORBIDDEN = 403;
|
|
497
|
+
var HTTP_TOO_MANY_REQUESTS = 429;
|
|
498
|
+
var GithubLatestSchema = object({ tag_name: string() });
|
|
499
|
+
var DECODER = new TextDecoder;
|
|
500
|
+
var send = async (fetcher, url, { headers, timeoutMs }) => {
|
|
501
|
+
try {
|
|
502
|
+
return await fetcher(url, {
|
|
503
|
+
headers,
|
|
504
|
+
redirect: "follow",
|
|
505
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
506
|
+
});
|
|
507
|
+
} catch (error) {
|
|
508
|
+
throw new Error(`GET ${url} failed: ${messageOf(error)}`, { cause: error });
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
var rateLimitHint = (url, response) => {
|
|
512
|
+
const refused = response.status === HTTP_FORBIDDEN || response.status === HTTP_TOO_MANY_REQUESTS;
|
|
513
|
+
const exhausted = refused && response.headers.get("x-ratelimit-remaining") === "0";
|
|
514
|
+
return exhausted && url.startsWith(GITHUB_API_ORIGIN) ? " (the unauthenticated GitHub API rate limit was hit; try again later)" : "";
|
|
515
|
+
};
|
|
516
|
+
var discardBody = async (response) => {
|
|
517
|
+
try {
|
|
518
|
+
await response.body?.cancel();
|
|
519
|
+
} catch {}
|
|
520
|
+
};
|
|
521
|
+
var get = async (fetcher, url, options) => {
|
|
522
|
+
const response = await send(fetcher, url, options);
|
|
523
|
+
if (!response.ok) {
|
|
524
|
+
await discardBody(response);
|
|
525
|
+
throw new Error(`GET ${url} failed: HTTP ${response.status}${rateLimitHint(url, response)}`);
|
|
526
|
+
}
|
|
527
|
+
if (response.url.startsWith("http://")) {
|
|
528
|
+
await discardBody(response);
|
|
529
|
+
throw new Error(`GET ${url} failed: redirected to ${response.url}, which is not HTTPS`);
|
|
530
|
+
}
|
|
531
|
+
return response;
|
|
532
|
+
};
|
|
533
|
+
var readBody = async (url, read) => {
|
|
534
|
+
try {
|
|
535
|
+
return await read();
|
|
536
|
+
} catch (error) {
|
|
537
|
+
throw new Error(`GET ${url} failed while reading the response: ${messageOf(error)}`, {
|
|
538
|
+
cause: error
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
var getBytes = async (fetcher, url, options) => {
|
|
543
|
+
const response = await get(fetcher, url, options);
|
|
544
|
+
return await readBody(url, () => readCapped(response, options.maxBytes));
|
|
545
|
+
};
|
|
546
|
+
var getJson = async (fetcher, url, schema, options) => {
|
|
547
|
+
const bytes = await getBytes(fetcher, url, options);
|
|
548
|
+
const body = await readBody(url, () => JSON.parse(DECODER.decode(bytes)));
|
|
549
|
+
const parsed = safeParse(schema, body);
|
|
550
|
+
if (!parsed.success) {
|
|
551
|
+
throw new Error(`GET ${url} returned an unexpected response`);
|
|
552
|
+
}
|
|
553
|
+
return parsed.output;
|
|
554
|
+
};
|
|
555
|
+
var createReleaseHttp = (userAgent, fetcher = fetch) => ({
|
|
556
|
+
async latestGithubVersion() {
|
|
557
|
+
const release = await getJson(fetcher, GITHUB_LATEST_RELEASE_URL, GithubLatestSchema, {
|
|
558
|
+
headers: {
|
|
559
|
+
Accept: "application/vnd.github+json",
|
|
560
|
+
"User-Agent": userAgent
|
|
561
|
+
},
|
|
562
|
+
timeoutMs: METADATA_TIMEOUT_MS,
|
|
563
|
+
maxBytes: METADATA_MAX_BYTES
|
|
564
|
+
});
|
|
565
|
+
return versionFromTag(release.tag_name);
|
|
566
|
+
},
|
|
567
|
+
async downloadReleaseAsset(version, assetName) {
|
|
568
|
+
return await getBytes(fetcher, releaseAssetUrl(version, assetName), {
|
|
569
|
+
headers: { Accept: "application/octet-stream", "User-Agent": userAgent },
|
|
570
|
+
timeoutMs: ASSET_TIMEOUT_MS,
|
|
571
|
+
maxBytes: BINARY_MAX_BYTES
|
|
572
|
+
});
|
|
573
|
+
},
|
|
574
|
+
async downloadReleaseText(version, assetName) {
|
|
575
|
+
const bytes = await getBytes(fetcher, releaseAssetUrl(version, assetName), {
|
|
576
|
+
headers: { Accept: "text/plain", "User-Agent": userAgent },
|
|
577
|
+
timeoutMs: METADATA_TIMEOUT_MS,
|
|
578
|
+
maxBytes: CHECKSUM_MAX_BYTES
|
|
579
|
+
});
|
|
580
|
+
return DECODER.decode(bytes);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// src/infra/self-update.ts
|
|
585
|
+
var createSelfUpdatePort = ({
|
|
586
|
+
compiled,
|
|
587
|
+
version
|
|
588
|
+
}) => ({
|
|
589
|
+
installFacts: () => readInstallFacts({
|
|
590
|
+
compiled,
|
|
591
|
+
execPath: process.execPath,
|
|
592
|
+
argv1: process.argv[1],
|
|
593
|
+
platform: process.platform,
|
|
594
|
+
arch: process.arch
|
|
595
|
+
}),
|
|
596
|
+
...createReleaseHttp(`hop/${version}`),
|
|
597
|
+
sha256Hex,
|
|
598
|
+
assertWritableDir,
|
|
599
|
+
replaceExecutable
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// src/cli-update.ts
|
|
603
|
+
var CHECK_FLAG = "--check";
|
|
604
|
+
var JSON_FLAG = "--json";
|
|
605
|
+
var UPDATE_FLAGS = new Set([CHECK_FLAG, JSON_FLAG]);
|
|
606
|
+
var parseUpdateArgs = (tokens) => {
|
|
607
|
+
const stray = tokens.filter((token) => !UPDATE_FLAGS.has(token));
|
|
608
|
+
if (stray.length > 0) {
|
|
609
|
+
return {
|
|
610
|
+
ok: false,
|
|
611
|
+
json: wantsJson(tokens),
|
|
612
|
+
message: `Unknown argument for --update: ${stray.join(" ")}. Usage: hop --update [--check] [--json]`
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
return { ok: true, check: tokens.includes(CHECK_FLAG), json: tokens.includes(JSON_FLAG) };
|
|
616
|
+
};
|
|
617
|
+
var runUpdate = async (rawArgs, term) => {
|
|
618
|
+
try {
|
|
619
|
+
const parsed = parseUpdateArgs(rawArgs.slice(1));
|
|
620
|
+
if (!parsed.ok) {
|
|
621
|
+
reportResult("update", fail(EXIT_USAGE_ERROR, parsed.message), parsed.json);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const port = createSelfUpdatePort({
|
|
625
|
+
compiled: isRunningAsCompiledBinary(),
|
|
626
|
+
version: VERSION
|
|
627
|
+
});
|
|
628
|
+
const result = await selfUpdate(port, term, {
|
|
629
|
+
currentVersion: VERSION,
|
|
630
|
+
check: parsed.check
|
|
631
|
+
});
|
|
632
|
+
reportResult("update", result, parsed.json);
|
|
633
|
+
} catch (error) {
|
|
634
|
+
reportFatalError("update", wantsJson(rawArgs), error);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
export {
|
|
638
|
+
runUpdate
|
|
639
|
+
};
|