@volter/browser-substrate 0.1.103 → 0.1.105

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.
Files changed (160) hide show
  1. package/catalog/demand-input.json +133 -0
  2. package/dist/boot-screen.d.ts +2 -0
  3. package/dist/boot-screen.d.ts.map +1 -1
  4. package/dist/boot-screen.js +1 -1
  5. package/dist/boot-screen.js.map +1 -1
  6. package/dist/cli/archive.d.ts +1 -0
  7. package/dist/cli/archive.d.ts.map +1 -1
  8. package/dist/cli/archive.js +4 -4
  9. package/dist/cli/archive.js.map +1 -1
  10. package/dist/cli/build.d.ts +5 -1
  11. package/dist/cli/build.d.ts.map +1 -1
  12. package/dist/cli/build.js +21 -2
  13. package/dist/cli/build.js.map +1 -1
  14. package/dist/cli/cache-root.d.ts +7 -0
  15. package/dist/cli/cache-root.d.ts.map +1 -0
  16. package/dist/cli/cache-root.js +24 -0
  17. package/dist/cli/cache-root.js.map +1 -0
  18. package/dist/cli/companions.d.ts +1 -1
  19. package/dist/cli/companions.d.ts.map +1 -1
  20. package/dist/cli/companions.js +30 -16
  21. package/dist/cli/companions.js.map +1 -1
  22. package/dist/cli/dependency-plan-build.d.ts +4 -0
  23. package/dist/cli/dependency-plan-build.d.ts.map +1 -1
  24. package/dist/cli/dependency-plan-build.js +111 -30
  25. package/dist/cli/dependency-plan-build.js.map +1 -1
  26. package/dist/cli/docker-build.d.ts +1 -0
  27. package/dist/cli/docker-build.d.ts.map +1 -1
  28. package/dist/cli/docker-build.js +4 -4
  29. package/dist/cli/docker-build.js.map +1 -1
  30. package/dist/cli/dry-run.d.ts +4 -4
  31. package/dist/cli/dry-run.d.ts.map +1 -1
  32. package/dist/cli/dry-run.js +10 -9
  33. package/dist/cli/dry-run.js.map +1 -1
  34. package/dist/cli/generated-code.d.ts +2 -1
  35. package/dist/cli/generated-code.d.ts.map +1 -1
  36. package/dist/cli/generated-code.js +46 -16
  37. package/dist/cli/generated-code.js.map +1 -1
  38. package/dist/cli/host-build.d.ts +1 -0
  39. package/dist/cli/host-build.d.ts.map +1 -1
  40. package/dist/cli/host-build.js +10 -9
  41. package/dist/cli/host-build.js.map +1 -1
  42. package/dist/cli/host-managers.d.ts +3 -1
  43. package/dist/cli/host-managers.d.ts.map +1 -1
  44. package/dist/cli/host-managers.js +7 -3
  45. package/dist/cli/host-managers.js.map +1 -1
  46. package/dist/cli/image-layers.d.ts.map +1 -1
  47. package/dist/cli/image-layers.js +5 -2
  48. package/dist/cli/image-layers.js.map +1 -1
  49. package/dist/cli/main.d.ts +5 -1
  50. package/dist/cli/main.d.ts.map +1 -1
  51. package/dist/cli/main.js +97 -34
  52. package/dist/cli/main.js.map +1 -1
  53. package/dist/cli/pack-publication.d.ts +2 -2
  54. package/dist/cli/pack-publication.d.ts.map +1 -1
  55. package/dist/cli/pack-publication.js +3 -3
  56. package/dist/cli/pack-publication.js.map +1 -1
  57. package/dist/cli/package-pack.d.ts +1 -1
  58. package/dist/cli/package-pack.d.ts.map +1 -1
  59. package/dist/cli/package-pack.js +7 -4
  60. package/dist/cli/package-pack.js.map +1 -1
  61. package/dist/cli/package-packs.d.ts +1 -0
  62. package/dist/cli/package-packs.d.ts.map +1 -1
  63. package/dist/cli/package-packs.js +84 -14
  64. package/dist/cli/package-packs.js.map +1 -1
  65. package/dist/cli/plan-tarballs.d.ts +1 -0
  66. package/dist/cli/plan-tarballs.d.ts.map +1 -1
  67. package/dist/cli/plan-tarballs.js +6 -3
  68. package/dist/cli/plan-tarballs.js.map +1 -1
  69. package/dist/cli/preparation-memory.d.ts +17 -0
  70. package/dist/cli/preparation-memory.d.ts.map +1 -0
  71. package/dist/cli/preparation-memory.js +367 -0
  72. package/dist/cli/preparation-memory.js.map +1 -0
  73. package/dist/cli/project-build.d.ts +1 -0
  74. package/dist/cli/project-build.d.ts.map +1 -1
  75. package/dist/cli/project-build.js +100 -71
  76. package/dist/cli/project-build.js.map +1 -1
  77. package/dist/cli/provision-catalog.d.ts +49 -0
  78. package/dist/cli/provision-catalog.d.ts.map +1 -0
  79. package/dist/cli/provision-catalog.js +72 -0
  80. package/dist/cli/provision-catalog.js.map +1 -0
  81. package/dist/cli/provision-demand.d.ts +45 -0
  82. package/dist/cli/provision-demand.d.ts.map +1 -0
  83. package/dist/cli/provision-demand.js +208 -0
  84. package/dist/cli/provision-demand.js.map +1 -0
  85. package/dist/cli/rolldown-binding.d.ts +1 -1
  86. package/dist/cli/rolldown-binding.d.ts.map +1 -1
  87. package/dist/cli/rolldown-binding.js +8 -4
  88. package/dist/cli/rolldown-binding.js.map +1 -1
  89. package/dist/cli/serve.d.ts +3 -0
  90. package/dist/cli/serve.d.ts.map +1 -1
  91. package/dist/cli/serve.js +9 -2
  92. package/dist/cli/serve.js.map +1 -1
  93. package/dist/index.d.ts +2 -0
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +75 -31
  96. package/dist/index.js.map +1 -1
  97. package/dist/origin-reset.d.ts +4 -0
  98. package/dist/origin-reset.d.ts.map +1 -0
  99. package/dist/origin-reset.js +76 -0
  100. package/dist/origin-reset.js.map +1 -0
  101. package/dist/program-dom.d.ts +2 -0
  102. package/dist/program-dom.d.ts.map +1 -1
  103. package/dist/program-dom.js +11 -1
  104. package/dist/program-dom.js.map +1 -1
  105. package/dist/program-types.d.ts +2 -0
  106. package/dist/program-types.d.ts.map +1 -1
  107. package/dist/shell/assets/browser-opfs-filesystem-worker-CmF6UKMW.js +3 -0
  108. package/dist/shell/assets/browser-sandbox-bridge-C8nlSsT2.js +237 -0
  109. package/dist/shell/assets/{filesystem-host-DJbFuokb.js → filesystem-host-BzPlXSri.js} +1 -1
  110. package/dist/shell/assets/filesystem-host-Pq9IHKRx.js +3 -0
  111. package/dist/shell/assets/filesystem-worker-CC3pSXUP.js +5 -0
  112. package/dist/shell/assets/{fork-worker-BXY2kfdX.js → fork-worker-4YKQ4KTo.js} +1 -1
  113. package/dist/shell/assets/{node-execution-worker-7e8e0c5321ca0d10.js → node-execution-worker-3ddf5efbaa427e9d.js} +8 -8
  114. package/dist/shell/assets/{sandbox-bMeKqKBn.js → sandbox-mhFzIWaQ.js} +1 -1
  115. package/dist/shell/assets/{shell-DLIQ6jHu.js → shell-BZbhxnju.js} +763 -757
  116. package/dist/shell/assets/worker-BmHEnCyv.js +816 -0
  117. package/dist/shell/assets/{worker-program-worker-B4OcKVtV.js → worker-program-worker-DueMwljE.js} +10 -10
  118. package/dist/shell/brand/fonts/Geist-Variable.woff2 +0 -0
  119. package/dist/shell/brand/fonts/GeistMono-Variable.woff2 +0 -0
  120. package/dist/shell/brand/fonts/InstrumentSerif-Italic.woff2 +0 -0
  121. package/dist/shell/brand/fonts/InstrumentSerif-Regular.woff2 +0 -0
  122. package/dist/shell/brand/icons/app-window.svg +1 -0
  123. package/dist/shell/brand/icons/at-sign.svg +1 -0
  124. package/dist/shell/brand/icons/box.svg +1 -0
  125. package/dist/shell/brand/icons/chevron-right.svg +1 -0
  126. package/dist/shell/brand/icons/folder.svg +1 -0
  127. package/dist/shell/brand/icons/info.svg +1 -0
  128. package/dist/shell/brand/icons/key-round.svg +1 -0
  129. package/dist/shell/brand/icons/link.svg +1 -0
  130. package/dist/shell/brand/icons/monitor.svg +1 -0
  131. package/dist/shell/brand/icons/plus.svg +1 -0
  132. package/dist/shell/brand/icons/rotate-ccw.svg +1 -0
  133. package/dist/shell/brand/icons/share-2.svg +1 -0
  134. package/dist/shell/brand/icons/square-arrow-out-up-right.svg +1 -0
  135. package/dist/shell/brand/icons/terminal.svg +1 -0
  136. package/dist/shell/brand/icons/x.svg +1 -0
  137. package/dist/shell/brand/substrate.svg +1 -0
  138. package/dist/shell/brand/tokens.css +211 -0
  139. package/dist/shell/brand/wordmark.svg +1 -0
  140. package/dist/shell/index.html +5 -3
  141. package/dist/shell/node-execution-worker.js +16 -13
  142. package/dist/shell/node-host.html +2 -0
  143. package/dist/shell/python-execution-worker.js +15 -12
  144. package/dist/shell/python-host.html +2 -0
  145. package/dist/shell/python-runtime-snapshot.bin +0 -0
  146. package/dist/shell/python-runtime-snapshot.json +1 -1
  147. package/dist/shell/sandbox.html +4 -2
  148. package/dist/toolbar-styles.generated.d.ts +1 -1
  149. package/dist/toolbar-styles.generated.d.ts.map +1 -1
  150. package/dist/toolbar-styles.generated.js +1 -1
  151. package/dist/toolbar-styles.generated.js.map +1 -1
  152. package/dist/toolbar.d.ts.map +1 -1
  153. package/dist/toolbar.js +3 -1
  154. package/dist/toolbar.js.map +1 -1
  155. package/package.json +1 -1
  156. package/dist/shell/assets/browser-opfs-filesystem-worker-k2CPzPBU.js +0 -3
  157. package/dist/shell/assets/browser-sandbox-bridge-BJbQoc4f.js +0 -237
  158. package/dist/shell/assets/filesystem-host-yMcJuvmT.js +0 -3
  159. package/dist/shell/assets/filesystem-worker-BbsBhZ9p.js +0 -5
  160. package/dist/shell/assets/worker-Ce8bBwSJ.js +0 -816
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { validateImageCacheEntries } from "./image-packs.js";
10
10
  import { PROGRAM_ARCHIVE_RECEIPT, imageProgramLayerRoot, programArchiveReceipt } from "@volter/browser-runtime/browser-image-layers.js";
