@volter/world-runtime 3.0.100 → 3.0.102

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/src/schema.ts CHANGED
@@ -97,16 +97,15 @@ export type WorldExternalDiscover = {
97
97
  };
98
98
 
99
99
  /**
100
- * Optional readiness probe, polled after `up` until it passes or `timeoutMs` elapses. Exactly one of:
101
- * - `command`/`args`: a command that must exit 0,
102
- * - `httpUrl`: an HTTP(S) URL that must return a 2xx/3xx response,
103
- * - `stdoutMatch`: a regex that must appear in the `up` command's captured stdout/stderr log.
100
+ * Optional readiness probe, polled after `up` until it passes or `timeoutMs` elapses: the service answering its own
101
+ * protocol (architecture D9). Exactly one of:
102
+ * - `command`/`args`: a command that asks the service and exits 0 when it answers,
103
+ * - `httpUrl`: an HTTP(S) URL that must return a 2xx/3xx response.
104
104
  */
105
105
  export type WorldExternalReadyWhen = {
106
106
  command?: string;
107
107
  args?: string[];
108
108
  httpUrl?: string;
109
- stdoutMatch?: string;
110
109
  timeoutMs?: number;
111
110
  intervalMs?: number;
112
111
  };
@@ -224,7 +223,6 @@ export type WorldServiceConfig = {
224
223
  * service truly serves, so `up` returning means "ready" (no post-up re-polling needed):
225
224
  * - `{ httpUrl }` — poll until 2xx/3xx. `${url}`/`${host}`/`${port}` resolve to the allocated port.
226
225
  * - `{ command, args }` — poll until the command exits 0 (runs with the service env, incl. `PORT`).
227
- * - `{ stdoutMatch }` — poll until a regex matches the service's stdout/stderr log.
228
226
  * 'external' services use `external.readyWhen` instead.
229
227
  */
230
228
  ready?: 'tcp' | WorldExternalReadyWhen;
@@ -461,26 +459,21 @@ function assertValidRegex(pattern: string, what: string, path: string): void {
461
459
  }
462
460
 
463
461
  /** Shared validator for a readiness probe — used by both `external.readyWhen` and a process/twin
464
- * service's `ready` object. Exactly one of command/httpUrl/stdoutMatch, correct types, and any
465
- * stdoutMatch regex must compile (so a typo fails at config time, not mid-boot). */
462
+ * service's `ready` object. Exactly one of command/httpUrl, with correct types. */
466
463
  function assertReadyWhen(ready: unknown, what: string, path: string): void {
467
464
  if (!ready || typeof ready !== 'object' || Array.isArray(ready)) {
468
465
  throw new Error(`${what} must be an object in ${path}`);
469
466
  }
470
- assertKeys(ready as Record<string, unknown>, ['command', 'args', 'httpUrl', 'stdoutMatch', 'timeoutMs', 'intervalMs'], what, path);
467
+ assertKeys(ready as Record<string, unknown>, ['command', 'args', 'httpUrl', 'timeoutMs', 'intervalMs'], what, path);
471
468
  const r = ready as WorldExternalReadyWhen;
472
- const probes = [r.command !== undefined, r.httpUrl !== undefined, r.stdoutMatch !== undefined].filter(Boolean).length;
469
+ const probes = [r.command !== undefined, r.httpUrl !== undefined].filter(Boolean).length;
473
470
  if (probes !== 1) {
474
- throw new Error(`${what} must set exactly one of command/httpUrl/stdoutMatch in ${path}`);
471
+ throw new Error(`${what} must set exactly one of command/httpUrl in ${path}`);
475
472
  }
476
473
  if (r.command !== undefined && typeof r.command !== 'string') throw new Error(`${what}.command must be a string in ${path}`);
477
474
  if (r.args !== undefined && (!Array.isArray(r.args) || r.args.some((arg) => typeof arg !== 'string'))) throw new Error(`${what}.args must be a string array in ${path}`);
478
475
  if (r.args !== undefined && r.command === undefined) throw new Error(`${what}.args requires command in ${path}`);
479
476
  if (r.httpUrl !== undefined && typeof r.httpUrl !== 'string') throw new Error(`${what}.httpUrl must be a string in ${path}`);
480
- if (r.stdoutMatch !== undefined) {
481
- if (typeof r.stdoutMatch !== 'string') throw new Error(`${what}.stdoutMatch must be a string in ${path}`);
482
- assertValidRegex(r.stdoutMatch, `${what}.stdoutMatch`, path);
483
- }
484
477
  for (const key of ['timeoutMs', 'intervalMs'] as const) {
485
478
  if (r[key] !== undefined && (!Number.isInteger(r[key]) || r[key]! <= 0)) throw new Error(`${what}.${key} must be a positive integer in ${path}`);
486
479
  }
@@ -83,12 +83,6 @@ function split(line: string): { output: string; record?: { event?: unknown; at?:
83
83
  try { return { output: line.slice(0, at), record: JSON.parse(line.slice(at)) as { event?: unknown; at?: unknown } }; } catch { return { output: line }; }
84
84
  }
85
85
 
86
- /** A log's text as the service wrote it, without the recorder's records: what a `stdoutMatch` readiness pattern is
87
- * matched against. */
88
- export function serviceOutput(text: string): string {
89
- return text.includes(MARK) ? text.split('\n').map((line) => split(line).output).join('\n') : text;
90
- }
91
-
92
86
  /** The last run's end in a service log: its start, its exit record (absent while it runs, or when its parent was killed
93
87
  * with it), and the output it wrote last. Undefined for a log the parent never wrote to (a co-located twin's host log),
94
88
  * which has no runs to tell apart. A run is bounded by its start record or, for a spawn that failed before starting
package/src/up-cli.ts CHANGED
@@ -22,7 +22,17 @@ export async function upCommand(subject: string | undefined, rest: string[], roo
22
22
  // --keep-state: boot over the state the World's directory already holds (a World carried somewhere else, e.g. a
23
23
  // browser tab's image, comes up over its data instead of starting empty, as a fresh `up` does)
24
24
  const keepState = rest.includes('--keep-state');
25
+ // --app-url: where the booter serves the app, recorded as `volter-world app-url --set` records it, once the World is
26
+ // up and in this process: a booter that knows the address before the app listens (a browser tab's preview origin)
27
+ // saves a whole command between up and the app's start.
28
+ const appUrl = optionValue(rest, '--app-url') || undefined;
29
+ if (appUrl !== undefined && !(URL.canParse(appUrl) && /^https?:$/.test(new URL(appUrl).protocol))) throw new Error(`volter-world up: --app-url "${appUrl}" must be an http(s) URL`);
25
30
  const instance = await superviseWorldUp(subject, { name, root, mode, envFile, forceEnvOutput: rest.includes('--force'), owner: ownerOption(rest), ...(isolation ? { isolation } : {}), ...(keepState ? { keepState: true } : {}) });
31
+ if (appUrl !== undefined) {
32
+ const { setAppUrl } = await import('./app-url.ts');
33
+ setAppUrl(instance.name, appUrl, { root });
34
+ process.stderr.write(`volter-world up ${instance.name}: app url recorded: ${appUrl}\n`);
35
+ }
26
36
  if (rest.includes('--json')) process.stdout.write(JSON.stringify(instance, null, 2) + '\n');
27
37
  else printReady(instance);
28
38
  }