wikimemory 0.2.3 → 0.2.4

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.
@@ -79,6 +79,10 @@ export function compareSemanticVersions(left, right) {
79
79
  }
80
80
  return 0;
81
81
  }
82
+ export function deploymentIsCurrent(currentVersion, manifest, pendingMigrations) {
83
+ return (compareSemanticVersions(currentVersion, manifest.version) === 0 &&
84
+ pendingMigrations.length === 0);
85
+ }
82
86
  export function productionUpgradeConfig(record, packageRoot) {
83
87
  return `${JSON.stringify({
84
88
  $schema: join(packageRoot, "node_modules", "wrangler", "config-schema.json"),
@@ -166,34 +170,64 @@ async function confirmUpgrade(summary, automatic) {
166
170
  if (answer !== "y" && answer !== "yes")
167
171
  throw new Error("Cancelled");
168
172
  }
169
- async function verifyRelease(origin, manifest) {
170
- const health = await fetch(`${origin}/health`, { redirect: "error" });
171
- const healthBody = z
172
- .object({ status: z.literal("ok"), service: z.literal("wikimemory"), version: z.string() })
173
- .parse(await health.json());
174
- if (!health.ok || healthBody.version !== manifest.version)
175
- throw new Error("Deployed Worker version verification failed");
176
- const ready = await fetch(`${origin}/ready`, { redirect: "error" });
177
- const readyBody = z
178
- .object({
179
- status: z.literal("ready"),
180
- service: z.literal("wikimemory"),
181
- version: z.string(),
182
- schemaVersion: z.string()
183
- })
184
- .parse(await ready.json());
185
- if (!ready.ok ||
186
- readyBody.version !== manifest.version ||
187
- readyBody.schemaVersion !== manifest.schemaVersion)
188
- throw new Error("Deployed schema version verification failed");
189
- const discovery = await fetch(`${origin}/.well-known/oauth-protected-resource/mcp`, {
190
- redirect: "error"
173
+ function sleep(milliseconds) {
174
+ return new Promise((resolve) => {
175
+ setTimeout(resolve, milliseconds);
191
176
  });
192
- if (!discovery.ok)
193
- throw new Error("OAuth protected-resource discovery verification failed");
194
- const app = await fetch(`${origin}/app`, { redirect: "error" });
195
- if (!app.ok || !(await app.text()).includes('<div id="root"></div>'))
196
- throw new Error("React application verification failed");
177
+ }
178
+ async function responseJson(response, label) {
179
+ const text = await response.text();
180
+ if (!response.ok)
181
+ throw new Error(`${label} returned HTTP ${response.status}: ${text.slice(0, 300)}`);
182
+ try {
183
+ return JSON.parse(text);
184
+ }
185
+ catch {
186
+ throw new Error(`${label} returned non-JSON content`);
187
+ }
188
+ }
189
+ export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper = sleep, attempts = 6) {
190
+ let finalError;
191
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
192
+ try {
193
+ const health = await fetcher(`${origin}/health`, { redirect: "error" });
194
+ const healthBody = z
195
+ .object({ status: z.literal("ok"), service: z.literal("wikimemory"), version: z.string() })
196
+ .parse(await responseJson(health, "Health endpoint"));
197
+ if (healthBody.version !== manifest.version) {
198
+ throw new Error(`Health endpoint reports version ${healthBody.version}; expected ${manifest.version}`);
199
+ }
200
+ const ready = await fetcher(`${origin}/ready`, { redirect: "error" });
201
+ const readyBody = z
202
+ .object({
203
+ status: z.literal("ready"),
204
+ service: z.literal("wikimemory"),
205
+ version: z.string(),
206
+ schemaVersion: z.string()
207
+ })
208
+ .parse(await responseJson(ready, "Readiness endpoint"));
209
+ if (readyBody.version !== manifest.version ||
210
+ readyBody.schemaVersion !== manifest.schemaVersion) {
211
+ throw new Error(`Readiness endpoint reports version ${readyBody.version} and schema ${readyBody.schemaVersion}; expected ${manifest.version} and ${manifest.schemaVersion}`);
212
+ }
213
+ const discovery = await fetcher(`${origin}/.well-known/oauth-protected-resource/mcp`, {
214
+ redirect: "error"
215
+ });
216
+ if (!discovery.ok)
217
+ throw new Error(`OAuth protected-resource discovery returned HTTP ${discovery.status}`);
218
+ const app = await fetcher(`${origin}/app`, { redirect: "error" });
219
+ if (!app.ok || !(await app.text()).includes('<div id="root"></div>'))
220
+ throw new Error("React application verification failed");
221
+ return;
222
+ }
223
+ catch (error) {
224
+ finalError = error;
225
+ if (attempt + 1 < attempts)
226
+ await sleeper(250 * 2 ** attempt);
227
+ }
228
+ }
229
+ const detail = finalError instanceof Error ? finalError.message : "Unknown verification failure";
230
+ throw new Error(`Deployed release verification failed after ${attempts} attempts: ${detail}`);
197
231
  }
198
232
  export async function runUpgrade(args) {
199
233
  const options = parseUpgradeOptions(args);
@@ -271,6 +305,12 @@ export async function runUpgrade(args) {
271
305
  throw new Error("Current deployment health check failed");
272
306
  if (compareSemanticVersions(current, manifest.version) > 0)
273
307
  throw new Error(`Refusing to downgrade Wikimemory from ${current} to ${manifest.version}`);
308
+ if (deploymentIsCurrent(current, manifest, pending)) {
309
+ await verifyRelease(record.origin, manifest);
310
+ await writeDeploymentRecord({ ...record, installedVersion: manifest.version }, recordPath);
311
+ console.log(`\nWikimemory ${manifest.version} is already ready. Reconciled the local deployment record without redeploying.`);
312
+ return;
313
+ }
274
314
  await confirmUpgrade(`\nWikimemory upgrade\n Account: ${record.accountId}\n Worker: ${record.workerName}\n D1: ${record.databaseName} (${record.databaseId})\n KV: ${record.kvName} (${record.kvId})\n Origin: ${record.origin}\n Version: ${current} -> ${manifest.version}\n Migrations: ${pending.map((item) => item.name).join(", ") || "none"}`, options.yes);
275
315
  for (const migration of pending) {
276
316
  const bundlePath = join(temporary, migration.name);
@@ -1,2 +1,2 @@
1
- export const WIKIMEMORY_VERSION = "0.2.3";
1
+ export const WIKIMEMORY_VERSION = "0.2.4";
2
2
  export const LATEST_SCHEMA_VERSION = "0004_credential_bound_registration_tokens.sql";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikimemory",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Personal, passkey-protected memory for Claude, Codex, and other MCP clients",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.3",
2
+ "version": "0.2.4",
3
3
  "schemaVersion": "0004_credential_bound_registration_tokens.sql",
4
4
  "migrations": [
5
5
  {
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const WIKIMEMORY_VERSION = "0.2.3";
1
+ export const WIKIMEMORY_VERSION = "0.2.4";
2
2
  export const LATEST_SCHEMA_VERSION = "0004_credential_bound_registration_tokens.sql";