11
11
  import { attachLocalPreviewSockets, injectLoopbackIntoPreviews } from "./local-preview.js";
12
12
  import { exposeBrowserCookieJar, openPersistentBrowserCookieJar } from "@volter/browser-runtime/browser-cookie-jar.js";
13
+ import { holdOriginForProject } from "./origin-reset.js";
13
14
  import { attachTabnodeCookieJar } from "@volter/browser-node/tabnode-cookie-jar.js";
14
15
  import { browserProjectId, buildContainerFile, classifyContainerFile, clearVirtualFileSystem, containerLaneTableForRuntime, parseContainerFile, probeBrowserCapabilities, loadProgramHttpTransportPolicies, rebaseCommandPaths, resolveRelayService, stagePath, syncContainerBuild, BrowserSandboxBridge, HostFetchServiceRegistry, browserRegisteredServer, HOST_FETCH_DEFAULT_MAX_REQUEST_BYTES, } from "@volter/browser-runtime";
15
16
  import { createIsolatedBrowserNodeRuntime, createWorkerBrowserNodeRuntime, registerPackageManagerAliases, withTabnodeHttpStreaming, prepareBrowserNodeProgram, VIRTUAL_HOST_PARAMETER, warmPreparedImage, } from "@volter/browser-node";
@@ -144,16 +145,36 @@ function rejectedReady(reason) {
144
145
  ready.catch(() => undefined);
145
146
  return ready;
146
147
  }
