@volter/world-runtime 2.0.55 → 3.0.0

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 (121) hide show
  1. package/dist/known-external-services.json +5 -4
  2. package/dist/src/actions-runner-cli.d.ts +2 -0
  3. package/dist/src/actions-runner-cli.js +25 -0
  4. package/dist/src/actions-runner.d.ts +18 -0
  5. package/dist/src/actions-runner.js +281 -0
  6. package/dist/src/ancestry.js +1 -1
  7. package/dist/src/app-url.js +1 -1
  8. package/dist/src/backings.d.ts +29 -0
  9. package/dist/src/backings.js +45 -0
  10. package/dist/src/branch.js +3 -2
  11. package/dist/src/browser-proxy-cli.js +1 -1
  12. package/dist/src/catalog-index.d.ts +38 -0
  13. package/dist/src/catalog-index.js +37 -0
  14. package/dist/src/catalog.d.ts +16 -3
  15. package/dist/src/catalog.js +69 -26
  16. package/dist/src/changeset.d.ts +1 -1
  17. package/dist/src/changeset.js +9 -11
  18. package/dist/src/cli-args.d.ts +4 -0
  19. package/dist/src/cli-args.js +18 -0
  20. package/dist/src/cli-commands.d.ts +1 -0
  21. package/dist/src/cli-commands.js +1216 -0
  22. package/dist/src/cli.js +8 -1300
  23. package/dist/src/configs.js +1 -1
  24. package/dist/src/console-apart.d.ts +2 -2
  25. package/dist/src/console-apart.js +5 -5
  26. package/dist/src/consumers.js +1 -1
  27. package/dist/src/covers.d.ts +1 -1
  28. package/dist/src/covers.js +94 -30
  29. package/dist/src/env-output.d.ts +1 -1
  30. package/dist/src/env-output.js +2 -2
  31. package/dist/src/fixture-env.js +18 -26
  32. package/dist/src/host-cli.js +4 -2
  33. package/dist/src/host-fault-fixture.js +6 -0
  34. package/dist/src/host-ready.d.ts +8 -2
  35. package/dist/src/host-ready.js +16 -4
  36. package/dist/src/host-worker.js +6 -1
  37. package/dist/src/host.d.ts +2 -0
  38. package/dist/src/host.js +9 -1
  39. package/dist/src/index.d.ts +2 -2
  40. package/dist/src/index.js +2 -2
  41. package/dist/src/init.d.ts +33 -23
  42. package/dist/src/init.js +228 -96
  43. package/dist/src/lifecycle-record.js +2 -2
  44. package/dist/src/local-branches.js +1 -1
  45. package/dist/src/origin.d.ts +1 -1
  46. package/dist/src/origin.js +12 -10
  47. package/dist/src/pack-facts.d.ts +30 -6
  48. package/dist/src/pack-facts.js +13 -3
  49. package/dist/src/pglite-backing.js +2 -1
  50. package/dist/src/placeholder.d.ts +1 -1
  51. package/dist/src/placeholder.js +12 -4
  52. package/dist/src/project-inspect.d.ts +1 -1
  53. package/dist/src/project-inspect.js +2 -2
  54. package/dist/src/redirect-proxy.d.ts +5 -0
  55. package/dist/src/redirect-proxy.js +8 -1
  56. package/dist/src/redis-backing.js +4 -2
  57. package/dist/src/reflect.js +1 -1
  58. package/dist/src/root.d.ts +11 -33
  59. package/dist/src/root.js +28 -59
  60. package/dist/src/runtime-test-support.js +1 -1
  61. package/dist/src/runtime.d.ts +7 -1
  62. package/dist/src/runtime.js +317 -53
  63. package/dist/src/schema.d.ts +27 -5
  64. package/dist/src/schema.js +13 -12
  65. package/dist/src/served-world.d.ts +11 -35
  66. package/dist/src/served-world.js +54 -141
  67. package/dist/src/tail.js +1 -1
  68. package/dist/src/up-cli.d.ts +3 -0
  69. package/dist/src/up-cli.js +55 -0
  70. package/dist/src/websocket-relay.d.ts +1 -1
  71. package/dist/src/world-origins.d.ts +1 -1
  72. package/dist/src/world-origins.js +1 -1
  73. package/dist/src/world-view.d.ts +1 -1
  74. package/dist/src/world-view.js +2 -2
  75. package/known-external-services.json +5 -4
  76. package/package.json +11 -11
  77. package/src/actions-runner-cli.ts +24 -0
  78. package/src/actions-runner.ts +244 -0
  79. package/src/ancestry.ts +1 -1
  80. package/src/app-url.ts +1 -1
  81. package/src/backings.ts +55 -0
  82. package/src/branch.ts +3 -2
  83. package/src/browser-proxy-cli.ts +1 -1
  84. package/src/catalog-index.ts +44 -0
  85. package/src/catalog.ts +67 -24
  86. package/src/changeset.ts +10 -13
  87. package/src/cli-args.ts +17 -0
  88. package/src/cli-commands.ts +1070 -0
  89. package/src/cli.ts +9 -1139
  90. package/src/configs.ts +1 -1
  91. package/src/console-apart.ts +5 -5
  92. package/src/consumers.ts +1 -1
  93. package/src/covers.ts +83 -30
  94. package/src/env-output.ts +2 -2
  95. package/src/fixture-env.ts +18 -26
  96. package/src/host-cli.ts +4 -2
  97. package/src/host-ready.ts +13 -4
  98. package/src/host-worker.ts +6 -2
  99. package/src/host.ts +11 -2
  100. package/src/index.ts +2 -2
  101. package/src/init.ts +222 -101
  102. package/src/lifecycle-record.ts +2 -2
  103. package/src/local-branches.ts +1 -1
  104. package/src/origin.ts +12 -10
  105. package/src/pack-facts.ts +22 -6
  106. package/src/pglite-backing.ts +2 -1
  107. package/src/placeholder.ts +13 -4
  108. package/src/project-inspect.ts +2 -2
  109. package/src/redirect-proxy.ts +9 -1
  110. package/src/redis-backing.ts +4 -2
  111. package/src/reflect.ts +1 -1
  112. package/src/root.ts +33 -65
  113. package/src/runtime-test-support.ts +1 -1
  114. package/src/runtime.ts +261 -55
  115. package/src/schema.ts +26 -13
  116. package/src/served-world.ts +50 -130
  117. package/src/tail.ts +1 -1
  118. package/src/up-cli.ts +55 -0
  119. package/src/websocket-relay.ts +1 -1
  120. package/src/world-origins.ts +1 -1
  121. package/src/world-view.ts +3 -3
