@volter/world-runtime 3.0.34 → 3.0.36

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,9 +3,9 @@
3
3
  // parent through its own doors (its history cut at an instant, when one is asked), its clock frozen at
4
4
  // that instant so no twin's catch-up walks it forward, and removed when its time runs out. world-host
5
5
  // and `volter world view` both make them with this; a hosted World's supervisor makes its own.
6
- import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
7
7
  import { dirname, join } from 'node:path';
8
- import { stateDirName, withStateRemoval } from '@volter/world-core/runtime';
8
+ import { readBranchMeta, stateDirName, withHistoryLock, withStateRemoval, worldPaths, worldStateRoot, writeBranchMeta } from '@volter/world-core/runtime';
9
9
  import { inheritWorldBrowsers, materializeBrowserFiles } from "./browsers.js";
10
10
  import { clockFile, statusWorld } from "./runtime.js";
11
11
  import { loadWorldConfig } from "./configs.js";
@@ -169,7 +169,25 @@ export class LocalBranches {
169
169
  writeFileSync(file, `${clock}\n`);
170
170
  }
171
171
  const child = statusWorld(id, root);
172
- inheritWorldBrowsers(statusWorld(world, from.root).dirs.data, child.dirs.data);
172
+ // The HTTP clone holds the history view here, but its URL parent holds no local resources.
173
+ // Retain the actual parent's state through the kernel's pointer/pin protocol: blob reads can
174
+ // follow it without copying uploads, while history keeps using the downloaded immutable view.
175
+ const base = statusWorld(world, from.root);
176
+ for (const service of Object.keys(child.services)) {
177
+ const control = join(child.dirs.data, service);
178
+ for (const state of readdirSync(worldStateRoot(control), { withFileTypes: true })) {
179
+ if (!state.isDirectory())
180
+ continue;
181
+ const directory = worldPaths(state.name, control).dir;
182
+ withHistoryLock(directory, () => {
183
+ const meta = readBranchMeta(state.name, control);
184
+ if (!meta?.parent)
185
+ throw new Error(`cloning ${parent}: ${service}/${state.name} has no parent`);
186
+ writeBranchMeta(state.name, { ...meta, parent: { ...meta.parent, at: join(base.dirs.data, service), viewDirectory: directory } }, control);
187
+ });
188
+ }
189
+ }
190
+ inheritWorldBrowsers(base.dirs.data, child.dirs.data);
173
191
  materializeBrowserFiles(child.dirs.data, root, id, child.config);
174
192
  const createdAt = new Date().toISOString();
175
193
  const expiresAt = ttlSeconds === null ? null : new Date(Date.now() + ttlSeconds * 1000).toISOString();
@@ -659,6 +659,9 @@ export class WorldDoors {
659
659
  }
660
660
  const answer = await this.site(request, site, browser, held);
661
661
  const out = new Headers(answer.headers);
662
+ // A document belongs to this live page session and browser state, not an earlier opening of its URL.
663
+ if (out.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase() === 'text/html')
664
+ out.set('cache-control', 'private, no-store');
662
665
  out.append('content-security-policy', `frame-ancestors 'self'${held.framer ? ` ${held.framer}` : ''}`);
663
666
  this.keepCookiesHere(answer, out);
664
667
  return new Response(answer.body, { status: answer.status, statusText: answer.statusText, headers: out });
@@ -1153,7 +1156,7 @@ export class WorldDoors {
1153
1156
  }
1154
1157
  putBrowserLoad(this.up().dirs.data, loadId, { name, site, version: browser.version, cookies: cookies.filter(c => !protectedCookie(c.name)).map(c => ({ name: c.name, path: c.path })) });
1155
1158
  headers.append('set-cookie', `${marker}=${loadId}; Path=/; HttpOnly; Secure; SameSite=None; Partitioned`);
1156
- const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.replace(location.href);`;
1159
+ const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.reload();`;
1157
1160
  const hash = createHash('sha256').update(script).digest('base64');
1158
1161
  const framer = this.pageSession(request, url, site, name)?.framer;