148
+ async function fetchImageManifest(name, url) {
149
+ const response = await nativeFetch(url, { cache: "no-store" });
150
+ const type = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() ?? "";
151
+ if (!response.ok || !/^(?:application|text)\/(?:[\w.+-]+\+)?json$/u.test(type)) {
152
+ throw new Error(`image ${name} not found at ${url} (HTTP ${response.status}, ${type || "no content type"}). Build one with browser-substrate build.`);
153
+ }
154
+ let source;
155
+ try {
156
+ source = await response.json();
157
+ }
158
+ catch (cause) {
159
+ throw new Error(`image ${name} at ${url} is invalid JSON`, { cause });
160
+ }
161
+ return validateImageManifest(source);
162
+ }
147
163
  export async function openProject(nameOrOptions = {}) {
148
164
  // An open that fails after starting the engine's worker leaves nothing
149
165
  // running: what it started is disposed before the failure is reported.
150
166
  const started = [];
167
+ const releaseOrigin = await holdOriginForProject();
151
168
  try {
152
- return await openProjectTracking(nameOrOptions, (node) => { started.push(node); });
169
+ const project = await openProjectTracking(nameOrOptions, (node) => { started.push(node); });
170
+ const dispose = project.dispose.bind(project);
171
+ project.dispose = async () => { await dispose(); await releaseOrigin(); };
172
+ return project;
153
173
  }
154
174
  catch (cause) {
155
- for (const node of started)
156
- await node.runtime.dispose().catch(() => undefined);
175
+ const cleanup = await Promise.allSettled(started.map((node) => node.runtime.dispose()));
176
+ if (cleanup.every((result) => result.status === "fulfilled"))
177
+ await releaseOrigin();
157
178
  throw cause;
158
179
  }
159
180
  }
