@volter/world-runtime 3.0.102 → 3.0.104

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.
@@ -220,8 +220,22 @@ export function preparedCluster(worldConfig) {
220
220
  * another password takes the same cluster and authenticates against its own. Extensions are loaded into the module
221
221
  * at each open, never created in the cluster by the open path.
222
222
  */
223
+ /** This package's own manifest, found by its name walking up from this module: the package root from src/ in the
224
+ * repository and from dist/src/ in the published package alike, where a fixed `..` reached dist/package.json. */
225
+ function runtimeManifest() {
226
+ for (let dir = here();; dir = dirname(dir)) {
227
+ const candidate = join(dir, 'package.json');
228
+ if (existsSync(candidate)) {
229
+ const manifest = JSON.parse(readFileSync(candidate, 'utf8'));
230
+ if (manifest.name === '@volter/world-runtime')
231
+ return manifest;
232
+ }
233
+ if (dirname(dir) === dir)
234
+ throw new Error(`no @volter/world-runtime package.json above ${here()}`);
235
+ }
236
+ }
223
237
  function preparedStamp(service) {
224
- const manifest = JSON.parse(readFileSync(join(here(), '..', 'package.json'), 'utf8'));
238
+ const manifest = runtimeManifest();
225
239
  const pglite = manifest.dependencies?.['@electric-sql/pglite'];
226
240
  if (!pglite || !/^\d+\.\d+\.\d+$/.test(pglite))
227
241
  throw new Error('world-runtime pins no exact @electric-sql/pglite version');
@@ -14,6 +14,7 @@ export function superviseWorldUp(configId, options) {
14
14
  });
15
15
  let reply;
16
16
  let failure;
17
+ let settled = false;
17
18
  const cancel = (signal) => {
18
19
  if (child.connected)
19
20
  child.send({ type: 'cancel', signal }, error => { if (error)
@@ -23,8 +24,31 @@ export function superviseWorldUp(configId, options) {
23
24
  const onTerm = () => cancel('SIGTERM');
24
25
  process.on('SIGINT', onInt);
25
26
  process.on('SIGTERM', onTerm);
26
- child.on('error', error => { failure = error; });
27
- child.on('message', message => { reply = message; });
27
+ // `up` is done the moment the owner answers: the caller settles on the reply and lets go of the owner (its channel
28
+ // and its handle), never waiting on its 'close'. 'close' waits for every inherited descriptor to end, and in a
29
+ // browser tab's kernel a service the owner started still held one: run 29 printed "returning: the World is up" at
30
+ // 7.54 s and this process stayed until the 10 s stop. With no reply, the owner's channel closing and its exit settle it.
31
+ const settle = () => {
32
+ if (settled)
33
+ return;
34
+ settled = true;
35
+ offSignal('SIGINT', onInt);
36
+ offSignal('SIGTERM', onTerm);
37
+ if (child.connected)
38
+ child.disconnect();
39
+ child.unref();
40
+ if (failure)
41
+ reject(failure);
42
+ else if (reply && 'error' in reply)
43
+ reject(new Error(reply.error));
44
+ else if (reply)
45
+ done(reply.instance);
46
+ else
47
+ reject(new Error(`World boot owner exited without a result (${child.signalCode ?? child.exitCode}); inspect \`volter-world status\` and the lifecycle log`));
48
+ };
49
+ child.on('error', error => { failure = error; if (!child.pid)
50
+ settle(); });
51
+ child.on('message', message => { reply = message; settle(); });
28
52
  child.once('spawn', () => {
29
53
  const request = { type: 'start', configId, options: { ...options, root: resolve(options.root ?? process.cwd()) } };
30
54
  child.send(request, error => { if (error) {
@@ -33,17 +57,14 @@ export function superviseWorldUp(configId, options) {
33
57
  child.disconnect();
34
58
  } });
35
59
  });
36
- child.once('close', (code, signal) => {
37
- offSignal('SIGINT', onInt);
38
- offSignal('SIGTERM', onTerm);
39
- if (failure)
40
- reject(failure);
41
- else if (reply && 'error' in reply)
42
- reject(new Error(reply.error));
43
- else if (reply && code === 0)
44
- done(reply.instance);
60
+ // No reply: every message the channel carried has been delivered once it disconnects; the owner's exit says why.
61
+ child.once('disconnect', () => {
62
+ if (reply)
63
+ return;
64
+ if (child.exitCode !== null || child.signalCode !== null)
65
+ settle();
45
66
  else
46
- reject(new Error(`World boot owner exited without a result (${signal ?? code}); inspect \`volter-world status\` and the lifecycle log`));
67
+ child.once('exit', settle);
47
68
  });
48
69
  });
49
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world-runtime",
3
- "version": "3.0.102",
3
+ "version": "3.0.104",
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",
@@ -69,14 +69,14 @@
69
69
  "node": ">=22.3"
70
70
  },
71
71
  "peerDependencies": {
72
- "@volter/world-core": "3.0.102",
73
- "@volter/world-console": "3.0.97"
72
+ "@volter/world-core": "3.0.104",
73
+ "@volter/world-console": "3.0.99"
74
74
  },
75
75
  "dependencies": {
76
76
  "@electric-sql/pglite": "0.5.8",
77
77
  "@electric-sql/pglite-pgvector": "0.0.9",
78
78
  "@volter/world-access": "3.0.1",
79
- "@volter/world-core": "3.0.102",
79
+ "@volter/world-core": "3.0.104",
80
80
  "mongodb": "~6.20.0",
81
81
  "pg-gateway": "0.3.0-beta.4",
82
82
  "smol-toml": "^1.8.0",
@@ -220,8 +220,21 @@ export function preparedCluster(worldConfig: string): { directory: string; stamp
220
220
  * another password takes the same cluster and authenticates against its own. Extensions are loaded into the module
221
221
  * at each open, never created in the cluster by the open path.
222
222
  */
223
+ /** This package's own manifest, found by its name walking up from this module: the package root from src/ in the
224
+ * repository and from dist/src/ in the published package alike, where a fixed `..` reached dist/package.json. */
225
+ function runtimeManifest(): { version?: string; dependencies?: Record<string, string> } {
226
+ for (let dir = here(); ; dir = dirname(dir)) {
227
+ const candidate = join(dir, 'package.json');
228
+ if (existsSync(candidate)) {
229
+ const manifest = JSON.parse(readFileSync(candidate, 'utf8')) as { name?: string; version?: string; dependencies?: Record<string, string> };
230
+ if (manifest.name === '@volter/world-runtime') return manifest;
231
+ }
232
+ if (dirname(dir) === dir) throw new Error(`no @volter/world-runtime package.json above ${here()}`);
233
+ }
234
+ }
235
+
223
236
  function preparedStamp(service: InfraService): string {
224
- const manifest = JSON.parse(readFileSync(join(here(), '..', 'package.json'), 'utf8')) as { version?: string; dependencies?: Record<string, string> };
237
+ const manifest = runtimeManifest();
225
238
  const pglite = manifest.dependencies?.['@electric-sql/pglite'];
226
239
  if (!pglite || !/^\d+\.\d+\.\d+$/.test(pglite)) throw new Error('world-runtime pins no exact @electric-sql/pglite version');
227
240
  return `${JSON.stringify({ runtime: manifest.version ?? 'unversioned', pglite, user: service.postgres!.user, database: service.postgres!.database })}\n`;
package/src/up-task.ts CHANGED
@@ -20,6 +20,7 @@ export function superviseWorldUp(configId: string, options: UpWorldOptions): Pro
20
20
  });
21
21
  let reply: UpTaskReply | undefined;
22
22
  let failure: Error | undefined;
23
+ let settled = false;
23
24
  const cancel = (signal: 'SIGINT' | 'SIGTERM') => {
24
25
  if (child.connected) child.send({ type: 'cancel', signal }, error => { if (error) failure ??= error; });
25
26
  };
@@ -27,19 +28,33 @@ export function superviseWorldUp(configId: string, options: UpWorldOptions): Pro
27
28
  const onTerm = () => cancel('SIGTERM');
28
29
  process.on('SIGINT', onInt);
29
30
  process.on('SIGTERM', onTerm);
30
- child.on('error', error => { failure = error; });
31
- child.on('message', message => { reply = message as UpTaskReply; });
32
- child.once('spawn', () => {
33
- const request: UpTaskRequest = { type: 'start', configId, options: { ...options, root: resolve(options.root ?? process.cwd()) } };
34
- child.send(request, error => { if (error) { failure = error; if (child.connected) child.disconnect(); } });
35
- });
36
- child.once('close', (code, signal) => {
31
+ // `up` is done the moment the owner answers: the caller settles on the reply and lets go of the owner (its channel
32
+ // and its handle), never waiting on its 'close'. 'close' waits for every inherited descriptor to end, and in a
33
+ // browser tab's kernel a service the owner started still held one: run 29 printed "returning: the World is up" at
34
+ // 7.54 s and this process stayed until the 10 s stop. With no reply, the owner's channel closing and its exit settle it.
35
+ const settle = () => {
36
+ if (settled) return;
37
+ settled = true;
37
38
  offSignal('SIGINT', onInt);
38
39
  offSignal('SIGTERM', onTerm);
40
+ if (child.connected) child.disconnect();
41
+ child.unref();
39
42
  if (failure) reject(failure);
40
43
  else if (reply && 'error' in reply) reject(new Error(reply.error));
41
- else if (reply && code === 0) done(reply.instance);
42
- else reject(new Error(`World boot owner exited without a result (${signal ?? code}); inspect \`volter-world status\` and the lifecycle log`));
44
+ else if (reply) done(reply.instance);
45
+ else reject(new Error(`World boot owner exited without a result (${child.signalCode ?? child.exitCode}); inspect \`volter-world status\` and the lifecycle log`));
46
+ };
47
+ child.on('error', error => { failure = error; if (!child.pid) settle(); });
48
+ child.on('message', message => { reply = message as UpTaskReply; settle(); });
49
+ child.once('spawn', () => {
50
+ const request: UpTaskRequest = { type: 'start', configId, options: { ...options, root: resolve(options.root ?? process.cwd()) } };
51
+ child.send(request, error => { if (error) { failure = error; if (child.connected) child.disconnect(); } });
52
+ });
53
+ // No reply: every message the channel carried has been delivered once it disconnects; the owner's exit says why.
54
+ child.once('disconnect', () => {
55
+ if (reply) return;
56
+ if (child.exitCode !== null || child.signalCode !== null) settle();
57
+ else child.once('exit', settle);
43
58
  });
44
59
  });
45
60
  }