1159
1162
  headers.set('content-security-policy', `default-src 'none'; script-src 'sha256-${hash}'; base-uri 'none'; frame-ancestors 'self'${framer ? ` ${framer}` : ''}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world-runtime",
3
- "version": "3.0.34",
3
+ "version": "3.0.36",
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,14 +68,14 @@
68
68
  "node": ">=22.3"
69
69
  },
70
70
  "peerDependencies": {
71
- "@volter/world-core": "3.0.34",
72
- "@volter/world-console": "3.0.29"
71
+ "@volter/world-core": "3.0.36",
72
+ "@volter/world-console": "3.0.31"
73
73
  },
74
74
  "dependencies": {
75
75
  "@electric-sql/pglite": "0.5.8",
76
76
  "@electric-sql/pglite-pgvector": "0.0.9",
77
77
  "@volter/world-access": "3.0.1",
78
- "@volter/world-core": "3.0.34",
78
+ "@volter/world-core": "3.0.36",
79
79
  "pg-gateway": "0.3.0-beta.4",
80
80
  "smol-toml": "^1.8.0",
81
81
  "yaml": "2.9.0"
@@ -3,9 +3,9 @@
3
3
  // parent through its own doors (its history cut at an instant, when one is asked), its clock frozen at
4
4
  // that instant so no twin's catch-up walks it forward, and removed when its time runs out. world-host
5
5
  // and `volter world view` both make them with this; a hosted World's supervisor makes its own.
6
- import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
7
7
  import { dirname, join } from 'node:path';
8
- import { stateDirName, withStateRemoval } from '@volter/world-core/runtime';
8
+ import { readBranchMeta, stateDirName, withHistoryLock, withStateRemoval, worldPaths, worldStateRoot, writeBranchMeta } from '@volter/world-core/runtime';
9
9
  import { inheritWorldBrowsers, materializeBrowserFiles } from './browsers.ts';
10
10
  import { clockFile, statusWorld } from './runtime.ts';
11
11
  import { loadWorldConfig } from './configs.ts';
@@ -162,7 +162,23 @@ export class LocalBranches {
162
162
  const clock = at.instant ?? this.frozenClock(from) ?? (at.live ? null : new Date().toISOString());
163
163
  if (clock) { const file = clockFile(root, id); mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, `${clock}\n`); }
164
164
  const child = statusWorld(id, root);
165
- inheritWorldBrowsers(statusWorld(world, from.root).dirs.data, child.dirs.data);
165
+ // The HTTP clone holds the history view here, but its URL parent holds no local resources.
166
+ // Retain the actual parent's state through the kernel's pointer/pin protocol: blob reads can
167
+ // follow it without copying uploads, while history keeps using the downloaded immutable view.
168
+ const base = statusWorld(world, from.root);
169
+ for (const service of Object.keys(child.services)) {
170
+ const control = join(child.dirs.data, service);
171
+ for (const state of readdirSync(worldStateRoot(control), { withFileTypes: true })) {
172
+ if (!state.isDirectory()) continue;
173
+ const directory = worldPaths(state.name, control).dir;
174
+ withHistoryLock(directory, () => {
175
+ const meta = readBranchMeta(state.name, control);
176
+ if (!meta?.parent) throw new Error(`cloning ${parent}: ${service}/${state.name} has no parent`);
177
+ writeBranchMeta(state.name, { ...meta, parent: { ...meta.parent, at: join(base.dirs.data, service), viewDirectory: directory } }, control);
178
+ });
179
+ }
180
+ }
181
+ inheritWorldBrowsers(base.dirs.data, child.dirs.data);
166
182
  materializeBrowserFiles(child.dirs.data, root, id, child.config);
167
183
  const createdAt = new Date().toISOString();
168
184
  const expiresAt = ttlSeconds === null ? null : new Date(Date.now() + ttlSeconds * 1000).toISOString();
@@ -645,6 +645,8 @@ export class WorldDoors {
645
645
  }
646
646
  const answer = await this.site(request, site, browser, held);
647
647
  const out = new Headers(answer.headers);
648
+ // A document belongs to this live page session and browser state, not an earlier opening of its URL.
649
+ if (out.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase() === 'text/html') out.set('cache-control', 'private, no-store');
648
650
  out.append('content-security-policy', `frame-ancestors 'self'${held.framer ? ` ${held.framer}` : ''}`);
649
651
  this.keepCookiesHere(answer, out);
650
652
  return new Response(answer.body, { status: answer.status, statusText: answer.statusText, headers: out });
@@ -1043,7 +1045,7 @@ export class WorldDoors {
1043
1045
  }
1044
1046
  putBrowserLoad(this.up().dirs.data, loadId, { name, site, version: browser.version, cookies: cookies.filter(c => !protectedCookie(c.name)).map(c => ({ name: c.name, path: c.path })) });
1045
1047
  headers.append('set-cookie', `${marker}=${loadId}; Path=/; HttpOnly; Secure; SameSite=None; Partitioned`);
1046
- const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.replace(location.href);`;
1048
+ const script = `localStorage.clear(); for (const entry of ${JSON.stringify(storage).replace(/</g, '\\u003c')}) localStorage.setItem(entry.name, entry.value); location.reload();`;
1047
1049
  const hash = createHash('sha256').update(script).digest('base64');
1048
1050
  const framer = this.pageSession(request, url, site, name)?.framer;
1049
1051
  headers.set('content-security-policy', `default-src 'none'; script-src 'sha256-${hash}'; base-uri 'none'; frame-ancestors 'self'${framer ? ` ${framer}` : ''}`);