@@ -167,10 +188,7 @@ async function openProjectTracking(nameOrOptions, track) {
167
188
  throw new Error(`Browser runtime unsupported: ${capabilities.missing.join(", ")}`);
168
189
  const name = options.name ?? "app";
169
190
  const manifestUrl = options.manifestUrl ?? imageManifestPath(name);
170
- const response = await nativeFetch(manifestUrl, { cache: "no-store" });
171
- if (!response.ok)
172
- throw new Error(`No image named ${name}: ${manifestUrl} answered ${response.status}. Build one with browser-substrate build.`);
173
- const read = validateImageManifest(await response.json());
191
+ const read = await fetchImageManifest(name, manifestUrl);
174
192
  // A caller's command in place of the image's own: the project, its packages
175
193
  // and its programs as the image places them, and this run in them.
176
194
  const manifest = options.command === undefined || read.setup.kind === "dockerfile" ? read : { ...read, setup: { ...read.setup, dev: options.command } };
@@ -199,8 +217,9 @@ async function openProjectTracking(nameOrOptions, track) {
199
217
  };
200
218
  // Begun before anything else: nothing waits for it, and whatever asks for
201
219
  // an entry first finds it kept, or fetches it as it would have.
202
- if (manifest.caches && !isDryRun())
203
- void restoreImageCaches(manifest.caches.url, browserProjectId(manifest.project ?? `substrate-${options.name ?? "app"}`), nativeFetch).catch((cause) => console.warn(`[caches] ${cause instanceof Error ? cause.message : String(cause)}`));
220
+ const restoringCaches = manifest.caches && !isDryRun()
221
+ ? restoreImageCaches(manifest.caches.url, browserProjectId(manifest.project ?? `substrate-${options.name ?? "app"}`), nativeFetch).catch((cause) => console.warn(`[caches] ${cause instanceof Error ? cause.message : String(cause)}`))
222
+ : Promise.resolve();
204
223
  const output = options.onOutput ?? (() => undefined);
205
224
  // A step that held the page a while says so when the next begins: a boot
206
225
  // that stalls is found in its own log, not by guessing which step it was.
@@ -266,6 +285,7 @@ async function openProjectTracking(nameOrOptions, track) {
266
285
  bundler: { workerUrl: "/bundler-worker.js", rollupModuleUrl: "/rollup/rollup.browser.js", esbuildModuleUrl: "/esbuild/browser.min.js", esbuildWasmUrl: "/esbuild/esbuild.wasm" },
267
286
  // The shell's own Rolldown binding, or the project's, built for its version at image time.
268
287
  rolldownModuleUrl: manifest.rolldown?.url ?? "/rolldown/factory.mjs",
288
+ ...(manifest.rolldown ? { rolldownVersion: manifest.rolldown.version } : {}),
269
289
  // Node's type stripper, amaro, served from this origin: a `.ts` entry runs as Node runs one.
270
290
  typeStripperModuleUrl: "/amaro/amaro.js",
271
291
  // What the module stand-ins compute with, served from this origin: Prisma's schema parser.
@@ -307,6 +327,7 @@ async function openProjectTracking(nameOrOptions, track) {
307
327
  status(`The runtime started in ${Math.round(performance.now() - runtimeStartedAt)} ms`);
308
328
  bootStage({ id: "machine", label: "Starting the machine", state: "done", detail: isolated ? "isolated runtime" : "runtime and store" });
309
329
  const { runtime, container } = node;
330
+ let closeCookies;
310
331
  const preparedProject = Boolean(node.workspaceStore?.preparedImage && node.workspaceStore.preparedImage === manifest.layers?.digest);
311
332
  /** Whether this open's prepared install already placed this archive at this root (ADR-0085). */
312
333
  const preparedTree = (root, integrity) => preparedProject
@@ -326,27 +347,16 @@ async function openProjectTracking(nameOrOptions, track) {
326
347
  }
327
348
  // Once the app has answered its first page, what a first boot read is the
328
349
  // image's startup set: the host keeps it for the next first boot. A repeat
329
- // boot holds every range earlier boots read, so it records nothing. The set
330
- // is kept in the order the boot first held each range, sampled as it runs,
331
- // so the next first boot fetches the World's ranges before the app's.
350
+ // boot holds every range earlier boots read, so it records nothing. Every
351
+ // reader stamps a range's first read, so one read of the set at the end
352
+ // gives the order the boot first needed each range.
332
353
  let startupRecorded = false;
333
- const startupOrder = new Set();
334
- const sampleStartup = async () => { for (const range of await imageStartup.imageStartupSet())
335
- startupOrder.add(range); };
336
- const startupSampler = startupUrl && imageStartup.imageStartupSet
337
- ? setInterval(() => { if (!preparedProject || startupRecorded)
338
- clearInterval(startupSampler);
339
- else
340
- void sampleStartup(); }, 250)
341
- : undefined;
342
354
  const recordStartup = () => {
343
- if (startupSampler !== undefined)
344
- clearInterval(startupSampler);
345
355
  if (!startupUrl || !preparedProject || !imageStartup.imageStartupSet || startupRecorded)
346
356
  return;
347
357
  startupRecorded = true;
348
- void sampleStartup().then(() => startupOrder.size === 0 ? undefined
349
- : nativeFetch(startupUrl, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ version: 1, ranges: [...startupOrder] }) }))
358
+ void imageStartup.imageStartupSet().then((ranges) => ranges.length === 0 ? undefined
359
+ : nativeFetch(startupUrl, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ version: 1, ranges }) }))
350
360
  .catch((cause) => console.warn(`[startup] ${cause instanceof Error ? cause.message : String(cause)}`));
