@volter/world-runtime 2.0.10 → 2.0.12
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/dist/src/branch.js +32 -1
- package/dist/src/served-world.js +11 -3
- package/package.json +2 -2
- package/src/branch.ts +16 -1
- package/src/served-world.ts +8 -2
package/dist/src/branch.js
CHANGED
|
@@ -46,7 +46,38 @@ export async function branchWorld(base, name, options = {}) {
|
|
|
46
46
|
throw new Error(`volter-world branch: ${base} has a managed database, which keeps no history to branch from at an instant or position; branch from its current state`);
|
|
47
47
|
}
|
|
48
48
|
refuseLiveDatabases(from, databases);
|
|
49
|
-
|
|
49
|
+
// a branch that fails to boot (a twin whose mounted version breaks the config's pin) leaves no half-made branch behind:
|
|
50
|
+
// its record without a fork would check out later as an empty World
|
|
51
|
+
const existed = (() => { try {
|
|
52
|
+
statusWorld(name, root);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
} })();
|
|
58
|
+
let instance;
|
|
59
|
+
try {
|
|
60
|
+
instance = await upWorld(existsSync(from.configPath) ? from.configPath : from.config, { name, root, mode: from.mode, envFile });
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
let recorded = false;
|
|
64
|
+
if (!existed) {
|
|
65
|
+
try {
|
|
66
|
+
statusWorld(name, root);
|
|
67
|
+
recorded = true;
|
|
68
|
+
}
|
|
69
|
+
catch { /* the boot recorded nothing */ }
|
|
70
|
+
}
|
|
71
|
+
if (recorded) {
|
|
72
|
+
try {
|
|
73
|
+
await downWorld(name, root, { purge: true });
|
|
74
|
+
}
|
|
75
|
+
catch (cleanup) {
|
|
76
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)} (and the half-made branch ${name} could not be removed: ${cleanup instanceof Error ? cleanup.message : String(cleanup)})`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
50
81
|
const forked = {};
|
|
51
82
|
try {
|
|
52
83
|
if (from.origin) {
|
package/dist/src/served-world.js
CHANGED
|
@@ -37,7 +37,7 @@ import { fetchFromOrigin } from "./origin.js";
|
|
|
37
37
|
import { CONSOLE_BASE, consoleRedirect, serveConsoleApart } from "./console-apart.js";
|
|
38
38
|
import { loadWorldConfig } from "./configs.js";
|
|
39
39
|
import { adaptersFor, credentialPath, packMirror, credentialPayloadFrom, deployTwin, deployWorld, loadWorldChecks, twinSigningSecret, materializeRoots, refreshPosture, refreshTwin, rootForControlRoot, sealTwinCredential, sealedCredentialInfo, setTwinRoot, spanMs } from "./root.js";
|
|
40
|
-
import { clockFile, downWorld, statusWorld, upWorld } from "./runtime.js";
|
|
40
|
+
import { clockFile, downWorld, listWorlds, statusWorld, upWorld } from "./runtime.js";
|
|
41
41
|
export const TOKEN_HEADER = 'x-volter-token';
|
|
42
42
|
/** The header the injector carries a hosted World's token in (inject.cjs, VOLTER_TWINS_KEY). */
|
|
43
43
|
export const TWINS_KEY_HEADER = 'x-twins-key';
|
|
@@ -1429,8 +1429,16 @@ export class WorldDoors {
|
|
|
1429
1429
|
const branches = this.host.branches;
|
|
1430
1430
|
if (!branches)
|
|
1431
1431
|
return Response.json({ error: `this host makes no branches of ${this.served}` }, { status: 404 });
|
|
1432
|
-
if (rest.length === 0 && request.method === 'GET')
|
|
1433
|
-
|
|
1432
|
+
if (rest.length === 0 && request.method === 'GET') {
|
|
1433
|
+
// beside the host's snapshots, the World's own NAMED branches (`volter world branch`): one runs at a time, and
|
|
1434
|
+
// `checkedOut` is the one this serve holds, so a page never names the World for the branch it shows
|
|
1435
|
+
let named = [];
|
|
1436
|
+
try {
|
|
1437
|
+
named = listWorlds(this.worldRoot).map((w) => ({ name: w.name, running: w.running }));
|
|
1438
|
+
}
|
|
1439
|
+
catch { /* a World with no local branches */ }
|
|
1440
|
+
return Response.json({ world: this.served, branches: await branches.list(), checkedOut: this.name, named });
|
|
1441
|
+
}
|
|
1434
1442
|
if (rest.length === 0 && request.method === 'POST') {
|
|
1435
1443
|
const body = (await request.json().catch(() => null));
|
|
1436
1444
|
const instant = body?.at?.instant;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-runtime",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "World configs for twins: boot named local runtimes, allocate ports, generate world.env/instance.json, and run apps against fake-key twin worlds.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@volter/world-core": "2.0.7",
|
|
71
|
-
"@volter/world-console": "2.0.
|
|
71
|
+
"@volter/world-console": "2.0.7"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@electric-sql/pglite": "0.5.8",
|
package/src/branch.ts
CHANGED
|
@@ -48,7 +48,22 @@ export async function branchWorld(base: string, name: string, options: BranchOpt
|
|
|
48
48
|
throw new Error(`volter-world branch: ${base} has a managed database, which keeps no history to branch from at an instant or position; branch from its current state`);
|
|
49
49
|
}
|
|
50
50
|
refuseLiveDatabases(from, databases);
|
|
51
|
-
|
|
51
|
+
// a branch that fails to boot (a twin whose mounted version breaks the config's pin) leaves no half-made branch behind:
|
|
52
|
+
// its record without a fork would check out later as an empty World
|
|
53
|
+
const existed = (() => { try { statusWorld(name, root); return true; } catch { return false; } })();
|
|
54
|
+
let instance: WorldInstance;
|
|
55
|
+
try {
|
|
56
|
+
instance = await upWorld(existsSync(from.configPath) ? from.configPath : from.config, { name, root, mode: from.mode, envFile });
|
|
57
|
+
} catch (error) {
|
|
58
|
+
let recorded = false;
|
|
59
|
+
if (!existed) { try { statusWorld(name, root); recorded = true; } catch { /* the boot recorded nothing */ } }
|
|
60
|
+
if (recorded) {
|
|
61
|
+
try { await downWorld(name, root, { purge: true }); } catch (cleanup) {
|
|
62
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)} (and the half-made branch ${name} could not be removed: ${cleanup instanceof Error ? cleanup.message : String(cleanup)})`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
52
67
|
const forked: Record<string, string[]> = {};
|
|
53
68
|
try {
|
|
54
69
|
if (from.origin) { instance.origin = { ...from.origin }; saveWorldInstance(instance); }
|
package/src/served-world.ts
CHANGED
|
@@ -40,7 +40,7 @@ import { fetchFromOrigin } from './origin.ts';
|
|
|
40
40
|
import { CONSOLE_BASE, consoleRedirect, serveConsoleApart, type ConsoleMount } from './console-apart.ts';
|
|
41
41
|
import { loadWorldConfig } from './configs.ts';
|
|
42
42
|
import { adaptersFor, credentialPath, packMirror, type PackMirror, credentialPayloadFrom, deployTwin, deployWorld, loadWorldChecks, twinSigningSecret, materializeRoots, refreshPosture, refreshTwin, rootForControlRoot, sealTwinCredential, sealedCredentialInfo, setTwinRoot, spanMs } from './root.ts';
|
|
43
|
-
import { clockFile, downWorld, statusWorld, upWorld } from './runtime.ts';
|
|
43
|
+
import { clockFile, downWorld, listWorlds, statusWorld, upWorld } from './runtime.ts';
|
|
44
44
|
import type { WorldInstance } from './schema.ts';
|
|
45
45
|
|
|
46
46
|
export const TOKEN_HEADER = 'x-volter-token';
|
|
@@ -1225,7 +1225,13 @@ export class WorldDoors {
|
|
|
1225
1225
|
private async branchesDoor(request: Request, url: URL, rest: string[], via: 'token' | 'key' | 'session' | null): Promise<Response> {
|
|
1226
1226
|
const branches = this.host.branches;
|
|
1227
1227
|
if (!branches) return Response.json({ error: `this host makes no branches of ${this.served}` }, { status: 404 });
|
|
1228
|
-
if (rest.length === 0 && request.method === 'GET')
|
|
1228
|
+
if (rest.length === 0 && request.method === 'GET') {
|
|
1229
|
+
// beside the host's snapshots, the World's own NAMED branches (`volter world branch`): one runs at a time, and
|
|
1230
|
+
// `checkedOut` is the one this serve holds, so a page never names the World for the branch it shows
|
|
1231
|
+
let named: Array<{ name: string; running: boolean }> = [];
|
|
1232
|
+
try { named = listWorlds(this.worldRoot).map((w) => ({ name: w.name, running: w.running })); } catch { /* a World with no local branches */ }
|
|
1233
|
+
return Response.json({ world: this.served, branches: await branches.list(), checkedOut: this.name, named });
|
|
1234
|
+
}
|
|
1229
1235
|
if (rest.length === 0 && request.method === 'POST') {
|
|
1230
1236
|
const body = (await request.json().catch(() => null)) as { at?: { instant?: unknown }; ttl?: unknown; live?: unknown; label?: unknown } | null;
|
|
1231
1237
|
const instant = body?.at?.instant;
|