feat(chord): replace the facet and service runtime with the chord2 host runtime

Moves chord2 (contracts, objects, cells, roots, plugins, hot reload, memory/IPC/WebSocket links)
from p2 into packages/chord, with its tests (now on vitest) and docs. The root export is the new
runtime plus the shared base (Context, JsonValue, JsonRepresentation, Draft, copyJson, isJsonValue,
replicatedState and its types). ./node now holds the host manager, child control, process locks,
and nodeLoader. ./context and ./delta are unchanged; ./bundler is removed.

replicatedState stays a standalone module with the same exports. It no longer exposes the exact-op
subscription the removed service provider used, so listener failures of mutable states are always
reported asynchronously. The package depends on typebox instead of esbuild. The wire format stays JSON.
This commit is contained in:
Mario Zechner 2026-10-12 10:29:06 +00:00
parent 7472cda0d6
commit 0104fef1e9
92 changed files with 15191 additions and 8974 deletions

View file

@ -323,7 +323,7 @@ implementation or reproduces the historical graph/mutation comparison.
Targeted tests from `packages/chord`:
```sh
node ../../node_modules/vitest/dist/cli.js --run test/delta.test.ts test/delta-clone.test.ts test/delta-tracker/retention.test.ts test/services.test.ts test/service-wire.test.ts test/facets.test.ts
node ../../node_modules/vitest/dist/cli.js --run test/delta.test.ts test/delta-clone.test.ts test/delta-tracker/retention.test.ts test/replicated-state.test.ts
```
Historical source identifiers:

View file

@ -1,6 +1,5 @@
import { defineDoc, defineDocFamily } from "@earendil-works/pi-durable";
import { describe, expect, it } from "vitest";
import { getReplicatedStateInternals } from "../../chord/src/services/state-internals.ts";
import { context, createConversation, documentChanges, flush, openTestSession } from "./session-support.ts";
type State = { value: number; retained: { label: string } };
@ -81,20 +80,15 @@ describe("Session document states", () => {
second.dispose();
});
it("shares exact committed value and operation references with Chord", async () => {
it("shares exact committed value references with Chord", async () => {
const { session, publications } = await createState();
const state = (await session.documentState(StateDoc, context))!;
let receivedOps: unknown;
getReplicatedStateInternals(state)!.subscribe((ops) => {
receivedOps = ops;
});
await session.commit(async (tx) => {
(await tx.doc(StateDoc)).value = 4;
}, context);
await flush();
const published = documentChanges(publications.at(-1)!)[0]!;
expect(state.value).toBe(published.value);
expect(receivedOps).toBe(published.ops);
expect(state.value?.retained).toBe((await session.snapshot(StateDoc, context))!.retained);
state.dispose();
});
@ -116,17 +110,12 @@ describe("Session document states", () => {
it("publishes null retirement and never follows a replacement incarnation", async () => {
const { session } = await createState();
const oldState = (await session.documentState(StateDoc, context))!;
let retirementOps: unknown;
getReplicatedStateInternals(oldState)!.subscribe((ops) => {
retirementOps = ops;
});
await session.commit(async (tx) => {
await tx.retireDoc(StateDoc);
(await tx.doc(StateDoc)).value = 10;
}, context);
await flush();
expect(oldState.value).toBeNull();
expect(retirementOps).toEqual([["r", null]]);
const replacement = (await session.documentState(StateDoc, context))!;
expect(replacement.value?.value).toBe(10);