The TypeScript SDK's state reaches the runtime, a narrowed watch only its keys (novox/hq ADR 0201)
This commit is contained in:
@@ -135,3 +135,44 @@ func waitFor(t *testing.T, path, line string) {
|
|||||||
return errorf("%s has no %q yet: %s", filepath.Base(path), line, strings.Join(read(t, path), " | "))
|
return errorf("%s has no %q yet: %s", filepath.Base(path), line, strings.Join(read(t, path), " | "))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The TypeScript SDK against the runtime (ADR 0201): two watches of one state, each handed only its
|
||||||
|
// own changes, the current values handled before the module's import goes on.
|
||||||
|
func TestTheSDKsStateReachesTheRuntime(t *testing.T) {
|
||||||
|
mesh := mt.New(t)
|
||||||
|
kv := mesh.Bucket(t, "sdkkeeper_servers")
|
||||||
|
if _, err := kv.Put("all.one", []byte(`{"url":"http://one"}`)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m := mt.MembershipOf("sdkkeeper", "anchor", false, nil)
|
||||||
|
m.State = []bus.StateIssued{{Name: "servers", Bucket: "sdkkeeper_servers", Writes: true}}
|
||||||
|
mesh.Issue(t, m)
|
||||||
|
nodeTools := connect(t, "node-tools", "anchor")
|
||||||
|
asker := connect(t, "console", "workstation")
|
||||||
|
log := filepath.Join(t.TempDir(), "sdk.log")
|
||||||
|
said := &lines{}
|
||||||
|
stop, err := Run(nodeTools, []Served{{Module: "sdkkeeper", Entrypoints: []string{mt.Fixture("state-sdk.serve.mjs")}}},
|
||||||
|
map[string]map[string]string{"sdkkeeper": {"STATE_LOG": log}}, said.logf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v\n%s", err, said.all())
|
||||||
|
}
|
||||||
|
t.Cleanup(stop)
|
||||||
|
waitFor(t, log, "current")
|
||||||
|
if got := strings.Join(read(t, log), " | "); got != "was put all.one | current" {
|
||||||
|
t.Fatalf("the import went on with %q", got)
|
||||||
|
}
|
||||||
|
if _, err := call(t, asker, "sdkkeeper.register@anchor", map[string]any{"key": "anchor.two", "url": "http://two"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
waitFor(t, log, "now put anchor.two")
|
||||||
|
waitFor(t, log, "narrow put anchor.two")
|
||||||
|
if _, err := call(t, asker, "sdkkeeper.register@anchor", map[string]any{"key": "all.three", "url": "http://three"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
waitFor(t, log, "now put all.three")
|
||||||
|
for _, l := range read(t, log) {
|
||||||
|
if l == "narrow put all.three" {
|
||||||
|
t.Fatalf("a narrowed watch was handed a key outside it: %s", strings.Join(read(t, log), " | "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// A module that keeps state through the SDK (novox/hq ADR 0201): it watches its servers as it is
|
||||||
|
// imported, and writes each change it is handed to STATE_LOG; one tool registers a server.
|
||||||
|
import { appendFileSync } from "node:fs";
|
||||||
|
import { state } from "@novox/mesh-sdk/state";
|
||||||
|
import { registerModuleTools } from "@novox/mesh-sdk/tools";
|
||||||
|
|
||||||
|
const servers = state("servers");
|
||||||
|
await servers.watch((c) => appendFileSync(process.env.STATE_LOG, `${c.current ? "was" : "now"} ${c.op} ${c.key}\n`));
|
||||||
|
await servers.watch((c) => appendFileSync(process.env.STATE_LOG, `narrow ${c.op} ${c.key}\n`), { key: "anchor.*" });
|
||||||
|
appendFileSync(process.env.STATE_LOG, "current\n");
|
||||||
|
registerModuleTools("sdkkeeper", () => [
|
||||||
|
{ name: "register", description: "put a server", input: {}, run: async (a) => servers.put(a.key, { url: a.url }) },
|
||||||
|
]);
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// The launcher the builder writes beside an entrypoint (novox/hq ADR 0193), for state-sdk.mjs.
|
||||||
|
import { serveRegisteredOverStdio } from "@novox/mesh-sdk/stdio";
|
||||||
|
await import("./state-sdk.mjs");
|
||||||
|
await serveRegisteredOverStdio();
|
||||||
Reference in New Issue
Block a user