@moltzap/client 2026.414.0 → 2026.425.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.
- package/dist/channel-core.d.ts +28 -15
- package/dist/channel-core.d.ts.map +1 -1
- package/dist/channel-core.js +113 -74
- package/dist/channel-core.js.map +1 -1
- package/dist/channel-core.test.js +126 -34
- package/dist/channel-core.test.js.map +1 -1
- package/dist/cli/__tests__/cli-multi-agent.int.test.d.ts +2 -0
- package/dist/cli/__tests__/cli-multi-agent.int.test.d.ts.map +1 -0
- package/dist/cli/__tests__/cli-multi-agent.int.test.js +26 -0
- package/dist/cli/__tests__/cli-multi-agent.int.test.js.map +1 -0
- package/dist/cli/commands/agents.d.ts +13 -2
- package/dist/cli/commands/agents.d.ts.map +1 -1
- package/dist/cli/commands/agents.js +30 -18
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/apps.d.ts +96 -0
- package/dist/cli/commands/apps.d.ts.map +1 -0
- package/dist/cli/commands/apps.js +193 -0
- package/dist/cli/commands/apps.js.map +1 -0
- package/dist/cli/commands/apps.test.d.ts +2 -0
- package/dist/cli/commands/apps.test.d.ts.map +1 -0
- package/dist/cli/commands/apps.test.js +220 -0
- package/dist/cli/commands/apps.test.js.map +1 -0
- package/dist/cli/commands/contacts.d.ts +18 -2
- package/dist/cli/commands/contacts.d.ts.map +1 -1
- package/dist/cli/commands/contacts.js +44 -52
- package/dist/cli/commands/contacts.js.map +1 -1
- package/dist/cli/commands/conversations-v2.test.d.ts +2 -0
- package/dist/cli/commands/conversations-v2.test.d.ts.map +1 -0
- package/dist/cli/commands/conversations-v2.test.js +92 -0
- package/dist/cli/commands/conversations-v2.test.js.map +1 -0
- package/dist/cli/commands/conversations.d.ts +73 -3
- package/dist/cli/commands/conversations.d.ts.map +1 -1
- package/dist/cli/commands/conversations.js +157 -108
- package/dist/cli/commands/conversations.js.map +1 -1
- package/dist/cli/commands/invite.d.ts +6 -2
- package/dist/cli/commands/invite.d.ts.map +1 -1
- package/dist/cli/commands/invite.js +8 -6
- package/dist/cli/commands/invite.js.map +1 -1
- package/dist/cli/commands/messages.d.ts +48 -0
- package/dist/cli/commands/messages.d.ts.map +1 -0
- package/dist/cli/commands/messages.js +68 -0
- package/dist/cli/commands/messages.js.map +1 -0
- package/dist/cli/commands/messages.test.d.ts +2 -0
- package/dist/cli/commands/messages.test.d.ts.map +1 -0
- package/dist/cli/commands/messages.test.js +73 -0
- package/dist/cli/commands/messages.test.js.map +1 -0
- package/dist/cli/commands/permissions.d.ts +66 -0
- package/dist/cli/commands/permissions.d.ts.map +1 -0
- package/dist/cli/commands/permissions.js +101 -0
- package/dist/cli/commands/permissions.js.map +1 -0
- package/dist/cli/commands/permissions.test.d.ts +2 -0
- package/dist/cli/commands/permissions.test.d.ts.map +1 -0
- package/dist/cli/commands/permissions.test.js +126 -0
- package/dist/cli/commands/permissions.test.js.map +1 -0
- package/dist/cli/commands/ping.d.ts +6 -2
- package/dist/cli/commands/ping.d.ts.map +1 -1
- package/dist/cli/commands/ping.js +21 -20
- package/dist/cli/commands/ping.js.map +1 -1
- package/dist/cli/commands/presence.d.ts +10 -2
- package/dist/cli/commands/presence.d.ts.map +1 -1
- package/dist/cli/commands/presence.js +27 -15
- package/dist/cli/commands/presence.js.map +1 -1
- package/dist/cli/commands/register.d.ts +24 -2
- package/dist/cli/commands/register.d.ts.map +1 -1
- package/dist/cli/commands/register.js +123 -47
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/register.test.js +45 -27
- package/dist/cli/commands/register.test.js.map +1 -1
- package/dist/cli/commands/send.d.ts +43 -2
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +55 -13
- package/dist/cli/commands/send.js.map +1 -1
- package/dist/cli/commands/send.test.js +20 -19
- package/dist/cli/commands/send.test.js.map +1 -1
- package/dist/cli/commands/status.d.ts +6 -2
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +16 -10
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/whoami.d.ts +7 -2
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +13 -7
- package/dist/cli/commands/whoami.js.map +1 -1
- package/dist/cli/config.d.ts +24 -8
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +65 -39
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/config.test.js +12 -10
- package/dist/cli/config.test.js.map +1 -1
- package/dist/cli/http-client.d.ts +6 -2
- package/dist/cli/http-client.d.ts.map +1 -1
- package/dist/cli/http-client.js +25 -24
- package/dist/cli/http-client.js.map +1 -1
- package/dist/cli/index.d.ts +16 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +160 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/profile.d.ts +134 -0
- package/dist/cli/profile.d.ts.map +1 -0
- package/dist/cli/profile.js +217 -0
- package/dist/cli/profile.js.map +1 -0
- package/dist/cli/profile.test.d.ts +2 -0
- package/dist/cli/profile.test.d.ts.map +1 -0
- package/dist/cli/profile.test.js +263 -0
- package/dist/cli/profile.test.js.map +1 -0
- package/dist/cli/runtime.d.ts +34 -0
- package/dist/cli/runtime.d.ts.map +1 -0
- package/dist/cli/runtime.js +94 -0
- package/dist/cli/runtime.js.map +1 -0
- package/dist/cli/socket-client.d.ts +15 -12
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +72 -75
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/cli/transport.d.ts +181 -0
- package/dist/cli/transport.d.ts.map +1 -0
- package/dist/cli/transport.js +324 -0
- package/dist/cli/transport.js.map +1 -0
- package/dist/cli/transport.test.d.ts +2 -0
- package/dist/cli/transport.test.d.ts.map +1 -0
- package/dist/cli/transport.test.js +197 -0
- package/dist/cli/transport.test.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/close-info.d.ts +94 -0
- package/dist/runtime/close-info.d.ts.map +1 -0
- package/dist/runtime/close-info.js +109 -0
- package/dist/runtime/close-info.js.map +1 -0
- package/dist/runtime/close-info.test.d.ts +2 -0
- package/dist/runtime/close-info.test.d.ts.map +1 -0
- package/dist/runtime/close-info.test.js +102 -0
- package/dist/runtime/close-info.test.js.map +1 -0
- package/dist/runtime/errors.d.ts +47 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +20 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/errors.test.d.ts +2 -0
- package/dist/runtime/errors.test.d.ts.map +1 -0
- package/dist/runtime/errors.test.js +66 -0
- package/dist/runtime/errors.test.js.map +1 -0
- package/dist/runtime/frame.d.ts +25 -0
- package/dist/runtime/frame.d.ts.map +1 -0
- package/dist/runtime/frame.js +106 -0
- package/dist/runtime/frame.js.map +1 -0
- package/dist/runtime/frame.test.d.ts +2 -0
- package/dist/runtime/frame.test.d.ts.map +1 -0
- package/dist/runtime/frame.test.js +32 -0
- package/dist/runtime/frame.test.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/refs.d.ts +10 -0
- package/dist/runtime/refs.d.ts.map +1 -0
- package/dist/runtime/refs.js +10 -0
- package/dist/runtime/refs.js.map +1 -0
- package/dist/runtime/runtime.test.d.ts +2 -0
- package/dist/runtime/runtime.test.d.ts.map +1 -0
- package/dist/runtime/runtime.test.js +29 -0
- package/dist/runtime/runtime.test.js.map +1 -0
- package/dist/runtime/subscribers.d.ts +157 -0
- package/dist/runtime/subscribers.d.ts.map +1 -0
- package/dist/runtime/subscribers.js +131 -0
- package/dist/runtime/subscribers.js.map +1 -0
- package/dist/runtime/subscribers.test.d.ts +2 -0
- package/dist/runtime/subscribers.test.d.ts.map +1 -0
- package/dist/runtime/subscribers.test.js +155 -0
- package/dist/runtime/subscribers.test.js.map +1 -0
- package/dist/service.d.ts +68 -17
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +461 -307
- package/dist/service.js.map +1 -1
- package/dist/service.test.js +266 -77
- package/dist/service.test.js.map +1 -1
- package/dist/test/index.d.ts +33 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +44 -0
- package/dist/test/index.js.map +1 -0
- package/dist/test-utils/channel-service-fixture.d.ts +2 -1
- package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
- package/dist/test-utils/channel-service-fixture.js +31 -11
- package/dist/test-utils/channel-service-fixture.js.map +1 -1
- package/dist/test-utils/conformance-adapter.d.ts +50 -0
- package/dist/test-utils/conformance-adapter.d.ts.map +1 -0
- package/dist/test-utils/conformance-adapter.js +206 -0
- package/dist/test-utils/conformance-adapter.js.map +1 -0
- package/dist/test-utils/fake-service.d.ts +85 -0
- package/dist/test-utils/fake-service.d.ts.map +1 -0
- package/dist/test-utils/fake-service.js +103 -0
- package/dist/test-utils/fake-service.js.map +1 -0
- package/dist/test-utils/index.d.ts +2 -0
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +3 -2
- package/dist/test-utils/index.js.map +1 -1
- package/dist/ws-client.d.ts +157 -18
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +432 -94
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-client.test.d.ts +2 -0
- package/dist/ws-client.test.d.ts.map +1 -0
- package/dist/ws-client.test.js +672 -0
- package/dist/ws-client.test.js.map +1 -0
- package/package.json +23 -14
- package/LICENSE +0 -21
- package/dist/__tests__/service.integration.test.d.ts +0 -2
- package/dist/__tests__/service.integration.test.d.ts.map +0 -1
- package/dist/__tests__/service.integration.test.js +0 -1087
- package/dist/__tests__/service.integration.test.js.map +0 -1
- package/dist/cli/commands/delete.d.ts +0 -3
- package/dist/cli/commands/delete.d.ts.map +0 -1
- package/dist/cli/commands/delete.js +0 -10
- package/dist/cli/commands/delete.js.map +0 -1
- package/dist/cli/commands/react.d.ts +0 -3
- package/dist/cli/commands/react.d.ts.map +0 -1
- package/dist/cli/commands/react.js +0 -18
- package/dist/cli/commands/react.js.map +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { decodeFrames } from "./frame.js";
|
|
4
|
+
describe("decodeFrames", () => {
|
|
5
|
+
it("decodes padded chunks that contain both an event and a response", async () => {
|
|
6
|
+
const raw = JSON.stringify({
|
|
7
|
+
jsonrpc: "2.0",
|
|
8
|
+
type: "event",
|
|
9
|
+
event: "messages/received",
|
|
10
|
+
data: { message: { id: "m-1", conversationId: "c-1" } },
|
|
11
|
+
}) +
|
|
12
|
+
"\u0000\n" +
|
|
13
|
+
JSON.stringify({
|
|
14
|
+
jsonrpc: "2.0",
|
|
15
|
+
type: "response",
|
|
16
|
+
id: "rpc-7",
|
|
17
|
+
result: { ok: true },
|
|
18
|
+
});
|
|
19
|
+
const decoded = await Effect.runPromise(decodeFrames(raw));
|
|
20
|
+
expect(decoded).toHaveLength(2);
|
|
21
|
+
expect(decoded[0]).toMatchObject({
|
|
22
|
+
_tag: "Event",
|
|
23
|
+
frame: { event: "messages/received" },
|
|
24
|
+
});
|
|
25
|
+
expect(decoded[1]).toMatchObject({
|
|
26
|
+
_tag: "Response",
|
|
27
|
+
id: "rpc-7",
|
|
28
|
+
result: { ok: true },
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=frame.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.test.js","sourceRoot":"","sources":["../../src/runtime/frame.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,GAAG,GACP,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE;SACxD,CAAC;YACF,UAAU;YACV,IAAI,CAAC,SAAS,CAAC;gBACb,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,UAAU;gBAChB,EAAE,EAAE,OAAO;gBACX,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;aACrB,CAAC,CAAC;QAEL,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAE3D,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YAC/B,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;SACtC,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YAC/B,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,OAAO;YACX,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HashMap, Ref } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Read a `Ref` synchronously outside an Effect scope. Only safe for `Ref`s
|
|
4
|
+
* that never fiber-park (the stock `Ref.Ref<A>` never does). Use from
|
|
5
|
+
* object methods or sync code paths that hold a `Ref` set up at construction.
|
|
6
|
+
*/
|
|
7
|
+
export declare const snapshot: <A>(ref: Ref.Ref<A>) => A;
|
|
8
|
+
/** Lookup `key` in `m`, falling back to `dflt()` if absent. Lazy default. */
|
|
9
|
+
export declare const getOr: <K, V>(m: HashMap.HashMap<K, V>, key: K, dflt: () => V) => V;
|
|
10
|
+
//# sourceMappingURL=refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../../src/runtime/refs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,OAAO,EAAU,GAAG,EAAE,MAAM,QAAQ,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAG,CAAiC,CAAC;AAEhF,6EAA6E;AAC7E,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EACxB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EACxB,KAAK,CAAC,EACN,MAAM,MAAM,CAAC,KACZ,CAAgD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Effect, HashMap, Option, Ref } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Read a `Ref` synchronously outside an Effect scope. Only safe for `Ref`s
|
|
4
|
+
* that never fiber-park (the stock `Ref.Ref<A>` never does). Use from
|
|
5
|
+
* object methods or sync code paths that hold a `Ref` set up at construction.
|
|
6
|
+
*/
|
|
7
|
+
export const snapshot = (ref) => Effect.runSync(Ref.get(ref));
|
|
8
|
+
/** Lookup `key` in `m`, falling back to `dflt()` if absent. Lazy default. */
|
|
9
|
+
export const getOr = (m, key, dflt) => Option.getOrElse(HashMap.get(m, key), dflt);
|
|
10
|
+
//# sourceMappingURL=refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.js","sourceRoot":"","sources":["../../src/runtime/refs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAEtD;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAI,GAAe,EAAK,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAEhF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,CAAwB,EACxB,GAAM,EACN,IAAa,EACV,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.test.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { it } from "@effect/vitest";
|
|
2
|
+
import { Effect, Exit } from "effect";
|
|
3
|
+
import { expect } from "vitest";
|
|
4
|
+
import { NotConnectedError, RpcServerError, RpcTimeoutError, } from "./errors.js";
|
|
5
|
+
it.effect("tagged errors discriminate by _tag", () => Effect.gen(function* () {
|
|
6
|
+
const exit = yield* Effect.exit(Effect.fail(new RpcTimeoutError({ method: "messages/send", timeoutMs: 30_000 })));
|
|
7
|
+
if (Exit.isFailure(exit) && exit.cause._tag === "Fail") {
|
|
8
|
+
const err = exit.cause.error;
|
|
9
|
+
expect(err._tag).toBe("RpcTimeoutError");
|
|
10
|
+
expect(err.method).toBe("messages/send");
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
throw new Error("expected failure");
|
|
14
|
+
}
|
|
15
|
+
}));
|
|
16
|
+
it.effect("RpcServerError preserves wire fields", () => Effect.gen(function* () {
|
|
17
|
+
const err = new RpcServerError({
|
|
18
|
+
code: -32002,
|
|
19
|
+
message: "Not found",
|
|
20
|
+
});
|
|
21
|
+
expect(err.code).toBe(-32002);
|
|
22
|
+
expect(err.message).toBe("Not found");
|
|
23
|
+
expect(err.data).toBeUndefined();
|
|
24
|
+
}));
|
|
25
|
+
it.effect("NotConnectedError compiles and carries message", () => Effect.gen(function* () {
|
|
26
|
+
const err = new NotConnectedError({ message: "socket closed" });
|
|
27
|
+
expect(err.message).toBe("socket closed");
|
|
28
|
+
}));
|
|
29
|
+
//# sourceMappingURL=runtime.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.test.js","sourceRoot":"","sources":["../../src/runtime/runtime.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,EAAE,CAAC,MAAM,CAAC,oCAAoC,EAAE,GAAG,EAAE,CACnD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAC7B,MAAM,CAAC,IAAI,CACT,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CACpE,CACF,CAAC;IACF,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;AACH,CAAC,CAAC,CACH,CAAC;AAEF,EAAE,CAAC,MAAM,CAAC,sCAAsC,EAAE,GAAG,EAAE,CACrD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,IAAI,cAAc,CAAC;QAC7B,IAAI,EAAE,CAAC,KAAK;QACZ,OAAO,EAAE,WAAW;KACrB,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;AACnC,CAAC,CAAC,CACH,CAAC;AAEF,EAAE,CAAC,MAAM,CAAC,gDAAgD,EAAE,GAAG,EAAE,CAC/D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,IAAI,iBAAiB,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;IAChE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAC5C,CAAC,CAAC,CACH,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-subscription event registry for `MoltZapWsClient`.
|
|
3
|
+
*
|
|
4
|
+
* Responsibility: own the list of live `subscribe()` handles and fan each
|
|
5
|
+
* inbound `EventFrame` out to every subscription whose filter matches.
|
|
6
|
+
* Implements spec #222 §5.3 (C4 + the `RealClientEventSubscriber.subscribe`
|
|
7
|
+
* filter stub). Lives as an internal collaborator of `MoltZapWsClient`;
|
|
8
|
+
* the public types (`SubscriptionFilter`, `EventSubscription`,
|
|
9
|
+
* `SubscriptionId`) re-export from the package barrel.
|
|
10
|
+
*
|
|
11
|
+
* Dispatch ordering (Invariant 6 — events delivered in arrival order):
|
|
12
|
+
* 1. Inbound frames are handed to the registry in arrival order.
|
|
13
|
+
* 2. Within a single frame, subscriptions are notified in registration
|
|
14
|
+
* order.
|
|
15
|
+
* 3. There is no separate legacy `onEvent` fanout — spec #222 OQ-4 is
|
|
16
|
+
* resolved by DELETING `MoltZapWsClientOptions.onEvent`. Callers
|
|
17
|
+
* that want "every event" register `subscribe({}, handler)` after
|
|
18
|
+
* construction and before `connect()`.
|
|
19
|
+
*
|
|
20
|
+
* Unsubscribe semantics (OQ-3 A): `unsubscribe` takes effect on the next
|
|
21
|
+
* frame. The registry snapshots its live-subscription list at the start
|
|
22
|
+
* of each `dispatch` call; in-flight dispatch of frame N is not
|
|
23
|
+
* interrupted by an unsubscribe during frame N. Frame N+1 observes the
|
|
24
|
+
* unsubscribed state.
|
|
25
|
+
*
|
|
26
|
+
* Error channel: handlers are invoked inside a defect-catcher; a throw is
|
|
27
|
+
* logged via the client's injected `WsClientLogger` and swallowed
|
|
28
|
+
* (matching the prior `onEvent` contract at `ws-client.ts:650-655`
|
|
29
|
+
* pre-deletion). The registry itself has no typed error surface —
|
|
30
|
+
* `register`, `dispatch`, and `closeAll` are `Effect<T, never>`.
|
|
31
|
+
*/
|
|
32
|
+
import { Effect } from "effect";
|
|
33
|
+
import type { EventFrame } from "@moltzap/protocol";
|
|
34
|
+
/** Branded identifier for a subscription handle. Minted by `register`. */
|
|
35
|
+
export type SubscriptionId = string & {
|
|
36
|
+
readonly __brand: "SubscriptionId";
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Filter grammar for `subscribe`. An event is delivered to a subscription
|
|
40
|
+
* iff it matches **every** field that is set on the filter. Unset fields
|
|
41
|
+
* are wildcards; the empty filter `{}` matches every event.
|
|
42
|
+
*
|
|
43
|
+
* OQ-2 resolution (A): exactly these three fields, no free-form
|
|
44
|
+
* predicate, no schema-derived matcher. Matches the existing
|
|
45
|
+
* `RealClientEventFilter` contract at
|
|
46
|
+
* `packages/protocol/src/testing/conformance/client/runner.ts:104-116`
|
|
47
|
+
* one-for-one.
|
|
48
|
+
*
|
|
49
|
+
* - `emissionTag` — exact match against the canonical payload key
|
|
50
|
+
* `frame.data.__emissionTag`. (The adapter reads the same key at
|
|
51
|
+
* `packages/client/src/test-utils/conformance-adapter.ts:77-79`;
|
|
52
|
+
* `emitTaggedEventDefault` writes it at `runner.ts:343-357`. The
|
|
53
|
+
* `__emissionId` string in the `runner.ts:108` doc comment is a
|
|
54
|
+
* known doc-bug — architect files a follow-up issue against
|
|
55
|
+
* protocol to correct the comment; it is not the canonical name.)
|
|
56
|
+
* - `conversationId` — exact match against `frame.data.conversationId`
|
|
57
|
+
* when set on the event payload.
|
|
58
|
+
* - `eventNamePrefix` — `frame.event.startsWith(prefix)`.
|
|
59
|
+
*/
|
|
60
|
+
export interface SubscriptionFilter {
|
|
61
|
+
readonly emissionTag?: string;
|
|
62
|
+
readonly conversationId?: string;
|
|
63
|
+
readonly eventNamePrefix?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Handle returned by `register` / `MoltZapWsClient.subscribe`. Caller
|
|
67
|
+
* holds the handle for its subscription's lifetime and runs
|
|
68
|
+
* `unsubscribe` to stop delivery.
|
|
69
|
+
*
|
|
70
|
+
* `unsubscribe` is `Effect<void, never>`: it is idempotent and total.
|
|
71
|
+
* Calling `unsubscribe` a second time, or after `closeAll`, is a no-op.
|
|
72
|
+
*/
|
|
73
|
+
export interface EventSubscription {
|
|
74
|
+
readonly id: SubscriptionId;
|
|
75
|
+
readonly unsubscribe: Effect.Effect<void, never>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Per-subscription handler signature. Runs inside the registry's
|
|
79
|
+
* dispatch fiber. Must not throw — throws are caught by the registry,
|
|
80
|
+
* logged via the injected logger, and swallowed.
|
|
81
|
+
*
|
|
82
|
+
* Returning an `Effect` (not a plain `void`) lets handlers compose with
|
|
83
|
+
* Effect-native downstream code without an extra runSync shim. The
|
|
84
|
+
* registry awaits each handler's effect before moving to the next
|
|
85
|
+
* subscription for this frame — fairness over throughput, so a slow
|
|
86
|
+
* handler on subscription A does not reorder frames seen by
|
|
87
|
+
* subscription B across frames.
|
|
88
|
+
*/
|
|
89
|
+
export type SubscriberHandler = (frame: EventFrame) => Effect.Effect<void, never>;
|
|
90
|
+
/**
|
|
91
|
+
* Subscriber registry. One instance per `MoltZapWsClient`, created at
|
|
92
|
+
* construction time and owned by the client. Not exported from the
|
|
93
|
+
* package barrel — consumers reach the registry only through
|
|
94
|
+
* `MoltZapWsClient.subscribe`.
|
|
95
|
+
*/
|
|
96
|
+
export interface SubscriberRegistry {
|
|
97
|
+
/**
|
|
98
|
+
* Add a subscription. Returns the handle immediately; delivery starts
|
|
99
|
+
* with the next frame passed to `dispatch`. Does not await any
|
|
100
|
+
* connection state — subscribe is legal pre-connect (spec §5.3 +
|
|
101
|
+
* Assumption 1 deletion: post-delete of `onEvent`, subscribe is the
|
|
102
|
+
* only pre-connect event hook).
|
|
103
|
+
*/
|
|
104
|
+
readonly register: (filter: SubscriptionFilter, handler: SubscriberHandler) => Effect.Effect<EventSubscription, never>;
|
|
105
|
+
/**
|
|
106
|
+
* Fan an inbound event out to every matching subscription. Called by
|
|
107
|
+
* `MoltZapWsClient.handleIncoming` at the existing event-dispatch
|
|
108
|
+
* point (`ws-client.ts:649-685`). Implementation snapshots the
|
|
109
|
+
* live-subscription list at the start of dispatch so
|
|
110
|
+
* unsubscribe-during-dispatch observes next-frame semantics (OQ-3 A).
|
|
111
|
+
*
|
|
112
|
+
* Dispatch order: registration order, iterated sequentially; slow
|
|
113
|
+
* handlers block later subscriptions for this frame but never
|
|
114
|
+
* reorder frame N relative to frame N+1.
|
|
115
|
+
*/
|
|
116
|
+
readonly dispatch: (frame: EventFrame) => Effect.Effect<void, never>;
|
|
117
|
+
/**
|
|
118
|
+
* Drop every live subscription. Called from `MoltZapWsClient.close`
|
|
119
|
+
* so handlers stop firing once the client is torn down. Idempotent.
|
|
120
|
+
*/
|
|
121
|
+
readonly closeAll: Effect.Effect<void, never>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Construct an empty registry. Called once from the `MoltZapWsClient`
|
|
125
|
+
* constructor. Takes a logger so registry-internal error logs reach the
|
|
126
|
+
* same sink as the rest of the client.
|
|
127
|
+
*
|
|
128
|
+
* Implementation notes:
|
|
129
|
+
* - Live subscriptions are stored in a `Ref<ReadonlyArray<…>>` keyed
|
|
130
|
+
* by registration order. Append-on-register, filter-on-unsubscribe
|
|
131
|
+
* keeps the dispatch path O(N) with N = live subscription count.
|
|
132
|
+
* Bigger structures aren't justified at the expected sub count
|
|
133
|
+
* (≤ ~10 per fixture).
|
|
134
|
+
* - `dispatch` snapshots the array at start (OQ-3 A). An
|
|
135
|
+
* `unsubscribe` mid-dispatch mutates the Ref but the in-flight
|
|
136
|
+
* iteration walks the snapshot.
|
|
137
|
+
* - Handler exceptions are caught with `Effect.catchAllDefect` after
|
|
138
|
+
* the handler Effect. Sync `throw` from a `(frame) => …` body
|
|
139
|
+
* surfaces as a defect; we log + swallow to match the pre-deletion
|
|
140
|
+
* `onEvent` contract.
|
|
141
|
+
*/
|
|
142
|
+
export declare function makeSubscriberRegistry(logger: {
|
|
143
|
+
readonly warn: (...args: ReadonlyArray<unknown>) => void;
|
|
144
|
+
}): Effect.Effect<SubscriberRegistry, never>;
|
|
145
|
+
/**
|
|
146
|
+
* Pure filter-match predicate. Exposed for unit testing so executable
|
|
147
|
+
* client-side divergence proofs can force known-bad subscription
|
|
148
|
+
* behavior without bypassing the production dispatch path.
|
|
149
|
+
*
|
|
150
|
+
* Returns `true` iff `frame` matches every set field on `filter`:
|
|
151
|
+
* - `filter.emissionTag === frame.data.__emissionTag` (strict ===)
|
|
152
|
+
* - `filter.conversationId === frame.data.conversationId` (strict ===)
|
|
153
|
+
* - `frame.event.startsWith(filter.eventNamePrefix)`
|
|
154
|
+
* Unset filter fields are wildcards.
|
|
155
|
+
*/
|
|
156
|
+
export declare function matchesFilter(filter: SubscriptionFilter, frame: EventFrame): boolean;
|
|
157
|
+
//# sourceMappingURL=subscribers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribers.d.ts","sourceRoot":"","sources":["../../src/runtime/subscribers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,MAAM,EAAO,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;CAClD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,UAAU,KACd,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,CACjB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,iBAAiB,KACvB,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAE7C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAErE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;CAC/C;AAQD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CAC1D,GAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CA8C3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,UAAU,GAChB,OAAO,CA8BT"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-subscription event registry for `MoltZapWsClient`.
|
|
3
|
+
*
|
|
4
|
+
* Responsibility: own the list of live `subscribe()` handles and fan each
|
|
5
|
+
* inbound `EventFrame` out to every subscription whose filter matches.
|
|
6
|
+
* Implements spec #222 §5.3 (C4 + the `RealClientEventSubscriber.subscribe`
|
|
7
|
+
* filter stub). Lives as an internal collaborator of `MoltZapWsClient`;
|
|
8
|
+
* the public types (`SubscriptionFilter`, `EventSubscription`,
|
|
9
|
+
* `SubscriptionId`) re-export from the package barrel.
|
|
10
|
+
*
|
|
11
|
+
* Dispatch ordering (Invariant 6 — events delivered in arrival order):
|
|
12
|
+
* 1. Inbound frames are handed to the registry in arrival order.
|
|
13
|
+
* 2. Within a single frame, subscriptions are notified in registration
|
|
14
|
+
* order.
|
|
15
|
+
* 3. There is no separate legacy `onEvent` fanout — spec #222 OQ-4 is
|
|
16
|
+
* resolved by DELETING `MoltZapWsClientOptions.onEvent`. Callers
|
|
17
|
+
* that want "every event" register `subscribe({}, handler)` after
|
|
18
|
+
* construction and before `connect()`.
|
|
19
|
+
*
|
|
20
|
+
* Unsubscribe semantics (OQ-3 A): `unsubscribe` takes effect on the next
|
|
21
|
+
* frame. The registry snapshots its live-subscription list at the start
|
|
22
|
+
* of each `dispatch` call; in-flight dispatch of frame N is not
|
|
23
|
+
* interrupted by an unsubscribe during frame N. Frame N+1 observes the
|
|
24
|
+
* unsubscribed state.
|
|
25
|
+
*
|
|
26
|
+
* Error channel: handlers are invoked inside a defect-catcher; a throw is
|
|
27
|
+
* logged via the client's injected `WsClientLogger` and swallowed
|
|
28
|
+
* (matching the prior `onEvent` contract at `ws-client.ts:650-655`
|
|
29
|
+
* pre-deletion). The registry itself has no typed error surface —
|
|
30
|
+
* `register`, `dispatch`, and `closeAll` are `Effect<T, never>`.
|
|
31
|
+
*/
|
|
32
|
+
import { Effect, Ref } from "effect";
|
|
33
|
+
/**
|
|
34
|
+
* Construct an empty registry. Called once from the `MoltZapWsClient`
|
|
35
|
+
* constructor. Takes a logger so registry-internal error logs reach the
|
|
36
|
+
* same sink as the rest of the client.
|
|
37
|
+
*
|
|
38
|
+
* Implementation notes:
|
|
39
|
+
* - Live subscriptions are stored in a `Ref<ReadonlyArray<…>>` keyed
|
|
40
|
+
* by registration order. Append-on-register, filter-on-unsubscribe
|
|
41
|
+
* keeps the dispatch path O(N) with N = live subscription count.
|
|
42
|
+
* Bigger structures aren't justified at the expected sub count
|
|
43
|
+
* (≤ ~10 per fixture).
|
|
44
|
+
* - `dispatch` snapshots the array at start (OQ-3 A). An
|
|
45
|
+
* `unsubscribe` mid-dispatch mutates the Ref but the in-flight
|
|
46
|
+
* iteration walks the snapshot.
|
|
47
|
+
* - Handler exceptions are caught with `Effect.catchAllDefect` after
|
|
48
|
+
* the handler Effect. Sync `throw` from a `(frame) => …` body
|
|
49
|
+
* surfaces as a defect; we log + swallow to match the pre-deletion
|
|
50
|
+
* `onEvent` contract.
|
|
51
|
+
*/
|
|
52
|
+
export function makeSubscriberRegistry(logger) {
|
|
53
|
+
return Effect.gen(function* () {
|
|
54
|
+
const subsRef = yield* Ref.make([]);
|
|
55
|
+
const counterRef = yield* Ref.make(0);
|
|
56
|
+
const register = (filter, handler) => Effect.gen(function* () {
|
|
57
|
+
const n = yield* Ref.updateAndGet(counterRef, (c) => c + 1);
|
|
58
|
+
const id = `sub-${n}`;
|
|
59
|
+
const live = { id, filter, handler };
|
|
60
|
+
yield* Ref.update(subsRef, (xs) => [...xs, live]);
|
|
61
|
+
const unsubscribe = Ref.update(subsRef, (xs) => xs.filter((s) => s.id !== id));
|
|
62
|
+
return { id, unsubscribe };
|
|
63
|
+
});
|
|
64
|
+
const dispatch = (frame) => Effect.gen(function* () {
|
|
65
|
+
// Snapshot at dispatch-start: OQ-3 A. Unsubscribes during this
|
|
66
|
+
// frame mutate `subsRef` but our iteration walks `snapshot`.
|
|
67
|
+
const snapshot = yield* Ref.get(subsRef);
|
|
68
|
+
// Short-circuit the common pre-subscribe path (every inbound
|
|
69
|
+
// frame before any `subscribe()` call) so hot-path dispatch
|
|
70
|
+
// avoids even the `for…of` allocation.
|
|
71
|
+
if (snapshot.length === 0)
|
|
72
|
+
return;
|
|
73
|
+
for (const sub of snapshot) {
|
|
74
|
+
if (!matchesFilter(sub.filter, frame))
|
|
75
|
+
continue;
|
|
76
|
+
// Handlers must not throw; we catch defects defensively so
|
|
77
|
+
// one buggy subscriber can't kill the dispatch loop or break
|
|
78
|
+
// the reader fiber.
|
|
79
|
+
yield* Effect.suspend(() => sub.handler(frame)).pipe(Effect.catchAllDefect((err) => Effect.sync(() => {
|
|
80
|
+
logger.warn("subscriber handler threw", err);
|
|
81
|
+
})));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const closeAll = Ref.set(subsRef, []);
|
|
85
|
+
return { register, dispatch, closeAll };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Pure filter-match predicate. Exposed for unit testing so executable
|
|
90
|
+
* client-side divergence proofs can force known-bad subscription
|
|
91
|
+
* behavior without bypassing the production dispatch path.
|
|
92
|
+
*
|
|
93
|
+
* Returns `true` iff `frame` matches every set field on `filter`:
|
|
94
|
+
* - `filter.emissionTag === frame.data.__emissionTag` (strict ===)
|
|
95
|
+
* - `filter.conversationId === frame.data.conversationId` (strict ===)
|
|
96
|
+
* - `frame.event.startsWith(filter.eventNamePrefix)`
|
|
97
|
+
* Unset filter fields are wildcards.
|
|
98
|
+
*/
|
|
99
|
+
export function matchesFilter(filter, frame) {
|
|
100
|
+
if (filter.eventNamePrefix !== undefined) {
|
|
101
|
+
if (!frame.event.startsWith(filter.eventNamePrefix))
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
// `frame.data` is `unknown` per the EventFrame schema; narrow it to
|
|
105
|
+
// a record before reading the two payload-keyed fields. Anything
|
|
106
|
+
// else (string, number, array) cannot satisfy a payload-key filter.
|
|
107
|
+
// The cast is annotated below: the EventFrame schema declares `data:
|
|
108
|
+
// unknown`, and the OQ-2 A filter grammar intentionally treats
|
|
109
|
+
// payloads as untyped maps so arbitrary publisher payloads can be
|
|
110
|
+
// filtered without a per-event schema tax. The `typeof === "object"`
|
|
111
|
+
// guard is the runtime boundary; the read sites
|
|
112
|
+
// (`data?.["__emissionTag"]`, `data?.["conversationId"]`) defend
|
|
113
|
+
// against missing or wrong-typed values via `=== filter.<field>`.
|
|
114
|
+
const data = typeof frame.data === "object" &&
|
|
115
|
+
frame.data !== null &&
|
|
116
|
+
!Array.isArray(frame.data)
|
|
117
|
+
? frame.data // #ignore-sloppy-code[record-cast]: EventFrame schema declares `data: unknown`; OQ-2 A filter treats payloads as untyped maps; the typeof guard above is the runtime boundary
|
|
118
|
+
: null;
|
|
119
|
+
if (filter.emissionTag !== undefined) {
|
|
120
|
+
const tag = data?.["__emissionTag"];
|
|
121
|
+
if (tag !== filter.emissionTag)
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (filter.conversationId !== undefined) {
|
|
125
|
+
const cid = data?.["conversationId"];
|
|
126
|
+
if (cid !== filter.conversationId)
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=subscribers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribers.js","sourceRoot":"","sources":["../../src/runtime/subscribers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribers.test.d.ts","sourceRoot":"","sources":["../../src/runtime/subscribers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the per-subscription event registry.
|
|
3
|
+
*
|
|
4
|
+
* Spec #222 §5.3 (C4 + the `RealClientEventSubscriber.subscribe` filter
|
|
5
|
+
* stub) requires:
|
|
6
|
+
* - Every set filter field narrows delivery (AND semantics).
|
|
7
|
+
* - Subscriptions fire in registration order.
|
|
8
|
+
* - Unsubscribe-during-dispatch is next-frame effective (OQ-3 A
|
|
9
|
+
* snapshot semantics).
|
|
10
|
+
* - Handler exceptions are caught + logged, not propagated.
|
|
11
|
+
*
|
|
12
|
+
* Each scenario gets a dedicated test so a mutation that bypasses the
|
|
13
|
+
* filter (e.g. force `matchesFilter` to always return `true`) trips the
|
|
14
|
+
* filter-narrows tests but leaves order tests passing — the tests
|
|
15
|
+
* discriminate.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, it, vi } from "vitest";
|
|
18
|
+
import { Effect, Ref } from "effect";
|
|
19
|
+
import { makeSubscriberRegistry, matchesFilter } from "./subscribers.js";
|
|
20
|
+
const eventFrame = (event, data) => ({
|
|
21
|
+
jsonrpc: "2.0",
|
|
22
|
+
type: "event",
|
|
23
|
+
event,
|
|
24
|
+
data,
|
|
25
|
+
});
|
|
26
|
+
const noopLogger = { warn: vi.fn() };
|
|
27
|
+
describe("matchesFilter", () => {
|
|
28
|
+
it("empty filter matches every frame", () => {
|
|
29
|
+
expect(matchesFilter({}, eventFrame("any", { x: 1 }))).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
it("emissionTag matches on `__emissionTag` key", () => {
|
|
32
|
+
const frame = eventFrame("e", { __emissionTag: "tag-1" });
|
|
33
|
+
expect(matchesFilter({ emissionTag: "tag-1" }, frame)).toBe(true);
|
|
34
|
+
expect(matchesFilter({ emissionTag: "tag-other" }, frame)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it("conversationId matches on `conversationId` key", () => {
|
|
37
|
+
const frame = eventFrame("e", { conversationId: "c-1" });
|
|
38
|
+
expect(matchesFilter({ conversationId: "c-1" }, frame)).toBe(true);
|
|
39
|
+
expect(matchesFilter({ conversationId: "c-2" }, frame)).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
it("eventNamePrefix uses startsWith", () => {
|
|
42
|
+
const frame = eventFrame("messages/received", {});
|
|
43
|
+
expect(matchesFilter({ eventNamePrefix: "messages/" }, frame)).toBe(true);
|
|
44
|
+
expect(matchesFilter({ eventNamePrefix: "presence/" }, frame)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
it("AND semantics: all set fields must match", () => {
|
|
47
|
+
const frame = eventFrame("messages/received", {
|
|
48
|
+
__emissionTag: "tag-1",
|
|
49
|
+
conversationId: "c-1",
|
|
50
|
+
});
|
|
51
|
+
expect(matchesFilter({ emissionTag: "tag-1", conversationId: "c-1", eventNamePrefix: "m" }, frame)).toBe(true);
|
|
52
|
+
// conversationId mismatch → reject even when other fields match.
|
|
53
|
+
expect(matchesFilter({ emissionTag: "tag-1", conversationId: "c-other" }, frame)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it("non-record `data` cannot satisfy payload-key filters", () => {
|
|
56
|
+
const stringData = eventFrame("e", "string-payload");
|
|
57
|
+
expect(matchesFilter({ emissionTag: "x" }, stringData)).toBe(false);
|
|
58
|
+
expect(matchesFilter({ conversationId: "x" }, stringData)).toBe(false);
|
|
59
|
+
// …but an event-name-prefix filter still applies.
|
|
60
|
+
expect(matchesFilter({ eventNamePrefix: "e" }, stringData)).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("SubscriberRegistry", () => {
|
|
64
|
+
it("dispatches in registration order", async () => {
|
|
65
|
+
const registry = await Effect.runPromise(makeSubscriberRegistry(noopLogger));
|
|
66
|
+
const order = [];
|
|
67
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => void order.push("a"))));
|
|
68
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => void order.push("b"))));
|
|
69
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", {})));
|
|
70
|
+
expect(order).toEqual(["a", "b"]);
|
|
71
|
+
});
|
|
72
|
+
it("unsubscribe stops delivery for the next frame (OQ-3 A snapshot)", async () => {
|
|
73
|
+
const registry = await Effect.runPromise(makeSubscriberRegistry(noopLogger));
|
|
74
|
+
const aCalls = [];
|
|
75
|
+
const bCalls = [];
|
|
76
|
+
let frameIdx = 0;
|
|
77
|
+
let unsubA = null;
|
|
78
|
+
const handleA = await Effect.runPromise(registry.register({}, () => Effect.gen(function* () {
|
|
79
|
+
aCalls.push(frameIdx);
|
|
80
|
+
// During the FIRST frame's dispatch, a unsubscribes itself.
|
|
81
|
+
// OQ-3 A: b should still see frame 0 (snapshot taken at
|
|
82
|
+
// dispatch start), but a should NOT see frame 1.
|
|
83
|
+
if (frameIdx === 0 && unsubA !== null) {
|
|
84
|
+
yield* unsubA;
|
|
85
|
+
}
|
|
86
|
+
})));
|
|
87
|
+
unsubA = handleA.unsubscribe;
|
|
88
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => void bCalls.push(frameIdx))));
|
|
89
|
+
frameIdx = 0;
|
|
90
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", {})));
|
|
91
|
+
frameIdx = 1;
|
|
92
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", {})));
|
|
93
|
+
expect(aCalls).toEqual([0]); // a saw frame 0, not frame 1.
|
|
94
|
+
expect(bCalls).toEqual([0, 1]); // b saw both — snapshot at start of frame 0 still included a; b is unaffected by a's unsub.
|
|
95
|
+
});
|
|
96
|
+
it("filters narrow delivery", async () => {
|
|
97
|
+
const registry = await Effect.runPromise(makeSubscriberRegistry(noopLogger));
|
|
98
|
+
const seenByConv1 = [];
|
|
99
|
+
const seenByConv2 = [];
|
|
100
|
+
await Effect.runPromise(registry.register({ conversationId: "c-1" }, (frame) => Effect.sync(() => void seenByConv1.push(frame))));
|
|
101
|
+
await Effect.runPromise(registry.register({ conversationId: "c-2" }, (frame) => Effect.sync(() => void seenByConv2.push(frame))));
|
|
102
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", { conversationId: "c-1" })));
|
|
103
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", { conversationId: "c-2" })));
|
|
104
|
+
expect(seenByConv1).toHaveLength(1);
|
|
105
|
+
expect(seenByConv2).toHaveLength(1);
|
|
106
|
+
expect(seenByConv1[0].data.conversationId).toBe("c-1");
|
|
107
|
+
expect(seenByConv2[0].data.conversationId).toBe("c-2");
|
|
108
|
+
});
|
|
109
|
+
it("handler exceptions are caught + logged via the injected logger", async () => {
|
|
110
|
+
const warn = vi.fn();
|
|
111
|
+
const registry = await Effect.runPromise(makeSubscriberRegistry({ warn }));
|
|
112
|
+
const otherCalls = [];
|
|
113
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => {
|
|
114
|
+
throw new Error("subscriber blew up");
|
|
115
|
+
})));
|
|
116
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => void otherCalls.push(1))));
|
|
117
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", {})));
|
|
118
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
119
|
+
expect(otherCalls).toEqual([1]); // other subscribers still fire.
|
|
120
|
+
});
|
|
121
|
+
it("construction-time throw from handler is caught + logged (not escaped)", async () => {
|
|
122
|
+
const warn = vi.fn();
|
|
123
|
+
const registry = await Effect.runPromise(makeSubscriberRegistry({ warn }));
|
|
124
|
+
const otherCalls = [];
|
|
125
|
+
await Effect.runPromise(registry.register({}, (_frame) => {
|
|
126
|
+
throw new Error("construction-time throw");
|
|
127
|
+
}));
|
|
128
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => void otherCalls.push(1))));
|
|
129
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", {})));
|
|
130
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
131
|
+
expect(otherCalls).toEqual([1]); // subsequent subscribers still fire.
|
|
132
|
+
});
|
|
133
|
+
it("closeAll drops every subscription", async () => {
|
|
134
|
+
const registry = await Effect.runPromise(makeSubscriberRegistry(noopLogger));
|
|
135
|
+
const calls = [];
|
|
136
|
+
await Effect.runPromise(registry.register({}, () => Effect.sync(() => void calls.push(1))));
|
|
137
|
+
await Effect.runPromise(registry.closeAll);
|
|
138
|
+
await Effect.runPromise(registry.dispatch(eventFrame("e", {})));
|
|
139
|
+
expect(calls).toEqual([]);
|
|
140
|
+
});
|
|
141
|
+
it("Ref-make composition works: register inside an Effect.gen", async () => {
|
|
142
|
+
// Smoke test that the Effect surface composes; the registry never
|
|
143
|
+
// resolves with `never` failure tag, so this can't catch
|
|
144
|
+
// typechecker drift, but it does pin behaviour.
|
|
145
|
+
const result = await Effect.runPromise(Effect.gen(function* () {
|
|
146
|
+
const registry = yield* makeSubscriberRegistry(noopLogger);
|
|
147
|
+
const counter = yield* Ref.make(0);
|
|
148
|
+
yield* registry.register({}, () => Ref.update(counter, (n) => n + 1));
|
|
149
|
+
yield* registry.dispatch(eventFrame("e", {}));
|
|
150
|
+
return yield* Ref.get(counter);
|
|
151
|
+
}));
|
|
152
|
+
expect(result).toBe(1);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=subscribers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribers.test.js","sourceRoot":"","sources":["../../src/runtime/subscribers.test.ts"],"names":[],"mappings":"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"}
|