wikimemory 0.2.5 → 0.2.6

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.
@@ -3,7 +3,7 @@ import { mkdir } from "node:fs/promises";
3
3
  import process from "node:process";
4
4
  import { WIKIMEMORY_VERSION } from "../src/version.js";
5
5
  import { deploymentArguments, installArguments } from "./cli-options.js";
6
- import { deploymentPaths } from "./deployment-record.js";
6
+ import { deploymentPaths, requireInstalledDeployment } from "./deployment-record.js";
7
7
  import { packageRoot } from "./package-root.js";
8
8
  import { conciseError } from "./subprocess.js";
9
9
  const [command, ...args] = process.argv.slice(2);
@@ -34,6 +34,18 @@ async function main() {
34
34
  }
35
35
  const parsed = deploymentArguments(args);
36
36
  const paths = deploymentPaths(parsed.deployment);
37
+ const requiresInstalledDeployment = command === "recover" ||
38
+ command === "status" ||
39
+ command === "passkeys" ||
40
+ command === "connect" ||
41
+ command === "uninstall" ||
42
+ (command === "upgrade" && !parsed.remaining.includes("--record"));
43
+ if (requiresInstalledDeployment) {
44
+ const requirement = command === "recover" || command === "passkeys" || command === "uninstall"
45
+ ? "config"
46
+ : "record";
47
+ await requireInstalledDeployment(parsed.deployment, requirement);
48
+ }
37
49
  if (command === "install" || command === "recover") {
38
50
  await mkdir(paths.directory, { recursive: true, mode: 0o700 });
39
51
  process.env["WIKIMEMORY_PACKAGE_ROOT"] = root;
@@ -1,4 +1,4 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, join } from "node:path";
4
4
  import { z } from "zod";
@@ -44,6 +44,43 @@ export function deploymentPaths(deployment = "wikimemory") {
44
44
  passkeyClient: join(directory, "passkey-client.json")
45
45
  };
46
46
  }
47
+ export async function requireInstalledDeployment(deployment, requirement = "record") {
48
+ const requestedPaths = deploymentPaths(deployment);
49
+ const requested = requirement === "record" ? requestedPaths.record : requestedPaths.config;
50
+ try {
51
+ await access(requested);
52
+ return;
53
+ }
54
+ catch (error) {
55
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
56
+ throw error;
57
+ }
58
+ const configRoot = process.env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config");
59
+ const deploymentsRoot = join(configRoot, "wikimemory", "deployments");
60
+ let installed = [];
61
+ try {
62
+ const entries = (await readdir(deploymentsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && DEPLOYMENT_NAME.test(entry.name));
63
+ const candidates = await Promise.all(entries.map(async (entry) => {
64
+ try {
65
+ await access(join(deploymentsRoot, entry.name, "deployment.json"));
66
+ return entry.name;
67
+ }
68
+ catch (error) {
69
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
70
+ return null;
71
+ throw error;
72
+ }
73
+ }));
74
+ installed = candidates.filter((name) => name !== null).sort();
75
+ }
76
+ catch (error) {
77
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
78
+ throw error;
79
+ }
80
+ if (installed.length === 0)
81
+ throw new Error(`No deployment named “${deployment}”. Run wikimemory install first.`);
82
+ throw new Error(`No deployment named “${deployment}”. Installed: ${installed.join(", ")}. Use --deployment NAME.`);
83
+ }
47
84
  export function deploymentRecordFromConfig(configText, installedVersion, kvName) {
48
85
  const config = PRODUCTION_CONFIG_SCHEMA.parse(JSON.parse(configText));
49
86
  const database = config.d1_databases.at(0);
@@ -0,0 +1,4 @@
1
+ export const DEPLOYMENT_VERIFY_ATTEMPTS = 25;
2
+ export function deploymentVerifyDelay(completedAttempt) {
3
+ return Math.min(1000 * 2 ** completedAttempt, 5000);
4
+ }
@@ -9,6 +9,7 @@ import { pathToFileURL } from "node:url";
9
9
  import { z } from "zod";
10
10
  import { WIKIMEMORY_VERSION } from "../src/version.js";
11
11
  import { deploymentRecordFromConfig, writeDeploymentRecord } from "./deployment-record.js";
12
+ import { DEPLOYMENT_VERIFY_ATTEMPTS, deploymentVerifyDelay } from "./deployment-wait.js";
12
13
  import { installedRecordPath, packageRoot, setupRuntime } from "./lifecycle-runtime.js";
13
14
  import { commandFailureMessage, conciseError, runCommand } from "./subprocess.js";
14
15
  export { commandFailureMessage } from "./subprocess.js";
@@ -319,7 +320,7 @@ function bootstrapSecret() {
319
320
  const hash = createHash("sha256").update(raw, "utf8").digest("hex");
320
321
  return { raw, hash };
321
322
  }
322
- export async function verifyEndpoint(origin, path, expectedStatus, fetcher = fetch, sleeper = sleep, attempts = 6) {
323
+ export async function verifyEndpoint(origin, path, expectedStatus, fetcher = fetch, sleeper = sleep, attempts = DEPLOYMENT_VERIFY_ATTEMPTS) {
323
324
  let finalDetail = "no response";
324
325
  for (let attempt = 0; attempt < attempts; attempt += 1) {
325
326
  try {
@@ -344,7 +345,7 @@ export async function verifyEndpoint(origin, path, expectedStatus, fetcher = fet
344
345
  finalDetail = error instanceof Error ? error.message : "Unknown request failure";
345
346
  }
346
347
  if (attempt + 1 < attempts)
347
- await sleeper(250 * 2 ** attempt);
348
+ await sleeper(deploymentVerifyDelay(attempt));
348
349
  }
349
350
  throw new Error(`Deployment ${path} check failed after ${attempts} attempts: ${finalDetail}`);
350
351
  }
@@ -6,6 +6,7 @@ import process from "node:process";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  import { z } from "zod";
8
8
  import { deploymentRecordPath, readDeploymentRecord, writeDeploymentRecord } from "./deployment-record.js";
9
+ import { DEPLOYMENT_VERIFY_ATTEMPTS, deploymentVerifyDelay } from "./deployment-wait.js";
9
10
  import { packageRoot } from "./package-root.js";
10
11
  import { commandFailureMessage, runCommand } from "./subprocess.js";
11
12
  const PACKAGE_ROOT = packageRoot();
@@ -173,7 +174,7 @@ async function responseJson(response, label) {
173
174
  throw new Error(`${label} returned non-JSON content`);
174
175
  }
175
176
  }
176
- export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper = sleep, attempts = 6) {
177
+ export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper = sleep, attempts = DEPLOYMENT_VERIFY_ATTEMPTS) {
177
178
  let finalError;
178
179
  for (let attempt = 0; attempt < attempts; attempt += 1) {
179
180
  try {
@@ -210,7 +211,7 @@ export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper =
210
211
  catch (error) {
211
212
  finalError = error;
212
213
  if (attempt + 1 < attempts)
213
- await sleeper(250 * 2 ** attempt);
214
+ await sleeper(deploymentVerifyDelay(attempt));
214
215
  }
215
216
  }
216
217
  const detail = finalError instanceof Error ? finalError.message : "Unknown verification failure";
@@ -1,2 +1,2 @@
1
- export const WIKIMEMORY_VERSION = "0.2.5";
1
+ export const WIKIMEMORY_VERSION = "0.2.6";
2
2
  export const LATEST_SCHEMA_VERSION = "0004_credential_bound_registration_tokens.sql";