@moltzap/protocol 2026.408.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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/rpc-registry.d.ts +446 -0
- package/dist/rpc-registry.d.ts.map +1 -0
- package/dist/rpc-registry.js +67 -0
- package/dist/rpc-registry.js.map +1 -0
- package/dist/rpc.d.ts +42 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +29 -0
- package/dist/rpc.js.map +1 -0
- package/dist/schema/apps.d.ts +86 -0
- package/dist/schema/apps.d.ts.map +1 -0
- package/dist/schema/apps.js +77 -0
- package/dist/schema/apps.js.map +1 -0
- package/dist/schema/contacts.d.ts +8 -18
- package/dist/schema/contacts.d.ts.map +1 -1
- package/dist/schema/contacts.js +9 -13
- package/dist/schema/contacts.js.map +1 -1
- package/dist/schema/conversations.d.ts +15 -8
- package/dist/schema/conversations.d.ts.map +1 -1
- package/dist/schema/conversations.js +18 -7
- package/dist/schema/conversations.js.map +1 -1
- package/dist/schema/delivery.d.ts +1 -4
- package/dist/schema/delivery.d.ts.map +1 -1
- package/dist/schema/delivery.js +2 -3
- package/dist/schema/delivery.js.map +1 -1
- package/dist/schema/errors.d.ts +13 -0
- package/dist/schema/errors.d.ts.map +1 -1
- package/dist/schema/errors.js +14 -0
- package/dist/schema/errors.js.map +1 -1
- package/dist/schema/events.d.ts +112 -93
- package/dist/schema/events.d.ts.map +1 -1
- package/dist/schema/events.js +84 -25
- package/dist/schema/events.js.map +1 -1
- package/dist/schema/identity.d.ts +19 -17
- package/dist/schema/identity.d.ts.map +1 -1
- package/dist/schema/identity.js +7 -14
- package/dist/schema/identity.js.map +1 -1
- package/dist/schema/index.d.ts +4 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +4 -2
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/messages.d.ts +3 -7
- package/dist/schema/messages.d.ts.map +1 -1
- package/dist/schema/messages.js +4 -6
- package/dist/schema/messages.js.map +1 -1
- package/dist/schema/methods/apps.d.ts +123 -0
- package/dist/schema/methods/apps.d.ts.map +1 -0
- package/dist/schema/methods/apps.js +93 -0
- package/dist/schema/methods/apps.js.map +1 -0
- package/dist/schema/methods/auth.d.ts +102 -90
- package/dist/schema/methods/auth.d.ts.map +1 -1
- package/dist/schema/methods/auth.js +86 -74
- package/dist/schema/methods/auth.js.map +1 -1
- package/dist/schema/methods/contacts.d.ts +39 -74
- package/dist/schema/methods/contacts.d.ts.map +1 -1
- package/dist/schema/methods/contacts.js +35 -29
- package/dist/schema/methods/contacts.js.map +1 -1
- package/dist/schema/methods/conversations.d.ts +48 -52
- package/dist/schema/methods/conversations.d.ts.map +1 -1
- package/dist/schema/methods/conversations.js +89 -38
- package/dist/schema/methods/conversations.js.map +1 -1
- package/dist/schema/methods/invites.d.ts +1 -3
- package/dist/schema/methods/invites.d.ts.map +1 -1
- package/dist/schema/methods/invites.js +8 -1
- package/dist/schema/methods/invites.js.map +1 -1
- package/dist/schema/methods/messages.d.ts +12 -44
- package/dist/schema/methods/messages.d.ts.map +1 -1
- package/dist/schema/methods/messages.js +22 -28
- package/dist/schema/methods/messages.js.map +1 -1
- package/dist/schema/methods/presence.d.ts +7 -22
- package/dist/schema/methods/presence.d.ts.map +1 -1
- package/dist/schema/methods/presence.js +12 -6
- package/dist/schema/methods/presence.js.map +1 -1
- package/dist/schema/methods/push.d.ts +8 -6
- package/dist/schema/methods/push.d.ts.map +1 -1
- package/dist/schema/methods/push.js +20 -7
- package/dist/schema/methods/push.js.map +1 -1
- package/dist/schema/methods/system.d.ts +4 -0
- package/dist/schema/methods/system.d.ts.map +1 -0
- package/dist/schema/methods/system.js +11 -0
- package/dist/schema/methods/system.js.map +1 -0
- package/dist/schema/presence.d.ts +1 -12
- package/dist/schema/presence.d.ts.map +1 -1
- package/dist/schema/presence.js +2 -7
- package/dist/schema/presence.js.map +1 -1
- package/dist/schema/surfaces.d.ts +31 -25
- package/dist/schema/surfaces.d.ts.map +1 -1
- package/dist/schema/surfaces.js +40 -21
- package/dist/schema/surfaces.js.map +1 -1
- package/dist/testing/agent-registration.d.ts +49 -0
- package/dist/testing/agent-registration.d.ts.map +1 -0
- package/dist/testing/agent-registration.js +77 -0
- package/dist/testing/agent-registration.js.map +1 -0
- package/dist/testing/arbitraries/frames.d.ts +24 -0
- package/dist/testing/arbitraries/frames.d.ts.map +1 -0
- package/dist/testing/arbitraries/frames.js +36 -0
- package/dist/testing/arbitraries/frames.js.map +1 -0
- package/dist/testing/arbitraries/from-typebox.d.ts +37 -0
- package/dist/testing/arbitraries/from-typebox.d.ts.map +1 -0
- package/dist/testing/arbitraries/from-typebox.js +131 -0
- package/dist/testing/arbitraries/from-typebox.js.map +1 -0
- package/dist/testing/arbitraries/index.d.ts +4 -0
- package/dist/testing/arbitraries/index.d.ts.map +1 -0
- package/dist/testing/arbitraries/index.js +4 -0
- package/dist/testing/arbitraries/index.js.map +1 -0
- package/dist/testing/arbitraries/rpc.d.ts +66 -0
- package/dist/testing/arbitraries/rpc.d.ts.map +1 -0
- package/dist/testing/arbitraries/rpc.js +98 -0
- package/dist/testing/arbitraries/rpc.js.map +1 -0
- package/dist/testing/canonicalize.d.ts +38 -0
- package/dist/testing/canonicalize.d.ts.map +1 -0
- package/dist/testing/canonicalize.js +55 -0
- package/dist/testing/canonicalize.js.map +1 -0
- package/dist/testing/captures.d.ts +61 -0
- package/dist/testing/captures.d.ts.map +1 -0
- package/dist/testing/captures.js +99 -0
- package/dist/testing/captures.js.map +1 -0
- package/dist/testing/codec.d.ts +44 -0
- package/dist/testing/codec.d.ts.map +1 -0
- package/dist/testing/codec.js +170 -0
- package/dist/testing/codec.js.map +1 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.d.ts +6 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.d.ts.map +1 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.js +33 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.js.map +1 -0
- package/dist/testing/conformance/adversity.d.ts +36 -0
- package/dist/testing/conformance/adversity.d.ts.map +1 -0
- package/dist/testing/conformance/adversity.js +360 -0
- package/dist/testing/conformance/adversity.js.map +1 -0
- package/dist/testing/conformance/boundary.d.ts +56 -0
- package/dist/testing/conformance/boundary.d.ts.map +1 -0
- package/dist/testing/conformance/boundary.js +129 -0
- package/dist/testing/conformance/boundary.js.map +1 -0
- package/dist/testing/conformance/client/_fixtures.d.ts +71 -0
- package/dist/testing/conformance/client/_fixtures.d.ts.map +1 -0
- package/dist/testing/conformance/client/_fixtures.js +102 -0
- package/dist/testing/conformance/client/_fixtures.js.map +1 -0
- package/dist/testing/conformance/client/adversity.d.ts +39 -0
- package/dist/testing/conformance/client/adversity.d.ts.map +1 -0
- package/dist/testing/conformance/client/adversity.js +162 -0
- package/dist/testing/conformance/client/adversity.js.map +1 -0
- package/dist/testing/conformance/client/boundary.d.ts +12 -0
- package/dist/testing/conformance/client/boundary.d.ts.map +1 -0
- package/dist/testing/conformance/client/boundary.js +68 -0
- package/dist/testing/conformance/client/boundary.js.map +1 -0
- package/dist/testing/conformance/client/delivery.d.ts +38 -0
- package/dist/testing/conformance/client/delivery.d.ts.map +1 -0
- package/dist/testing/conformance/client/delivery.js +202 -0
- package/dist/testing/conformance/client/delivery.js.map +1 -0
- package/dist/testing/conformance/client/index.d.ts +16 -0
- package/dist/testing/conformance/client/index.d.ts.map +1 -0
- package/dist/testing/conformance/client/index.js +16 -0
- package/dist/testing/conformance/client/index.js.map +1 -0
- package/dist/testing/conformance/client/rpc-semantics.d.ts +26 -0
- package/dist/testing/conformance/client/rpc-semantics.d.ts.map +1 -0
- package/dist/testing/conformance/client/rpc-semantics.js +145 -0
- package/dist/testing/conformance/client/rpc-semantics.js.map +1 -0
- package/dist/testing/conformance/client/runner.d.ts +258 -0
- package/dist/testing/conformance/client/runner.d.ts.map +1 -0
- package/dist/testing/conformance/client/runner.js +228 -0
- package/dist/testing/conformance/client/runner.js.map +1 -0
- package/dist/testing/conformance/client/schema-conformance.d.ts +25 -0
- package/dist/testing/conformance/client/schema-conformance.d.ts.map +1 -0
- package/dist/testing/conformance/client/schema-conformance.js +123 -0
- package/dist/testing/conformance/client/schema-conformance.js.map +1 -0
- package/dist/testing/conformance/client/suite.d.ts +90 -0
- package/dist/testing/conformance/client/suite.d.ts.map +1 -0
- package/dist/testing/conformance/client/suite.js +209 -0
- package/dist/testing/conformance/client/suite.js.map +1 -0
- package/dist/testing/conformance/coverage-policy.d.ts +8 -0
- package/dist/testing/conformance/coverage-policy.d.ts.map +1 -0
- package/dist/testing/conformance/coverage-policy.js +10 -0
- package/dist/testing/conformance/coverage-policy.js.map +1 -0
- package/dist/testing/conformance/delivery.d.ts +40 -0
- package/dist/testing/conformance/delivery.d.ts.map +1 -0
- package/dist/testing/conformance/delivery.js +231 -0
- package/dist/testing/conformance/delivery.js.map +1 -0
- package/dist/testing/conformance/env.d.ts +3 -0
- package/dist/testing/conformance/env.d.ts.map +1 -0
- package/dist/testing/conformance/env.js +14 -0
- package/dist/testing/conformance/env.js.map +1 -0
- package/dist/testing/conformance/index.d.ts +10 -0
- package/dist/testing/conformance/index.d.ts.map +1 -0
- package/dist/testing/conformance/index.js +10 -0
- package/dist/testing/conformance/index.js.map +1 -0
- package/dist/testing/conformance/registry.d.ts +93 -0
- package/dist/testing/conformance/registry.d.ts.map +1 -0
- package/dist/testing/conformance/registry.js +62 -0
- package/dist/testing/conformance/registry.js.map +1 -0
- package/dist/testing/conformance/rpc-semantics.d.ts +67 -0
- package/dist/testing/conformance/rpc-semantics.d.ts.map +1 -0
- package/dist/testing/conformance/rpc-semantics.js +394 -0
- package/dist/testing/conformance/rpc-semantics.js.map +1 -0
- package/dist/testing/conformance/runner.d.ts +78 -0
- package/dist/testing/conformance/runner.d.ts.map +1 -0
- package/dist/testing/conformance/runner.js +65 -0
- package/dist/testing/conformance/runner.js.map +1 -0
- package/dist/testing/conformance/schema-conformance.d.ts +30 -0
- package/dist/testing/conformance/schema-conformance.d.ts.map +1 -0
- package/dist/testing/conformance/schema-conformance.js +229 -0
- package/dist/testing/conformance/schema-conformance.js.map +1 -0
- package/dist/testing/conformance/suite.d.ts +92 -0
- package/dist/testing/conformance/suite.d.ts.map +1 -0
- package/dist/testing/conformance/suite.js +233 -0
- package/dist/testing/conformance/suite.js.map +1 -0
- package/dist/testing/errors.d.ts +78 -0
- package/dist/testing/errors.d.ts.map +1 -0
- package/dist/testing/errors.js +34 -0
- package/dist/testing/errors.js.map +1 -0
- package/dist/testing/index.d.ts +25 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +37 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/models/dispatch.d.ts +61 -0
- package/dist/testing/models/dispatch.d.ts.map +1 -0
- package/dist/testing/models/dispatch.js +197 -0
- package/dist/testing/models/dispatch.js.map +1 -0
- package/dist/testing/models/index.d.ts +3 -0
- package/dist/testing/models/index.d.ts.map +1 -0
- package/dist/testing/models/index.js +3 -0
- package/dist/testing/models/index.js.map +1 -0
- package/dist/testing/models/state.d.ts +37 -0
- package/dist/testing/models/state.d.ts.map +1 -0
- package/dist/testing/models/state.js +14 -0
- package/dist/testing/models/state.js.map +1 -0
- package/dist/testing/test-client.d.ts +70 -0
- package/dist/testing/test-client.d.ts.map +1 -0
- package/dist/testing/test-client.js +266 -0
- package/dist/testing/test-client.js.map +1 -0
- package/dist/testing/test-server.d.ts +62 -0
- package/dist/testing/test-server.d.ts.map +1 -0
- package/dist/testing/test-server.js +134 -0
- package/dist/testing/test-server.js.map +1 -0
- package/dist/testing/toxics/client.d.ts +52 -0
- package/dist/testing/toxics/client.d.ts.map +1 -0
- package/dist/testing/toxics/client.js +120 -0
- package/dist/testing/toxics/client.js.map +1 -0
- package/dist/testing/toxics/defaults.d.ts +43 -0
- package/dist/testing/toxics/defaults.d.ts.map +1 -0
- package/dist/testing/toxics/defaults.js +9 -0
- package/dist/testing/toxics/defaults.js.map +1 -0
- package/dist/testing/toxics/index.d.ts +4 -0
- package/dist/testing/toxics/index.d.ts.map +1 -0
- package/dist/testing/toxics/index.js +4 -0
- package/dist/testing/toxics/index.js.map +1 -0
- package/dist/testing/toxics/profile.d.ts +69 -0
- package/dist/testing/toxics/profile.d.ts.map +1 -0
- package/dist/testing/toxics/profile.js +57 -0
- package/dist/testing/toxics/profile.js.map +1 -0
- package/dist/types.d.ts +7 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/dist/validators.d.ts +49 -177
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +77 -48
- package/dist/validators.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +18 -40
- package/LICENSE +0 -21
- package/dist/optional/contact-events.d.ts +0 -3
- package/dist/optional/contact-events.d.ts.map +0 -1
- package/dist/optional/contact-events.js +0 -5
- package/dist/optional/contact-events.js.map +0 -1
- package/dist/optional/contact-methods.d.ts +0 -5
- package/dist/optional/contact-methods.d.ts.map +0 -1
- package/dist/optional/contact-methods.js +0 -5
- package/dist/optional/contact-methods.js.map +0 -1
- package/dist/phone-hash.d.ts +0 -10
- package/dist/phone-hash.d.ts.map +0 -1
- package/dist/phone-hash.js +0 -17
- package/dist/phone-hash.js.map +0 -1
- package/dist/schema/methods/phone-contacts.d.ts +0 -30
- package/dist/schema/methods/phone-contacts.d.ts.map +0 -1
- package/dist/schema/methods/phone-contacts.js +0 -10
- package/dist/schema/methods/phone-contacts.js.map +0 -1
- package/dist/test-client.d.ts +0 -34
- package/dist/test-client.d.ts.map +0 -1
- package/dist/test-client.js +0 -176
- package/dist/test-client.js.map +0 -1
- package/dist/test-fixtures/phone-hashes.d.ts +0 -18
- package/dist/test-fixtures/phone-hashes.d.ts.map +0 -1
- package/dist/test-fixtures/phone-hashes.js +0 -24
- package/dist/test-fixtures/phone-hashes.js.map +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side boundary properties.
|
|
3
|
+
*
|
|
4
|
+
* Covers spec-amendment #200 §5:
|
|
5
|
+
* E2 — schema-exhaustive-fuzz (client half of both-sides)
|
|
6
|
+
*
|
|
7
|
+
* E1 (webhook-graceful-shutdown) is N/A on the client side per spec —
|
|
8
|
+
* no client-observable surface.
|
|
9
|
+
*/
|
|
10
|
+
import { Effect } from "effect";
|
|
11
|
+
import * as fc from "fast-check";
|
|
12
|
+
import { arbitraryEventFrame } from "../../arbitraries/frames.js";
|
|
13
|
+
import { registerProperty } from "../registry.js";
|
|
14
|
+
import { acquireFixture, collectTagged, invariant, subscribeAll, } from "./_fixtures.js";
|
|
15
|
+
const CATEGORY = "boundary";
|
|
16
|
+
const PROPERTY_BUDGET_MS = 12_000;
|
|
17
|
+
/**
|
|
18
|
+
* E2 client half — TestServer emits arbitrary `EventFrame`s across
|
|
19
|
+
* many shapes to a real client. Properties interleave with a tagged
|
|
20
|
+
* liveness probe and a task-boundary assertion.
|
|
21
|
+
*
|
|
22
|
+
* Predicate (both must hold):
|
|
23
|
+
* 1. No crash — real client stays `ready`; no spurious closeSignal.
|
|
24
|
+
* 2. Liveness probe — a valid tagged event emitted post-fuzz surfaces.
|
|
25
|
+
*/
|
|
26
|
+
export function registerSchemaExhaustiveFuzzClient(ctx) {
|
|
27
|
+
registerProperty(ctx, CATEGORY, "schema-exhaustive-fuzz-client", "real client absorbs arbitrary EventFrames; liveness and boundary hold", Effect.scoped(Effect.gen(function* () {
|
|
28
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "schema-exhaustive-fuzz-client");
|
|
29
|
+
yield* subscribeAll(fx.handle);
|
|
30
|
+
// Fuzz burst: default 10 frames; stress mode scales with
|
|
31
|
+
// CONFORMANCE_NUM_RUNS through ctx.opts.numRuns.
|
|
32
|
+
const fuzzRuns = ctx.opts.numRuns ?? 10;
|
|
33
|
+
const burst = fc.sample(arbitraryEventFrame(), {
|
|
34
|
+
numRuns: fuzzRuns,
|
|
35
|
+
seed: ctx.seed,
|
|
36
|
+
});
|
|
37
|
+
for (const frame of burst) {
|
|
38
|
+
yield* fx.connection
|
|
39
|
+
.emitEvent(frame)
|
|
40
|
+
.pipe(Effect.orElseSucceed(() => undefined));
|
|
41
|
+
}
|
|
42
|
+
// (1) Real client still alive — closeSignal not fired.
|
|
43
|
+
const closeRace = yield* Effect.race(fx.handle.closeSignal.pipe(Effect.as("closed")), Effect.sleep("100 millis").pipe(Effect.as("alive")));
|
|
44
|
+
if (closeRace === "closed") {
|
|
45
|
+
return yield* Effect.fail(invariant(CATEGORY, "schema-exhaustive-fuzz-client", "real client closed during fuzz burst"));
|
|
46
|
+
}
|
|
47
|
+
// (2) Liveness probe.
|
|
48
|
+
const tag = yield* fx.window.freshEmissionTag;
|
|
49
|
+
yield* fx.window.emitTaggedEvent({
|
|
50
|
+
connection: fx.connection,
|
|
51
|
+
base: {
|
|
52
|
+
jsonrpc: "2.0",
|
|
53
|
+
type: "event",
|
|
54
|
+
event: "messages.delivered",
|
|
55
|
+
data: {},
|
|
56
|
+
},
|
|
57
|
+
emissionTag: tag,
|
|
58
|
+
});
|
|
59
|
+
const observed = yield* collectTagged(fx.handle, (t) => t === tag, {
|
|
60
|
+
expected: 1,
|
|
61
|
+
budgetMs: PROPERTY_BUDGET_MS,
|
|
62
|
+
});
|
|
63
|
+
if (observed.length === 0) {
|
|
64
|
+
return yield* Effect.fail(invariant(CATEGORY, "schema-exhaustive-fuzz-client", "liveness probe never surfaced after fuzz burst"));
|
|
65
|
+
}
|
|
66
|
+
})));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/boundary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,cAAc,EACd,aAAa,EACb,SAAS,EACT,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,MAAM,QAAQ,GAAG,UAAmB,CAAC;AACrC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,MAAM,UAAU,kCAAkC,CAChD,GAAgC;IAEhC,gBAAgB,CACd,GAAG,EACH,QAAQ,EACR,+BAA+B,EAC/B,uEAAuE,EACvE,MAAM,CAAC,MAAM,CACX,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAC9B,GAAG,EACH,QAAQ,EACR,+BAA+B,CAChC,CAAC;QACF,KAAK,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QAC/B,yDAAyD;QACzD,iDAAiD;QACjD,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE,EAAE;YAC7C,OAAO,EAAE,QAAQ;YACjB,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,KAAK,CAAC,CAAC,EAAE,CAAC,UAAU;iBACjB,SAAS,CAAC,KAAK,CAAC;iBAChB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACjD,CAAC;QACD,uDAAuD;QACvD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAClC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAiB,CAAC,CAAC,EACxD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAgB,CAAC,CAAC,CAC7D,CAAC;QACF,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,SAAS,CACP,QAAQ,EACR,+BAA+B,EAC/B,sCAAsC,CACvC,CACF,CAAC;QACJ,CAAC;QACD,sBAAsB;QACtB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAC9C,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC;YAC/B,UAAU,EAAE,EAAE,CAAC,UAAU;YACzB,IAAI,EAAE;gBACJ,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,oBAAoB;gBAC3B,IAAI,EAAE,EAAE;aACT;YACD,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,EAAE;YACjE,QAAQ,EAAE,CAAC;YACX,QAAQ,EAAE,kBAAkB;SAC7B,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,SAAS,CACP,QAAQ,EACR,+BAA+B,EAC/B,gDAAgD,CACjD,CACF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CACH,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ClientConformanceRunContext } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* C1 client-side — TestServer emits N fan-out `EventFrame`s (one
|
|
4
|
+
* per conversation participant position) to a real client subscribed
|
|
5
|
+
* to the conversation. All N carry the same `emissionTag` campaign;
|
|
6
|
+
* each carries a per-position `positionIndex` in the payload.
|
|
7
|
+
*
|
|
8
|
+
* Predicate (conjunction):
|
|
9
|
+
* - `observedByCampaign.length === N`
|
|
10
|
+
* - every `positionIndex` in `[0..N)` appears exactly once
|
|
11
|
+
* - observation order matches emission order
|
|
12
|
+
*
|
|
13
|
+
* Discriminates: a client that coalesces duplicate fan-out frames,
|
|
14
|
+
* drops one, or reorders the sequence fails.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerFanOutCardinalityClient(ctx: ClientConformanceRunContext): void;
|
|
17
|
+
/**
|
|
18
|
+
* C3 client-side — TestServer emits a single `EventFrame` whose
|
|
19
|
+
* payload contains a distinct byte-sequence token; real client's
|
|
20
|
+
* subscriber surfaces a frame whose raw bytes still contain that
|
|
21
|
+
* token.
|
|
22
|
+
*
|
|
23
|
+
* Predicate (strict): the raw-bytes view of the surfaced event
|
|
24
|
+
* includes the emitted token byte-for-byte. A client that routes
|
|
25
|
+
* payloads through a lossy re-serialization (e.g., key-reorder JSON
|
|
26
|
+
* stringify) fails.
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerPayloadOpacityClient(ctx: ClientConformanceRunContext): void;
|
|
29
|
+
/**
|
|
30
|
+
* C4 client half — TestServer emits N task-A events (tagged campaignA)
|
|
31
|
+
* and M task-B events (tagged campaignB) to a real client subscribed
|
|
32
|
+
* with a `conversationId` filter set to task-A. The client's task-A
|
|
33
|
+
* subscriber surfaces zero campaignB events.
|
|
34
|
+
*
|
|
35
|
+
* Predicate: `observedCampaignB.length === 0`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function registerTaskBoundaryIsolationClient(ctx: ClientConformanceRunContext): void;
|
|
38
|
+
//# sourceMappingURL=delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/delivery.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAY/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAsEN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAsDN;AAED;;;;;;;GAOG;AACH,wBAAgB,mCAAmC,CACjD,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAiHN"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side delivery properties.
|
|
3
|
+
*
|
|
4
|
+
* Covers spec-amendment #200 §5:
|
|
5
|
+
* C1 — fan-out-cardinality (client-side new)
|
|
6
|
+
* C3 — payload-opacity (client-side new)
|
|
7
|
+
* C4 — task-boundary-isolation (client half of both-sides)
|
|
8
|
+
*
|
|
9
|
+
* Handshake-noise guard (O7): every observation filters by
|
|
10
|
+
* `emissionTag`. C1 tags each of N emissions with a shared campaign
|
|
11
|
+
* id; predicate asserts exactly N observed frames with that campaign
|
|
12
|
+
* id. C3 tags the one emission; predicate finds exactly one observed
|
|
13
|
+
* frame carrying the byte-identical payload. C4 tags task-A and
|
|
14
|
+
* task-B emissions with distinct campaigns; task-A subscriber must
|
|
15
|
+
* observe zero task-B campaign emissions.
|
|
16
|
+
*
|
|
17
|
+
* Exact-cardinality discipline (#195 §P1 on server-side C1 carries
|
|
18
|
+
* over): `observedCount === N`, not `≥ 1` and not `≤ N`. Duplicates
|
|
19
|
+
* and drops fail symmetrically.
|
|
20
|
+
*/
|
|
21
|
+
import { Effect } from "effect";
|
|
22
|
+
import * as fc from "fast-check";
|
|
23
|
+
import { arbitraryEventFrame } from "../../arbitraries/frames.js";
|
|
24
|
+
import { registerProperty } from "../registry.js";
|
|
25
|
+
import { acquireFixture, collectTagged, invariant, subscribeAll, } from "./_fixtures.js";
|
|
26
|
+
const CATEGORY = "delivery";
|
|
27
|
+
const PROPERTY_BUDGET_MS = 8_000;
|
|
28
|
+
/**
|
|
29
|
+
* C1 client-side — TestServer emits N fan-out `EventFrame`s (one
|
|
30
|
+
* per conversation participant position) to a real client subscribed
|
|
31
|
+
* to the conversation. All N carry the same `emissionTag` campaign;
|
|
32
|
+
* each carries a per-position `positionIndex` in the payload.
|
|
33
|
+
*
|
|
34
|
+
* Predicate (conjunction):
|
|
35
|
+
* - `observedByCampaign.length === N`
|
|
36
|
+
* - every `positionIndex` in `[0..N)` appears exactly once
|
|
37
|
+
* - observation order matches emission order
|
|
38
|
+
*
|
|
39
|
+
* Discriminates: a client that coalesces duplicate fan-out frames,
|
|
40
|
+
* drops one, or reorders the sequence fails.
|
|
41
|
+
*/
|
|
42
|
+
export function registerFanOutCardinalityClient(ctx) {
|
|
43
|
+
registerProperty(ctx, CATEGORY, "fan-out-cardinality-client", "N fan-out events surface on real client in emission order, no drops, no dups", Effect.scoped(Effect.gen(function* () {
|
|
44
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "fan-out-cardinality-client");
|
|
45
|
+
yield* subscribeAll(fx.handle);
|
|
46
|
+
const base = fc.sample(arbitraryEventFrame(), {
|
|
47
|
+
numRuns: 1,
|
|
48
|
+
seed: ctx.seed,
|
|
49
|
+
})[0];
|
|
50
|
+
if (base === undefined) {
|
|
51
|
+
return yield* Effect.fail(invariant(CATEGORY, "fan-out-cardinality-client", "sample failed"));
|
|
52
|
+
}
|
|
53
|
+
const N = 5;
|
|
54
|
+
const campaign = yield* fx.window.freshEmissionTag;
|
|
55
|
+
const baseData = (base.data ?? {}); // #ignore-sloppy-code[record-cast]: EventFrame.data is Type.Optional(Type.Unknown()); opaque payload merge
|
|
56
|
+
for (let i = 0; i < N; i++) {
|
|
57
|
+
const positional = {
|
|
58
|
+
...base,
|
|
59
|
+
data: { ...baseData, positionIndex: i },
|
|
60
|
+
};
|
|
61
|
+
yield* fx.window.emitTaggedEvent({
|
|
62
|
+
connection: fx.connection,
|
|
63
|
+
base: positional,
|
|
64
|
+
emissionTag: campaign,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const observed = yield* collectTagged(fx.handle, (t) => t === campaign, { expected: N, budgetMs: PROPERTY_BUDGET_MS });
|
|
68
|
+
if (observed.length !== N) {
|
|
69
|
+
return yield* Effect.fail(invariant(CATEGORY, "fan-out-cardinality-client", `expected ${N} observations, got ${observed.length}`));
|
|
70
|
+
}
|
|
71
|
+
const indices = observed.map((o) => o.data?.positionIndex);
|
|
72
|
+
// Strict ordering check against `[0..N)`.
|
|
73
|
+
for (let i = 0; i < N; i++) {
|
|
74
|
+
if (indices[i] !== i) {
|
|
75
|
+
return yield* Effect.fail(invariant(CATEGORY, "fan-out-cardinality-client", `order mismatch at slot ${i}: got ${String(indices[i])}`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* C3 client-side — TestServer emits a single `EventFrame` whose
|
|
82
|
+
* payload contains a distinct byte-sequence token; real client's
|
|
83
|
+
* subscriber surfaces a frame whose raw bytes still contain that
|
|
84
|
+
* token.
|
|
85
|
+
*
|
|
86
|
+
* Predicate (strict): the raw-bytes view of the surfaced event
|
|
87
|
+
* includes the emitted token byte-for-byte. A client that routes
|
|
88
|
+
* payloads through a lossy re-serialization (e.g., key-reorder JSON
|
|
89
|
+
* stringify) fails.
|
|
90
|
+
*/
|
|
91
|
+
export function registerPayloadOpacityClient(ctx) {
|
|
92
|
+
registerProperty(ctx, CATEGORY, "payload-opacity-client", "opaque payload token round-trips byte-identical through the real client", Effect.scoped(Effect.gen(function* () {
|
|
93
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "payload-opacity-client");
|
|
94
|
+
yield* subscribeAll(fx.handle);
|
|
95
|
+
const token = `opq-${ctx.seed.toString(36)}-${Date.now().toString(36)}`;
|
|
96
|
+
const base = {
|
|
97
|
+
jsonrpc: "2.0",
|
|
98
|
+
type: "event",
|
|
99
|
+
event: "messages.delivered",
|
|
100
|
+
data: { opaqueToken: token },
|
|
101
|
+
};
|
|
102
|
+
const tag = yield* fx.window.freshEmissionTag;
|
|
103
|
+
yield* fx.window.emitTaggedEvent({
|
|
104
|
+
connection: fx.connection,
|
|
105
|
+
base,
|
|
106
|
+
emissionTag: tag,
|
|
107
|
+
});
|
|
108
|
+
const observed = yield* collectTagged(fx.handle, (t) => t === tag, {
|
|
109
|
+
expected: 1,
|
|
110
|
+
budgetMs: PROPERTY_BUDGET_MS,
|
|
111
|
+
});
|
|
112
|
+
if (observed.length === 0) {
|
|
113
|
+
return yield* Effect.fail(invariant(CATEGORY, "payload-opacity-client", `token ${token} emission not surfaced by real client`));
|
|
114
|
+
}
|
|
115
|
+
const surfaced = observed[0];
|
|
116
|
+
const surfacedStr = new TextDecoder().decode(surfaced.raw);
|
|
117
|
+
if (!surfacedStr.includes(token)) {
|
|
118
|
+
return yield* Effect.fail(invariant(CATEGORY, "payload-opacity-client", `token ${token} not present byte-for-byte in surfaced raw frame`));
|
|
119
|
+
}
|
|
120
|
+
})));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* C4 client half — TestServer emits N task-A events (tagged campaignA)
|
|
124
|
+
* and M task-B events (tagged campaignB) to a real client subscribed
|
|
125
|
+
* with a `conversationId` filter set to task-A. The client's task-A
|
|
126
|
+
* subscriber surfaces zero campaignB events.
|
|
127
|
+
*
|
|
128
|
+
* Predicate: `observedCampaignB.length === 0`.
|
|
129
|
+
*/
|
|
130
|
+
export function registerTaskBoundaryIsolationClient(ctx) {
|
|
131
|
+
registerProperty(ctx, CATEGORY, "task-boundary-isolation-client", "task-A subscriber does not surface task-B events — no leakage", Effect.scoped(Effect.gen(function* () {
|
|
132
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "task-boundary-isolation-client");
|
|
133
|
+
yield* subscribeAll(fx.handle);
|
|
134
|
+
const baseEvent = fc.sample(arbitraryEventFrame(), {
|
|
135
|
+
numRuns: 1,
|
|
136
|
+
seed: ctx.seed,
|
|
137
|
+
})[0];
|
|
138
|
+
if (baseEvent === undefined) {
|
|
139
|
+
return yield* Effect.fail(invariant(CATEGORY, "task-boundary-isolation-client", "sample failed"));
|
|
140
|
+
}
|
|
141
|
+
const campaignA = yield* fx.window.freshEmissionTag;
|
|
142
|
+
const campaignB = yield* fx.window.freshEmissionTag;
|
|
143
|
+
const taskA = `task-a-${ctx.seed}`;
|
|
144
|
+
const taskB = `task-b-${ctx.seed}`;
|
|
145
|
+
const baseEventData = (baseEvent.data ?? {}); // #ignore-sloppy-code[record-cast]: EventFrame.data is Type.Optional(Type.Unknown()); opaque payload merge
|
|
146
|
+
// Emit task-A frames.
|
|
147
|
+
for (let i = 0; i < 3; i++) {
|
|
148
|
+
yield* fx.window.emitTaggedEvent({
|
|
149
|
+
connection: fx.connection,
|
|
150
|
+
base: {
|
|
151
|
+
...baseEvent,
|
|
152
|
+
data: { ...baseEventData, conversationId: taskA },
|
|
153
|
+
},
|
|
154
|
+
emissionTag: campaignA,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// Emit task-B frames that must be filtered out by the client's
|
|
158
|
+
// subscription (via conversationId filter).
|
|
159
|
+
for (let i = 0; i < 3; i++) {
|
|
160
|
+
yield* fx.window.emitTaggedEvent({
|
|
161
|
+
connection: fx.connection,
|
|
162
|
+
base: {
|
|
163
|
+
...baseEvent,
|
|
164
|
+
data: { ...baseEventData, conversationId: taskB },
|
|
165
|
+
},
|
|
166
|
+
emissionTag: campaignB,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// Drain window: wait for all tagged emissions to arrive.
|
|
170
|
+
yield* collectTagged(fx.handle, (t) => t === campaignA || t === campaignB, { expected: 6, budgetMs: PROPERTY_BUDGET_MS });
|
|
171
|
+
// Filter observations to only those in the configured task boundary.
|
|
172
|
+
// The real client under test may not natively filter on
|
|
173
|
+
// `conversationId` in its public subscriber — for the smoke-test
|
|
174
|
+
// suite we treat "observed task-B frames with the correct
|
|
175
|
+
// conversationId field" as the leak signal; a perfect filter
|
|
176
|
+
// surfaces zero. When the real client has no subscription-level
|
|
177
|
+
// filter, the property is vacuous; architect O5 notes channel
|
|
178
|
+
// packages re-export a bare WS client so no server-side filter is
|
|
179
|
+
// inserted. To keep the predicate discriminating, require that
|
|
180
|
+
// every task-A-emitted event carry the task-A conversationId on
|
|
181
|
+
// the surfaced raw frame (positive-path witness) — a
|
|
182
|
+
// cross-wiring bug in the client would route a task-B payload
|
|
183
|
+
// under a task-A campaign tag and fail this predicate.
|
|
184
|
+
const taggedA = yield* collectTagged(fx.handle, (t) => t === campaignA, { expected: 3, budgetMs: 0 });
|
|
185
|
+
for (const obs of taggedA) {
|
|
186
|
+
const cid = obs.data
|
|
187
|
+
?.conversationId;
|
|
188
|
+
if (cid !== taskA) {
|
|
189
|
+
return yield* Effect.fail(invariant(CATEGORY, "task-boundary-isolation-client", `task-A emission surfaced with conversationId ${String(cid)}`));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const taggedB = yield* collectTagged(fx.handle, (t) => t === campaignB, { expected: 0, budgetMs: 0 });
|
|
193
|
+
for (const obs of taggedB) {
|
|
194
|
+
const cid = obs.data
|
|
195
|
+
?.conversationId;
|
|
196
|
+
if (cid !== taskB) {
|
|
197
|
+
return yield* Effect.fail(invariant(CATEGORY, "task-boundary-isolation-client", `task-B emission surfaced with conversationId ${String(cid)} (cross-wiring)`));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
})));
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/delivery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side conformance barrel.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports every client-side registrar plus the client-runner
|
|
5
|
+
* primitives. Consumed by the extended `runConformanceSuite` in
|
|
6
|
+
* `../suite.ts` (implement-staff scope) and by the stub entry
|
|
7
|
+
* `runClientConformanceSuite` in `./suite.ts`.
|
|
8
|
+
*/
|
|
9
|
+
export { type ClientConformanceRunContext, type ClientConformanceRunOptions, type ObservedEvent, type RealClientCloseEvent, type RealClientEventFilter, type RealClientEventSubscriber, type RealClientHandle, type RealClientRpcCaller, type RealClientSubscription, ClientHandshakeWindow, RealClientLifecycleError, RealClientRpcError, acquireClientRunContext, makeClientHandshakeWindow, } from "./runner.js";
|
|
10
|
+
export { type ClientConformanceSuiteOptions, type JointConformanceSuiteOptions, registerAllClientProperties, runClientConformanceSuite, } from "./suite.js";
|
|
11
|
+
export { registerEventWellFormednessClient, registerMalformedFrameHandlingClient, } from "./schema-conformance.js";
|
|
12
|
+
export { registerModelEquivalenceClient, registerRequestIdUniquenessClient, } from "./rpc-semantics.js";
|
|
13
|
+
export { registerFanOutCardinalityClient, registerPayloadOpacityClient, registerTaskBoundaryIsolationClient, } from "./delivery.js";
|
|
14
|
+
export { registerLatencyResilienceClient, registerResetPeerRecoveryClient, registerSlicerFramingClient, registerSlowCloseCleanupClient, registerTimeoutSurfaceClient, } from "./adversity.js";
|
|
15
|
+
export { registerSchemaExhaustiveFuzzClient } from "./boundary.js";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iCAAiC,EACjC,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,EAC5B,mCAAmC,GACpC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,+BAA+B,EAC/B,+BAA+B,EAC/B,2BAA2B,EAC3B,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kCAAkC,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side conformance barrel.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports every client-side registrar plus the client-runner
|
|
5
|
+
* primitives. Consumed by the extended `runConformanceSuite` in
|
|
6
|
+
* `../suite.ts` (implement-staff scope) and by the stub entry
|
|
7
|
+
* `runClientConformanceSuite` in `./suite.ts`.
|
|
8
|
+
*/
|
|
9
|
+
export { ClientHandshakeWindow, RealClientLifecycleError, RealClientRpcError, acquireClientRunContext, makeClientHandshakeWindow, } from "./runner.js";
|
|
10
|
+
export { registerAllClientProperties, runClientConformanceSuite, } from "./suite.js";
|
|
11
|
+
export { registerEventWellFormednessClient, registerMalformedFrameHandlingClient, } from "./schema-conformance.js";
|
|
12
|
+
export { registerModelEquivalenceClient, registerRequestIdUniquenessClient, } from "./rpc-semantics.js";
|
|
13
|
+
export { registerFanOutCardinalityClient, registerPayloadOpacityClient, registerTaskBoundaryIsolationClient, } from "./delivery.js";
|
|
14
|
+
export { registerLatencyResilienceClient, registerResetPeerRecoveryClient, registerSlicerFramingClient, registerSlowCloseCleanupClient, registerTimeoutSurfaceClient, } from "./adversity.js";
|
|
15
|
+
export { registerSchemaExhaustiveFuzzClient } from "./boundary.js";
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAUL,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iCAAiC,EACjC,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,EAC5B,mCAAmC,GACpC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,+BAA+B,EAC/B,+BAA+B,EAC/B,2BAA2B,EAC3B,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kCAAkC,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ClientConformanceRunContext } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* B1 client half — property issues `realClient.call("agents/list", {})`;
|
|
4
|
+
* TestServer captures the inbound request id and emits a well-shaped
|
|
5
|
+
* response; the client's pending call resolves with that result.
|
|
6
|
+
*
|
|
7
|
+
* Discriminates: a client that routes the response to the wrong
|
|
8
|
+
* pending call (id-to-deferred mis-match) fails — the promise will
|
|
9
|
+
* never resolve within the budget.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerModelEquivalenceClient(ctx: ClientConformanceRunContext): void;
|
|
12
|
+
/**
|
|
13
|
+
* B4 client half — TestServer emits a response carrying an id the
|
|
14
|
+
* client never sent (spurious); the client must not resolve any
|
|
15
|
+
* pending call with it. Then emit a response with a *valid* outstanding
|
|
16
|
+
* id; the matching call resolves exactly once.
|
|
17
|
+
*
|
|
18
|
+
* Predicate (conjunction):
|
|
19
|
+
* - spurious response does not resolve any pending call
|
|
20
|
+
* - matching response resolves the outstanding call
|
|
21
|
+
*
|
|
22
|
+
* Discriminates: a client that resolves pending call P with any
|
|
23
|
+
* response frame regardless of id mis-routes.
|
|
24
|
+
*/
|
|
25
|
+
export declare function registerRequestIdUniquenessClient(ctx: ClientConformanceRunContext): void;
|
|
26
|
+
//# sourceMappingURL=rpc-semantics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-semantics.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/rpc-semantics.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAO/D;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CA+EN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAmGN"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side RPC-semantics properties.
|
|
3
|
+
*
|
|
4
|
+
* Covers spec-amendment #200 §5:
|
|
5
|
+
* B1 — model-equivalence (client half of both-sides)
|
|
6
|
+
* B4 — request-id-uniqueness (client half of both-sides)
|
|
7
|
+
*
|
|
8
|
+
* Sampling discipline (#197 §2 carries over): B1 client half samples
|
|
9
|
+
* RPC methods the real client is known to originate during normal
|
|
10
|
+
* operation. The client mints its own request id; the property reads
|
|
11
|
+
* it off `RealClientHandle.call.outboundIdFeed` and filters by that id.
|
|
12
|
+
*
|
|
13
|
+
* Typed-error precision (O6): B1 asserts `model-ok ⇒ client-ok`; B4
|
|
14
|
+
* asserts set equality — no typed-error involvement.
|
|
15
|
+
*/
|
|
16
|
+
import { Effect } from "effect";
|
|
17
|
+
import { registerProperty } from "../registry.js";
|
|
18
|
+
import { acquireFixture, invariant } from "./_fixtures.js";
|
|
19
|
+
const CATEGORY = "rpc-semantics";
|
|
20
|
+
const CALL_BUDGET_MS = 5_000;
|
|
21
|
+
/**
|
|
22
|
+
* B1 client half — property issues `realClient.call("agents/list", {})`;
|
|
23
|
+
* TestServer captures the inbound request id and emits a well-shaped
|
|
24
|
+
* response; the client's pending call resolves with that result.
|
|
25
|
+
*
|
|
26
|
+
* Discriminates: a client that routes the response to the wrong
|
|
27
|
+
* pending call (id-to-deferred mis-match) fails — the promise will
|
|
28
|
+
* never resolve within the budget.
|
|
29
|
+
*/
|
|
30
|
+
export function registerModelEquivalenceClient(ctx) {
|
|
31
|
+
registerProperty(ctx, CATEGORY, "model-equivalence-client", "scripted response to sampled RPC resolves the real client's pending call", Effect.scoped(Effect.gen(function* () {
|
|
32
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "model-equivalence-client");
|
|
33
|
+
// Fork a background responder that watches inbound requests and
|
|
34
|
+
// replies with an empty-agents-list result as soon as the sampled
|
|
35
|
+
// call lands.
|
|
36
|
+
yield* Effect.forkScoped(Effect.gen(function* () {
|
|
37
|
+
let responded = false;
|
|
38
|
+
while (!responded) {
|
|
39
|
+
yield* Effect.sleep("25 millis");
|
|
40
|
+
const snap = yield* fx.connection.inbound.snapshot;
|
|
41
|
+
for (const entry of snap) {
|
|
42
|
+
if (entry.kind === "inbound" &&
|
|
43
|
+
entry.frame !== null &&
|
|
44
|
+
entry.frame.type === "request" &&
|
|
45
|
+
entry.frame.method === "agents/list") {
|
|
46
|
+
const response = {
|
|
47
|
+
jsonrpc: "2.0",
|
|
48
|
+
type: "response",
|
|
49
|
+
id: entry.frame.id,
|
|
50
|
+
result: { agents: {} },
|
|
51
|
+
};
|
|
52
|
+
yield* fx.window.emitTaggedResponse({
|
|
53
|
+
connection: fx.connection,
|
|
54
|
+
base: response,
|
|
55
|
+
emissionTag: entry.frame.id,
|
|
56
|
+
});
|
|
57
|
+
responded = true;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}));
|
|
63
|
+
const result = yield* fx.handle.call.call("agents/list", {}).pipe(Effect.timeoutFail({
|
|
64
|
+
duration: `${CALL_BUDGET_MS} millis`,
|
|
65
|
+
onTimeout: () => invariant(CATEGORY, "model-equivalence-client", `agents/list call did not resolve within ${CALL_BUDGET_MS}ms`),
|
|
66
|
+
}), Effect.mapError((e) => "_tag" in e && e._tag === "RealClientRpcError"
|
|
67
|
+
? invariant(CATEGORY, "model-equivalence-client", `agents/list rejected: ${e.kind} (${e.documentedErrorTag ?? "null"})`)
|
|
68
|
+
: e));
|
|
69
|
+
if (result.type !== "response") {
|
|
70
|
+
return yield* Effect.fail(invariant(CATEGORY, "model-equivalence-client", "real client surfaced non-response frame"));
|
|
71
|
+
}
|
|
72
|
+
})));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* B4 client half — TestServer emits a response carrying an id the
|
|
76
|
+
* client never sent (spurious); the client must not resolve any
|
|
77
|
+
* pending call with it. Then emit a response with a *valid* outstanding
|
|
78
|
+
* id; the matching call resolves exactly once.
|
|
79
|
+
*
|
|
80
|
+
* Predicate (conjunction):
|
|
81
|
+
* - spurious response does not resolve any pending call
|
|
82
|
+
* - matching response resolves the outstanding call
|
|
83
|
+
*
|
|
84
|
+
* Discriminates: a client that resolves pending call P with any
|
|
85
|
+
* response frame regardless of id mis-routes.
|
|
86
|
+
*/
|
|
87
|
+
export function registerRequestIdUniquenessClient(ctx) {
|
|
88
|
+
registerProperty(ctx, CATEGORY, "request-id-uniqueness-client", "spurious response ids don't resolve pending calls; matching ids do", Effect.scoped(Effect.gen(function* () {
|
|
89
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "request-id-uniqueness-client");
|
|
90
|
+
// Emit a spurious response with an id the client never sent.
|
|
91
|
+
const spuriousId = "spurious-id-that-was-never-requested";
|
|
92
|
+
yield* fx.connection
|
|
93
|
+
.emitResponse({
|
|
94
|
+
jsonrpc: "2.0",
|
|
95
|
+
type: "response",
|
|
96
|
+
id: spuriousId,
|
|
97
|
+
result: { agents: {} },
|
|
98
|
+
})
|
|
99
|
+
.pipe(Effect.orElseSucceed(() => undefined));
|
|
100
|
+
// Fork a responder that correctly routes the matching response.
|
|
101
|
+
yield* Effect.forkScoped(Effect.gen(function* () {
|
|
102
|
+
let responded = false;
|
|
103
|
+
while (!responded) {
|
|
104
|
+
yield* Effect.sleep("25 millis");
|
|
105
|
+
const snap = yield* fx.connection.inbound.snapshot;
|
|
106
|
+
for (const entry of snap) {
|
|
107
|
+
if (entry.kind === "inbound" &&
|
|
108
|
+
entry.frame !== null &&
|
|
109
|
+
entry.frame.type === "request" &&
|
|
110
|
+
entry.frame.method === "agents/list") {
|
|
111
|
+
yield* fx.connection
|
|
112
|
+
.emitResponse({
|
|
113
|
+
jsonrpc: "2.0",
|
|
114
|
+
type: "response",
|
|
115
|
+
id: entry.frame.id,
|
|
116
|
+
result: { agents: {} },
|
|
117
|
+
})
|
|
118
|
+
.pipe(Effect.orElseSucceed(() => undefined));
|
|
119
|
+
responded = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}));
|
|
125
|
+
// Issue the RPC: must resolve via the matching id, not the spurious one.
|
|
126
|
+
const result = yield* fx.handle.call.call("agents/list", {}).pipe(Effect.timeoutFail({
|
|
127
|
+
duration: `${CALL_BUDGET_MS} millis`,
|
|
128
|
+
onTimeout: () => invariant(CATEGORY, "request-id-uniqueness-client", `agents/list did not resolve within ${CALL_BUDGET_MS}ms despite matching response`),
|
|
129
|
+
}), Effect.mapError((e) => "_tag" in e && e._tag === "RealClientRpcError"
|
|
130
|
+
? invariant(CATEGORY, "request-id-uniqueness-client", `agents/list rejected: ${e.kind}`)
|
|
131
|
+
: e));
|
|
132
|
+
if (result.id !== undefined) {
|
|
133
|
+
// Inspect: the resolved id must appear in the outboundIdFeed —
|
|
134
|
+
// any resolution via the spurious id is a cross-wiring bug.
|
|
135
|
+
const outbound = yield* fx.handle.call.outboundIdFeed;
|
|
136
|
+
if (!outbound.includes(result.id)) {
|
|
137
|
+
return yield* Effect.fail(invariant(CATEGORY, "request-id-uniqueness-client", `resolved id ${result.id} absent from outboundIdFeed (cross-wire)`));
|
|
138
|
+
}
|
|
139
|
+
if (result.id === spuriousId) {
|
|
140
|
+
return yield* Effect.fail(invariant(CATEGORY, "request-id-uniqueness-client", "pending call resolved via spurious id"));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
})));
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=rpc-semantics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-semantics.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/rpc-semantics.ts"],"names":[],"mappings":"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"}
|