@@ -1,16 +1,19 @@
1
1
  // The catalog — where the twins a world can run come from (docs/concepts/worlds.md
2
- // #the-config-and-the-running-world). Two sources answer one interface: this CHECKOUT (`packages/twin/<vendor>/`, the
3
- // catalog as the repo holds it) and an INSTALLED catalog (`@volter/twin-<vendor>` packages under
2
+ // #the-config-and-the-running-world). Two sources answer one interface: a pack-repository CHECKOUT and an
3
+ // INSTALLED catalog (`@volter/twin-<vendor>` packages under
4
4
  // `node_modules`, walked up from the world root — hoisting-proof, and independent of the
5
5
  // packages' `exports` maps, which do not expose `package.json`). A world born in an app repo
6
6
  // names its twins as packages and the runtime resolves them at boot; a world born here names the
7
- // checkout-relative paths, as before.
7
+ // selected checkout paths.
8
8
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
9
9
  import { spawnSync } from 'node:child_process';
10
10
  import { dirname, join, resolve } from 'node:path';
11
+ import { overlayPackFacts } from "./pack-facts.js";
12
+ import { installedPackageDir, isDerivedPack, packRepository } from '@volter/world-core/runtime';
11
13
  export const TWIN_PACKAGE_PREFIX = '@volter/twin-';
12
- /** Packages under the `@volter/twin-` prefix that are NOT twins. */
13
14
  export function twinPackageName(vendor) { return `${TWIN_PACKAGE_PREFIX}${vendor}`; }