351
361
  };
352
362
  // An image that links a Node install runs Node: its worker loads the
@@ -435,6 +445,7 @@ async function openProjectTracking(nameOrOptions, track) {
435
445
  // before the worker is, which binds the bridge's message handler.
436
446
  if (bridge.initServiceWorker) {
437
447
  const cookies = await openPersistentBrowserCookieJar(projectId);
448
+ closeCookies = () => cookies.dispose();
438
449
  attachTabnodeCookieJar(container.serverBridge, cookies);
439
450
  exposeBrowserCookieJar(window, cookies);
440
451
  }
@@ -852,6 +863,7 @@ async function openProjectTracking(nameOrOptions, track) {
852
863
  * re-applied. Then the World's own runtime brings its twins up. Answers
853
864
  * the way to run a command attached to the World.
854
865
  */
866
+ let stopWorld;
855
867
  const attachWorld = async (world, preparedBodies) => {
856
868
  const root = `${WORLDS_ROOT}/${world.name}`;
857
869
  const record = `${root}/.substrate-world.json`;
@@ -923,6 +935,24 @@ async function openProjectTracking(nameOrOptions, track) {
923
935
  const cli = /^node "([^"]+)"/mu.exec(stub)?.[1];
924
936
  if (!cli)
925
937
  throw new Error(`World ${world.name}: node_modules/.bin/volter-world is not a stub naming the CLI.`);
938
+ stopWorld = async (say) => {
939
+ const run = async (verb) => {
940
+ say(`World ${world.name}: ${verb}`);
941
+ const command = `node ${cli} ${verb} ${world.name} --root ${root} --json`;
942
+ const result = await runtime.start(command, { cwd: root, env }).result;
943
+ if (result.exitCode !== 0)
944
+ throw new Error(`World ${world.name}: ${verb} refused shutdown (exit ${result.exitCode}). ${result.stderr.trim()}`);
945
+ say(`World ${world.name}: ${verb} finished`);
946
+ return result.stdout;
947
+ };
948
+ // Retire only consumers the CLI proves no longer live; down retains data.
949
+ await run("consumers retire");
950
+ await run("down");
951
+ const stopped = JSON.parse(await run("status"));
952
+ if (stopped.running !== false || !Array.isArray(stopped.livePids) || stopped.livePids.length) {
953
+ throw new Error(`World ${world.name}: shutdown was not verified. Saved state has not been cleared.`);
954
+ }
955
+ };
926
956
  // The World's data is its account: an instance here boots over it, never from nothing.
927
957
  const up = `node ${cli} up ${root}/${world.config} --name ${world.name} ${world.environmentOutputFlag ?? "--env-file"} ${root}/world.env --root ${root} --keep-state`;
928
958
  status(`$ ${up}`);
@@ -1397,9 +1427,6 @@ async function openProjectTracking(nameOrOptions, track) {
1397
1427
  await worldAttachment.catch(() => undefined);
1398
1428
  await Promise.allSettled(preparation);
1399
1429
  setupFailure = error;
1400
- // A boot whose app never answered read no startup set.
1401
- if (startupSampler !== undefined)
1402
- clearInterval(startupSampler);
1403
1430
  // The page's log says why, in full, the moment setup stops.
1404
1431
  output({ stream: "stderr", text: `\nsetup stopped: ${describeFailure(error)}\n\n` });
1405
1432
  }
@@ -1516,7 +1543,7 @@ async function openProjectTracking(nameOrOptions, track) {
1516
1543
  try {
1517
1544
  // A source edit may move preparation to the host. Keep that fact even
1518
1545
  // if a later edit returns to a browser-buildable image.
1519
- const latest = validateImageManifest(await (await fetch(imageManifestPath(name), { cache: "no-store" })).json());
1546
+ const latest = await fetchImageManifest(name, manifestUrl);
1520
1547
  recordPreparation(execution, latest.preparation);
1521
1548
  // A lockfile edit is another plan; the setup that runs again reads the current one.
1522
1549
  if (latest.dependencies)
@@ -1854,6 +1881,24 @@ async function openProjectTracking(nameOrOptions, track) {
1854
1881
  close: async () => { unlisten?.(); unlisten = undefined; await handle.close(); },
1855
1882
  };
1856
1883
  },
1884
+ async shutdown(say = () => undefined) {
1885
+ // Startup must settle before issuing lifecycle commands against its World.
1886
+ await project.ready.catch(() => undefined);
1887
+ await restoringCaches;
1888
+ setupAbort.abort(new Error("The project is shutting down."));
1889
+ await feed?.close();
1890
+ await feed?.idle;
1891
+ say("Stopping the application");
1892
+ await stack?.stop();
1893
+ process?.kill();
1894
+ await process?.result.catch(() => undefined);
1895
+ if (stopWorld) {
1896
+ say("Stopping the World");
1897
+ await stopWorld(say);
1898
+ }
1899
+ say("Closing runtime and storage workers");
1900
+ await project.dispose();
1901
+ },
1857
1902
  dispose() {
1858
1903
  if (disposePromise)
1859
1904
  return disposePromise;
@@ -1869,8 +1914,6 @@ async function openProjectTracking(nameOrOptions, track) {
1869
1914
  }
1870
1915
  };
1871
1916
  setupAbort.abort(new Error("The project was closed."));
1872
- if (startupSampler !== undefined)
1873
- clearInterval(startupSampler);
1874
1917
  await release(() => feed?.close());
1875
1918
  await release(async () => { await feed?.idle; });
1876
1919
  await release(async () => { await stack?.stop(); });
@@ -1883,6 +1926,7 @@ async function openProjectTracking(nameOrOptions, track) {
1883
1926
  await release(() => { stopUnavailableChromium?.(); });
1884
1927
  await release(() => bridge?.dispose());
1885
1928
  await release(() => runtime.dispose());
1929
+ await release(async () => { await closeCookies?.(); });
1886
1930
  if (failures.length === 1)
1887
1931
  throw failures[0];
1888
1932
  if (failures.length > 1)