15
+ /** A derived (Protocol 3) pack carries the surface derive-pack generated from its spec. */
16
+ const major = (version) => Number(/^(\d+)/.exec(version ?? '')?.[1] ?? 0);
14
17
  function packageVersion(dir) {
15
18
  const p = join(dir, 'package.json');
16
19
  if (!existsSync(p))
@@ -23,7 +26,8 @@ function packageVersion(dir) {
23
26
  return undefined;
24
27
  }
25
28
  }
26
- function isPackDir(dir) { return packCli(dir) !== undefined; }
29
+ /** A twin a catalog lists: a derived pack with its CLI. */
30
+ function isPackDir(dir) { return packCli(dir) !== undefined && isDerivedPack(dir); }
27
31
  function manifest(dir) { const p = join(dir, 'package.json'); if (!existsSync(p))
28
32
  return undefined; try {
29
33
  return JSON.parse(readFileSync(p, 'utf8'));
@@ -51,18 +55,7 @@ export function packCli(dir) {
51
55
  return existsSync(join(dir, 'src', 'cli.ts')) ? join(dir, 'src', 'cli.ts') : undefined;
52
56
  }
53
57
  /** The nearest `node_modules/<name>` above `from` that holds a package.json — node's own walk. */
54
- export function findInstalledPackage(from, name) {
55
- let dir = resolve(from);
56
- for (;;) {
57
- const candidate = join(dir, 'node_modules', ...name.split('/'));
58
- if (existsSync(join(candidate, 'package.json')))
59
- return candidate;
60
- const up = dirname(dir);
61
- if (up === dir)
62
- return undefined;
63
- dir = up;
64
- }
65
- }
58
+ export const findInstalledPackage = installedPackageDir;
66
59
  /** Every installed `@volter/twin-<vendor>` visible from `from`, nearest wins. */
67
60
  function installedTwins(from) {
68
61
  const found = new Map();
@@ -74,6 +67,8 @@ function installedTwins(from) {
74
67
  if (!entry.isDirectory() && !entry.isSymbolicLink())
75
68
  continue;
76
69
  const name = `@volter/${entry.name}`;
70
+ if (name === '@volter/twin-standard')
71
+ continue; // the platform's standard is tooling, not a vendor pack
77
72
  if (!name.startsWith(TWIN_PACKAGE_PREFIX))
78
73
  continue; // the platform's packages are `@volter/world-*`: outside the prefix by name
79
74
  const vendor = name.slice(TWIN_PACKAGE_PREFIX.length);
@@ -98,17 +93,18 @@ function fnv1a(text) {
98
93
  return h.toString(16).padStart(8, '0');
99
94
  }
100
95
  export function checkoutCatalog(root) {
101
- const packsDir = resolve(root, 'packages', 'twin');
96
+ const packsDir = resolve(root);
102
97
  const vendors = () => readdirSync(packsDir, { withFileTypes: true })
103
- .filter((entry) => entry.isDirectory() && isPackDir(join(packsDir, entry.name)))
98
+ .filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && manifest(join(packsDir, entry.name))?.name === twinPackageName(entry.name) && isPackDir(join(packsDir, entry.name)))
104
99
  .map((entry) => entry.name)
105
100
  .sort();
106
101
  return {
107
102
  kind: 'checkout',
108
103
  root: resolve(root),
109
104
  vendors,
110
- packDir: (vendor) => (isPackDir(join(packsDir, vendor)) ? join(packsDir, vendor) : undefined),
105
+ packDir: (vendor) => (/^[a-z][a-z0-9-]*$/.test(vendor) && manifest(join(packsDir, vendor))?.name === twinPackageName(vendor) && isPackDir(join(packsDir, vendor)) ? join(packsDir, vendor) : undefined),
111
106
  version: (vendor) => packageVersion(join(packsDir, vendor)),
107
+ kindOf: () => 'checkout',
112
108
  sha: () => {
113
109
  const proc = spawnSync('git', ['-C', root, 'rev-parse', 'HEAD'], { windowsHide: true, encoding: 'utf8' });
114
110
  const sha = proc.status === 0 ? proc.stdout.trim() : '';
@@ -124,20 +120,67 @@ export function installedCatalog(root) {
124
120
  vendors: () => [...twins.keys()].sort(),
125
121
  packDir: (vendor) => twins.get(vendor),
126
122
  version: (vendor) => { const dir = twins.get(vendor); return dir === undefined ? undefined : packageVersion(dir); },
123
+ kindOf: () => 'installed',
127
124
  sha: () => `installed-${fnv1a([...twins.keys()].sort().map((v) => `${v}@${packageVersion(twins.get(v)) ?? '?'}`).join(','))}`,
128
125
  };
129
126
  }
130
- /** The catalog for a world root: the checkout when `packages/twin` is there, else what is installed. */
127
+ /**
128
+ * One catalog over several sources (docs/contributing/architecture.md, "The catalog: where twins come from"): each
129
+ * vendor is served by the pack of the higher major; on a tie the earlier source keeps it. `kind` is the first source's.
130
+ */
131
+ export function mergedCatalog(sources) {
132
+ const chosen = new Map();
133
+ for (const source of sources) {
134
+ for (const vendor of source.vendors()) {
135
+ const held = chosen.get(vendor);
136
+ if (held === undefined) {
137
+ chosen.set(vendor, source);
138
+ continue;
139
+ }
140
+ if (major(source.version(vendor)) > major(held.version(vendor)))
141
+ chosen.set(vendor, source);
142
+ }
143
+ }
144
+ const of = (vendor) => chosen.get(vendor);
145
+ return {
146
+ kind: sources[0].kind,
147
+ root: sources[0].root,
148
+ vendors: () => [...chosen.keys()].sort(),
149
+ packDir: (vendor) => of(vendor)?.packDir(vendor),
150
+ version: (vendor) => of(vendor)?.version(vendor),
151
+ kindOf: (vendor) => of(vendor)?.kindOf(vendor) ?? sources[0].kind,
152
+ sha: () => sources.map((s) => s.sha()).join('+'),
153
+ };
154
+ }
155
+ /** Overlays one facts file on the kernel's (a file that does not read is skipped: the kernel's facts stand). */
156
+ export function overlayPackFactsFile(file) {
157
+ try {
158
+ overlayPackFacts(JSON.parse(readFileSync(file, 'utf8')).packs ?? {});
159
+ }
160
+ catch { /* the kernel's facts stand */ }
161
+ }
162
+ /** The facts file a released pack carries (`generated/pack-facts.json`, written by pack-release), when it has one. */
163
+ export function packFactsFile(dir) {
164
+ const file = join(dir, 'generated', 'pack-facts.json');
165
+ return existsSync(file) ? file : undefined;
166
+ }
167
+ /** The catalog for a World root: its pack repository (or the one beside its platform checkout), merged with installed
168
+ * packages. Only the selected packs' own facts are overlaid on the kernel's. */
131
169
  export function resolveCatalog(root) {
132
170
  const resolved = resolve(root);
133
- if (existsSync(join(resolved, 'packages', 'twin')))
134
- return checkoutCatalog(resolved);
135
171
  const installed = installedCatalog(resolved);
136
- if (installed.vendors().length === 0) {
137
- throw new Error(`No twins found for ${resolved}: no \`packages/twin\` catalog here and no \`@volter/twin-<vendor>\` package installed above it. `
172
+ const repo = packRepository(resolved);
173
+ const catalog = repo === undefined ? installed : mergedCatalog([checkoutCatalog(repo), installed]);
174
+ for (const vendor of catalog.vendors()) {
175
+ const file = packFactsFile(catalog.packDir(vendor));
176
+ if (file)
177
+ overlayPackFactsFile(file);
178
+ }
179
+ if (catalog.vendors().length === 0) {
180
+ throw new Error(`No twins found for ${resolved}: no pack repository and no \`@volter/twin-<vendor>\` package installed above it. `
138
181
  + `Install the twins your app needs (e.g. \`bun add -d @volter/twin-stripe\`) and run init again.`);
139
182
  }
140
- return installed;
183
+ return catalog;
141
184
  }
142
185
  /** The pack directory a service boots from: its `package` resolved from the world root at boot. */
143
186
  export function resolveServicePackage(root, pkg) {
@@ -1,4 +1,4 @@
1
- import type { ChangesetApplication, RebaseReport, Changeset, ChangesetApproval, ChangesetReadiness, ChangesetVerification, ChangesetVerifier, LedgerDelta, LedgerRef, ReplayReport, WorldMarker } from '@volter/world-core';
1
+ import type { ChangesetApplication, RebaseReport, Changeset, ChangesetApproval, ChangesetReadiness, ChangesetVerification, ChangesetVerifier, LedgerDelta, LedgerRef, ReplayReport, WorldMarker } from '@volter/world-core/runtime';
2
2
  export type WorldRootOptions = {
3
3
  root?: string;
4
4
  };
@@ -1,5 +1,5 @@
1
1
  import { worldChangesetRules } from "./configs.js";
2
- import { blobDigest, captureHistory, captureParentHistory, getActiveBlobStore, getActiveWorldStore, withAncestryLock, worldPaths } from '@volter/world-core';
2
+ import { blobDigest, captureHistory, captureParentHistory, getActiveBlobStore, getActiveWorldStore, withHistoryLocks, worldPaths } from '@volter/world-core/runtime';
3
3
  // `volter-world mark` / `diff` / `changeset` — the WORLD-side verbs over the changeset
4
4
  // primitive (docs/concepts/the-model.md v0). Thin, exactly like `covers` and `tail` are thin: all
5
5
  // the state logic (markers, deltas, content hashing, replay) lives in @volter/world-core's
@@ -24,12 +24,12 @@ import { blobDigest, captureHistory, captureParentHistory, getActiveBlobStore, g
24
24
  // here that WRITES, and it writes only through the control plane's kernel write path.
25
25
  import { tmpdir } from 'node:os';
26
26
  import { join, relative, resolve } from 'node:path';
27
- import { approveChangeset, rebaseChangeset, buildChangeset, captureMarker, changesetHashMatches, changesetReadiness, diffLedgers, normalizeChangeset, replayChangeset, runChangesetVerifiers, stateDirName, withApplication, withVerification, worldBootMarker, assertSafeChangesetName, assertMarkerBelongsTo, CHANGESET_KIND, MARKER_KIND, WORLD_BOOT_MARKER_ID, } from '@volter/world-core';
27
+ import { approveChangeset, rebaseChangeset, buildChangeset, captureMarker, changesetHashMatches, changesetReadiness, diffLedgers, normalizeChangeset, replayChangeset, runChangesetVerifiers, stateDirName, withApplication, withVerification, worldBootMarker, assertSafeChangesetName, assertMarkerBelongsTo, CHANGESET_KIND, MARKER_KIND, WORLD_BOOT_MARKER_ID, } from '@volter/world-core/runtime';
28
28
  import { instanceDir, listWorlds, statusWorld } from "./runtime.js";
29
29
  import { fetchFromOrigin } from "./origin.js";
30
30
  import { fetchReady, isPathRemote, landChangeset, landReceipts, localWorld, TOKEN_HEADER } from "./served-world.js";
31
31
  import { loadWorldChecks } from "./root.js";
32
- import { readBranchMeta, readTree, rebaseBranch, runChecks, wholeLog, writeBranchMeta } from '@volter/world-core';
32
+ import { readBranchMeta, readTree, rebaseBranch, runChecks, wholeLog, writeBranchMeta } from '@volter/world-core/runtime';
33
33
  function resolveRoot(root) {
34
34
  return resolve(root ?? process.cwd());
35
35
  }
@@ -44,7 +44,7 @@ function requireWorld(name, root) {
44
44
  if (!/World instance not found/.test(String(error.message)))
45
45
  throw error;
46
46
  const known = listWorlds(root).map((world) => world.name);
47
- throw new Error(`World "${name}" not found in ${root}${known.length ? ` (known worlds: ${known.join(', ')})` : ' (no worlds here)'} — boot it with \`volter-world up <config> --env-out <path> --name ${name}\``);
47
+ throw new Error(`World "${name}" not found in ${root}${known.length ? ` (known worlds: ${known.join(', ')})` : ' (no worlds here)'} — start it with \`volter world up\` in the app's folder (the operator's own: \`volter-world up <config> --env-out <path> --name ${name}\`)`);
48
48
  }
49
49
  }
50
50
  /** Every state service a world service currently records actions under (often exactly one). */
@@ -166,7 +166,7 @@ export function createWorldChangeset(world, name, options = {}) {
166
166
  // re-cutting an existing name (overwrite) starts from that changeset's own base, not from its cut
167
167
  const base = options.base ?? (getActiveWorldStore().exists(path) ? readChangesetFile(path).base : undefined);
168
168
  const delta = diffWorld(world, { root, ...(base === undefined ? {} : { base }) });
169
- // where the parent log stands for each twin the changeset touches (PROTOCOL 2: the cut position a rebase reads from)
169
+ // where the parent log stands for each twin the changeset touches (the cut position a rebase reads from)
170
170
  const cutInstance = requireWorld(world, root);
171
171
  const cut = {};
172
172
  for (const a of delta.actions)
@@ -555,13 +555,11 @@ export function rebaseWorldChangeset(name, options = {}) {
555
555
  const root = resolveRoot(options.root);
556
556
  const located = findWorldChangeset(name, { root, ...(options.world ? { world: options.world } : {}) });
557
557
  // the branch first: its pointer moves to the parent's current position (what a fetch brought in)
558
- withAncestryLock(() => {
559
- const inst = requireWorld(located.world, root);
560
- for (const service of new Set(located.changeset.actions.map((e) => e.service))) {
561
- const st = located.changeset.actions.find((e) => e.service === service)?.stateService ?? service;
562
- const controlRoot = join(inst.dirs.data, service);
558
+ const inst = requireWorld(located.world, root);
559
+ const moved = [...new Set(located.changeset.actions.map((e) => e.service))].map((service) => ({ st: located.changeset.actions.find((e) => e.service === service)?.stateService ?? service, controlRoot: join(inst.dirs.data, service) }));
560
+ withHistoryLocks(moved.map(({ st, controlRoot }) => worldPaths(st, controlRoot).dir), () => {
561
+ for (const { st, controlRoot } of moved)
563
562
  rebaseBranch(st, controlRoot, { origin: Boolean(readBranchMeta(st, controlRoot)?.origin) });
564
- }
565
563
  });
566
564
  if (!changesetHashMatches(located.changeset))
567
565
  throw new Error(`Refusing to rebase changeset "${name}": its stored contentHash does not match its body — the object drifted after authoring`);
@@ -0,0 +1,4 @@
1
+ /** The command line's option readers, shared by `up` (./up-cli.ts) and every other command (./cli-commands.ts). */
2
+ /** An option's value, written `--name value` or `--name=value`. */
3
+ export declare function optionValue(args: string[], name: string, fallback?: string): string;
4
+ export declare function ownerOption(args: string[]): string | undefined;
@@ -0,0 +1,18 @@
1
+ /** The command line's option readers, shared by `up` (./up-cli.ts) and every other command (./cli-commands.ts). */
2
+ /** An option's value, written `--name value` or `--name=value`. */
3
+ export function optionValue(args, name, fallback = '') {
4
+ const joined = args.find((arg) => arg.startsWith(`${name}=`));
5
+ if (joined !== undefined)
6
+ return joined.slice(name.length + 1);
7
+ const index = args.indexOf(name);
8
+ return index >= 0 && index + 1 < args.length ? args[index + 1] : fallback;
9
+ }
10
+ export function ownerOption(args) {
11
+ const index = args.indexOf('--owner');
12
+ if (index < 0)
13
+ return undefined;
14
+ const label = args[index + 1];
15
+ if (!label || label.startsWith('--'))
16
+ throw new Error('--owner requires a task label');
17
+ return label;
18
+ }
@@ -0,0 +1 @@
1
+ export declare function main(): Promise<void>;