@noy-db/test-mesh-conformance 0.7.0-pre.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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +123 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vLannaAi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @noy-db/test-mesh-conformance
|
|
2
|
+
|
|
3
|
+
Contract tests for the `by-*` family port. Every `NoydbMesh` implementation — `by-peer`, `by-tabs`, the reserved `by-server`/`by-room`, or your own — runs the same suite.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { runMeshConformanceTests } from '@noy-db/test-mesh-conformance'
|
|
7
|
+
import { pairInMemory, channelMesh } from '@noy-db/by-peer'
|
|
8
|
+
|
|
9
|
+
runMeshConformanceTests('my-transport', {
|
|
10
|
+
pair: () => {
|
|
11
|
+
const [x, y] = pairInMemory()
|
|
12
|
+
return [channelMesh(x), channelMesh(y)] as const
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## The fixture is a PAIR, not an instance
|
|
18
|
+
|
|
19
|
+
That is the whole point. A mesh's defining property is that **two participants see each other**; a suite built around a single instance passes on an implementation that shares nothing — which is exactly the implementation that lets a schema cutover proceed while another writer still believes the vault is normal.
|
|
20
|
+
|
|
21
|
+
Mutation-checked against `by-peer`: disabling the broadcast fails **5 of 9**; disabling the staleness filter fails exactly **1**.
|
|
22
|
+
|
|
23
|
+
## What it checks
|
|
24
|
+
|
|
25
|
+
A usable default fence before anything is written; `setFence` → `readFence` on the same participant **and across the pair**; `observeFence` firing on the other side and stopping after unsubscribe; `reportPresence` reachable from the other side; `reachableWriters` excluding entries older than `staleMs`; `observePresence` firing; and vault isolation.
|
|
26
|
+
|
|
27
|
+
Two of those carry most of the weight. **Cross-participant visibility** is the property a single-instance test cannot see. And **staleness filtering** is what stops a dead writer hanging a drain barrier until timeout — the failure the filter exists to prevent.
|
|
28
|
+
|
|
29
|
+
Every observation goes through a polling `waitFor`, because delivery is asynchronous on every transport: push-based ones deliver on a microtask, `StoreMesh` on its next poll. An implementation that only ever delivers on the next poll is conformant — slow, but conformant.
|
|
30
|
+
|
|
31
|
+
## ⚠️ `StoreMesh` cannot run this suite, and neither side can fix it alone
|
|
32
|
+
|
|
33
|
+
`StoreMesh` is hub's store-polling fallback — the implementation most consumers actually run when no `by-*` transport is injected. It is **not covered here**, and the reason is structural rather than an omission:
|
|
34
|
+
|
|
35
|
+
- **the kit cannot import it** — it is internal, deliberately not exported (`port/by/index.ts` says so outright, its only consumer being `with-shape/schema-update`);
|
|
36
|
+
- **hub cannot import the kit** — the kit peer-depends on hub, so that is a build cycle turbo refuses.
|
|
37
|
+
|
|
38
|
+
The sealer kit does not hit this only because `MemorySealer` happens to be exported. **Generalise before the next vertical: a port's in-hub default implementation is coverable by its published kit only if it is on the published surface.** Closing it for `by-*` means deciding whether an internal default belongs on the public surface — a real decision, not a test change.
|
|
39
|
+
|
|
40
|
+
Until then `StoreMesh`'s conformance is asserted only indirectly, by the schema-fence tests that happen to exercise it.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NoydbMesh } from '@noy-db/hub/by';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parameterized conformance suite for the `by-*` family port.
|
|
5
|
+
*
|
|
6
|
+
* `NoydbMesh` is what a session-share transport implements so a schema-fence
|
|
7
|
+
* cutover can drain: who else is live, and what generation are they on.
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ THE FIXTURE IS A PAIR, NOT AN INSTANCE — and that is the whole point.
|
|
10
|
+
* A mesh's defining property is that two participants see each other; a suite
|
|
11
|
+
* built around one instance would pass on an implementation that shares
|
|
12
|
+
* nothing, which is precisely the implementation that breaks a cutover. Same
|
|
13
|
+
* reasoning as the sealer kit requiring a second, differently-identified
|
|
14
|
+
* provider.
|
|
15
|
+
*
|
|
16
|
+
* `channelMesh` (by-peer, by-tabs) is push-based and `StoreMesh` polls,
|
|
17
|
+
* so every observation here is written against `waitFor` rather than assuming
|
|
18
|
+
* synchronous delivery. An implementation that only ever delivers on the next
|
|
19
|
+
* poll is conformant — slow, but conformant.
|
|
20
|
+
*/
|
|
21
|
+
declare function runMeshConformanceTests(name: string, fixture: {
|
|
22
|
+
/** Two meshes that share coordination state. */
|
|
23
|
+
readonly pair: () => Promise<readonly [NoydbMesh, NoydbMesh]> | readonly [NoydbMesh, NoydbMesh];
|
|
24
|
+
/** Tear down a pair, if the transport needs it. */
|
|
25
|
+
readonly cleanup?: (pair: readonly [NoydbMesh, NoydbMesh]) => void | Promise<void>;
|
|
26
|
+
}): void;
|
|
27
|
+
|
|
28
|
+
export { runMeshConformanceTests };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
async function waitFor(fn, ms = 3e3) {
|
|
4
|
+
const deadline = Date.now() + ms;
|
|
5
|
+
for (; ; ) {
|
|
6
|
+
const v = await fn();
|
|
7
|
+
if (v) return v;
|
|
8
|
+
if (Date.now() > deadline) throw new Error("waitFor: timed out");
|
|
9
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
var writer = (id, lastSeen) => ({
|
|
13
|
+
writerId: id,
|
|
14
|
+
sessionId: `${id}-s`,
|
|
15
|
+
lastSeen,
|
|
16
|
+
quiescedAtVersion: null
|
|
17
|
+
});
|
|
18
|
+
function runMeshConformanceTests(name, fixture) {
|
|
19
|
+
const VAULT = "conformance-vault";
|
|
20
|
+
describe(`NoydbMesh conformance: ${name}`, () => {
|
|
21
|
+
const withPair = async (fn) => {
|
|
22
|
+
const pair = await fixture.pair();
|
|
23
|
+
try {
|
|
24
|
+
await fn(pair[0], pair[1]);
|
|
25
|
+
} finally {
|
|
26
|
+
await fixture.cleanup?.(pair);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
it("readFence returns a usable default before anything is written", async () => {
|
|
30
|
+
await withPair(async (a) => {
|
|
31
|
+
const f = await a.readFence(VAULT);
|
|
32
|
+
expect(f).toBeDefined();
|
|
33
|
+
expect(typeof f.currentSchemaVersion).toBe("number");
|
|
34
|
+
expect(["normal", "draining", "migrating", "complete"]).toContain(f.fenceState);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
it("setFence \u2192 readFence round-trips on the SAME participant", async () => {
|
|
38
|
+
await withPair(async (a) => {
|
|
39
|
+
await a.setFence(VAULT, { currentSchemaVersion: 7, fenceState: "draining" });
|
|
40
|
+
expect(await a.readFence(VAULT)).toMatchObject({ currentSchemaVersion: 7, fenceState: "draining" });
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
it("setFence on one participant is VISIBLE to the other", async () => {
|
|
44
|
+
await withPair(async (a, b) => {
|
|
45
|
+
await a.setFence(VAULT, { currentSchemaVersion: 9, fenceState: "migrating" });
|
|
46
|
+
const f = await waitFor(async () => {
|
|
47
|
+
const cur = await b.readFence(VAULT);
|
|
48
|
+
return cur.fenceState === "migrating" ? cur : null;
|
|
49
|
+
});
|
|
50
|
+
expect(f.currentSchemaVersion).toBe(9);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("observeFence fires on the other participant when the fence moves", async () => {
|
|
54
|
+
await withPair(async (a, b) => {
|
|
55
|
+
const seen = [];
|
|
56
|
+
const un = b.observeFence(VAULT, (f) => {
|
|
57
|
+
seen.push(f.fenceState);
|
|
58
|
+
});
|
|
59
|
+
await a.setFence(VAULT, { currentSchemaVersion: 3, fenceState: "draining" });
|
|
60
|
+
await waitFor(() => seen.includes("draining"));
|
|
61
|
+
un();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it("observeFence stops delivering after unsubscribe", async () => {
|
|
65
|
+
await withPair(async (a, b) => {
|
|
66
|
+
const seen = [];
|
|
67
|
+
const un = b.observeFence(VAULT, (f) => {
|
|
68
|
+
seen.push(f.fenceState);
|
|
69
|
+
});
|
|
70
|
+
await a.setFence(VAULT, { currentSchemaVersion: 1, fenceState: "draining" });
|
|
71
|
+
await waitFor(() => seen.includes("draining"));
|
|
72
|
+
un();
|
|
73
|
+
const after = seen.length;
|
|
74
|
+
await a.setFence(VAULT, { currentSchemaVersion: 2, fenceState: "complete" });
|
|
75
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
76
|
+
expect(seen.length, "callback fired after unsubscribe").toBe(after);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
it("reportPresence on one participant is REACHABLE from the other", async () => {
|
|
80
|
+
await withPair(async (a, b) => {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
await a.reportPresence(VAULT, writer("w-a", now));
|
|
83
|
+
const found = await waitFor(async () => {
|
|
84
|
+
const ws = await b.reachableWriters(VAULT, { staleMs: 6e4, now: Date.now() });
|
|
85
|
+
return ws.some((w) => w.writerId === "w-a") ? ws : null;
|
|
86
|
+
}, 3e3);
|
|
87
|
+
expect(found.map((w) => w.writerId)).toContain("w-a");
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it("reachableWriters EXCLUDES a writer older than staleMs", async () => {
|
|
91
|
+
await withPair(async (a, b) => {
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
await a.reportPresence(VAULT, writer("w-stale", now - 12e4));
|
|
94
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
95
|
+
const ws = await b.reachableWriters(VAULT, { staleMs: 3e4, now });
|
|
96
|
+
expect(ws.map((w) => w.writerId)).not.toContain("w-stale");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
it("observePresence fires on the other participant", async () => {
|
|
100
|
+
await withPair(async (a, b) => {
|
|
101
|
+
let seen = [];
|
|
102
|
+
const un = b.observePresence(VAULT, (ws) => {
|
|
103
|
+
seen = ws;
|
|
104
|
+
});
|
|
105
|
+
await a.reportPresence(VAULT, writer("w-obs", Date.now()));
|
|
106
|
+
await waitFor(() => seen.some((w) => w.writerId === "w-obs"));
|
|
107
|
+
un();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it("keeps vaults isolated \u2014 presence in one is not reachable from another", async () => {
|
|
111
|
+
await withPair(async (a, b) => {
|
|
112
|
+
await a.reportPresence(VAULT, writer("w-iso", Date.now()));
|
|
113
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
114
|
+
const other = await b.reachableWriters("a-different-vault", { staleMs: 6e4, now: Date.now() });
|
|
115
|
+
expect(other.map((w) => w.writerId)).not.toContain("w-iso");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
runMeshConformanceTests
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\nimport type { NoydbMesh, WriterPresence } from '@noy-db/hub/by'\n\n/**\n * Poll until `fn` yields something truthy, or the budget runs out. Delivery is\n * asynchronous on every transport — push-based ones deliver on a microtask,\n * `StoreMesh` on its next poll — so no observation here may assume it has\n * already happened.\n *\n * ⚠️ It MUST await the predicate. An earlier version did not, so an `async`\n * predicate returned a Promise, a Promise is always truthy, and every test\n * using one passed without observing anything at all.\n */\nasync function waitFor<T>(\n fn: () => Promise<T | null | undefined | false> | T | null | undefined | false,\n ms = 3000,\n): Promise<T> {\n const deadline = Date.now() + ms\n for (;;) {\n const v = await fn()\n if (v) return v\n if (Date.now() > deadline) throw new Error('waitFor: timed out')\n await new Promise((r) => setTimeout(r, 10))\n }\n}\n\nconst writer = (id: string, lastSeen: number): WriterPresence => ({\n writerId: id, sessionId: `${id}-s`, lastSeen, quiescedAtVersion: null,\n})\n\n/**\n * Parameterized conformance suite for the `by-*` family port.\n *\n * `NoydbMesh` is what a session-share transport implements so a schema-fence\n * cutover can drain: who else is live, and what generation are they on.\n *\n * ⚠️ THE FIXTURE IS A PAIR, NOT AN INSTANCE — and that is the whole point.\n * A mesh's defining property is that two participants see each other; a suite\n * built around one instance would pass on an implementation that shares\n * nothing, which is precisely the implementation that breaks a cutover. Same\n * reasoning as the sealer kit requiring a second, differently-identified\n * provider.\n *\n * `channelMesh` (by-peer, by-tabs) is push-based and `StoreMesh` polls,\n * so every observation here is written against `waitFor` rather than assuming\n * synchronous delivery. An implementation that only ever delivers on the next\n * poll is conformant — slow, but conformant.\n */\nexport function runMeshConformanceTests(\n name: string,\n fixture: {\n /** Two meshes that share coordination state. */\n readonly pair: () => Promise<readonly [NoydbMesh, NoydbMesh]> | readonly [NoydbMesh, NoydbMesh]\n /** Tear down a pair, if the transport needs it. */\n readonly cleanup?: (pair: readonly [NoydbMesh, NoydbMesh]) => void | Promise<void>\n },\n): void {\n const VAULT = 'conformance-vault'\n\n describe(`NoydbMesh conformance: ${name}`, () => {\n const withPair = async (fn: (a: NoydbMesh, b: NoydbMesh) => Promise<void>) => {\n const pair = await fixture.pair()\n try { await fn(pair[0], pair[1]) } finally { await fixture.cleanup?.(pair) }\n }\n\n it('readFence returns a usable default before anything is written', async () => {\n // A cutover asks the fence before anyone sets one. Throwing here, or\n // returning undefined, breaks the first migration on a fresh vault.\n await withPair(async (a) => {\n const f = await a.readFence(VAULT)\n expect(f).toBeDefined()\n expect(typeof f.currentSchemaVersion).toBe('number')\n expect(['normal', 'draining', 'migrating', 'complete']).toContain(f.fenceState)\n })\n })\n\n it('setFence → readFence round-trips on the SAME participant', async () => {\n await withPair(async (a) => {\n await a.setFence(VAULT, { currentSchemaVersion: 7, fenceState: 'draining' })\n expect(await a.readFence(VAULT)).toMatchObject({ currentSchemaVersion: 7, fenceState: 'draining' })\n })\n })\n\n it('setFence on one participant is VISIBLE to the other', async () => {\n // The load-bearing one. A mesh that keeps fence state per-instance\n // passes every single-instance test and still lets a migration proceed\n // while another writer believes the vault is normal.\n await withPair(async (a, b) => {\n await a.setFence(VAULT, { currentSchemaVersion: 9, fenceState: 'migrating' })\n const f = await waitFor(async () => {\n const cur = await b.readFence(VAULT)\n return cur.fenceState === 'migrating' ? cur : null\n })\n expect(f.currentSchemaVersion).toBe(9)\n })\n })\n\n it('observeFence fires on the other participant when the fence moves', async () => {\n await withPair(async (a, b) => {\n const seen: string[] = []\n const un = b.observeFence(VAULT, (f) => { seen.push(f.fenceState) })\n await a.setFence(VAULT, { currentSchemaVersion: 3, fenceState: 'draining' })\n await waitFor(() => seen.includes('draining'))\n un()\n })\n })\n\n it('observeFence stops delivering after unsubscribe', async () => {\n await withPair(async (a, b) => {\n const seen: string[] = []\n const un = b.observeFence(VAULT, (f) => { seen.push(f.fenceState) })\n await a.setFence(VAULT, { currentSchemaVersion: 1, fenceState: 'draining' })\n await waitFor(() => seen.includes('draining'))\n un()\n const after = seen.length\n await a.setFence(VAULT, { currentSchemaVersion: 2, fenceState: 'complete' })\n await new Promise((r) => setTimeout(r, 200))\n expect(seen.length, 'callback fired after unsubscribe').toBe(after)\n })\n })\n\n it('reportPresence on one participant is REACHABLE from the other', async () => {\n await withPair(async (a, b) => {\n const now = Date.now()\n await a.reportPresence(VAULT, writer('w-a', now))\n const found = await waitFor(async () => {\n const ws = await b.reachableWriters(VAULT, { staleMs: 60_000, now: Date.now() })\n return ws.some((w) => w.writerId === 'w-a') ? ws : null\n }, 3000)\n expect(found.map((w) => w.writerId)).toContain('w-a')\n })\n })\n\n it('reachableWriters EXCLUDES a writer older than staleMs', async () => {\n // The drain barrier waits for reachable writers to quiesce. Counting a\n // dead writer as reachable hangs the cutover until the timeout — the\n // failure this filter exists to prevent.\n await withPair(async (a, b) => {\n const now = Date.now()\n await a.reportPresence(VAULT, writer('w-stale', now - 120_000))\n await new Promise((r) => setTimeout(r, 100))\n const ws = await b.reachableWriters(VAULT, { staleMs: 30_000, now })\n expect(ws.map((w) => w.writerId)).not.toContain('w-stale')\n })\n })\n\n it('observePresence fires on the other participant', async () => {\n await withPair(async (a, b) => {\n let seen: readonly WriterPresence[] = []\n const un = b.observePresence(VAULT, (ws) => { seen = ws })\n await a.reportPresence(VAULT, writer('w-obs', Date.now()))\n await waitFor(() => seen.some((w) => w.writerId === 'w-obs'))\n un()\n })\n })\n\n it('keeps vaults isolated — presence in one is not reachable from another', async () => {\n await withPair(async (a, b) => {\n await a.reportPresence(VAULT, writer('w-iso', Date.now()))\n await new Promise((r) => setTimeout(r, 150))\n const other = await b.reachableWriters('a-different-vault', { staleMs: 60_000, now: Date.now() })\n expect(other.map((w) => w.writerId)).not.toContain('w-iso')\n })\n })\n })\n}\n"],"mappings":";AAAA,SAAS,UAAU,IAAI,cAAc;AAarC,eAAe,QACb,IACA,KAAK,KACO;AACZ,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,aAAS;AACP,UAAM,IAAI,MAAM,GAAG;AACnB,QAAI,EAAG,QAAO;AACd,QAAI,KAAK,IAAI,IAAI,SAAU,OAAM,IAAI,MAAM,oBAAoB;AAC/D,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAAA,EAC5C;AACF;AAEA,IAAM,SAAS,CAAC,IAAY,cAAsC;AAAA,EAChE,UAAU;AAAA,EAAI,WAAW,GAAG,EAAE;AAAA,EAAM;AAAA,EAAU,mBAAmB;AACnE;AAoBO,SAAS,wBACd,MACA,SAMM;AACN,QAAM,QAAQ;AAEd,WAAS,0BAA0B,IAAI,IAAI,MAAM;AAC/C,UAAM,WAAW,OAAO,OAAsD;AAC5E,YAAM,OAAO,MAAM,QAAQ,KAAK;AAChC,UAAI;AAAE,cAAM,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AAAA,MAAE,UAAE;AAAU,cAAM,QAAQ,UAAU,IAAI;AAAA,MAAE;AAAA,IAC7E;AAEA,OAAG,iEAAiE,YAAY;AAG9E,YAAM,SAAS,OAAO,MAAM;AAC1B,cAAM,IAAI,MAAM,EAAE,UAAU,KAAK;AACjC,eAAO,CAAC,EAAE,YAAY;AACtB,eAAO,OAAO,EAAE,oBAAoB,EAAE,KAAK,QAAQ;AACnD,eAAO,CAAC,UAAU,YAAY,aAAa,UAAU,CAAC,EAAE,UAAU,EAAE,UAAU;AAAA,MAChF,CAAC;AAAA,IACH,CAAC;AAED,OAAG,iEAA4D,YAAY;AACzE,YAAM,SAAS,OAAO,MAAM;AAC1B,cAAM,EAAE,SAAS,OAAO,EAAE,sBAAsB,GAAG,YAAY,WAAW,CAAC;AAC3E,eAAO,MAAM,EAAE,UAAU,KAAK,CAAC,EAAE,cAAc,EAAE,sBAAsB,GAAG,YAAY,WAAW,CAAC;AAAA,MACpG,CAAC;AAAA,IACH,CAAC;AAED,OAAG,uDAAuD,YAAY;AAIpE,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,cAAM,EAAE,SAAS,OAAO,EAAE,sBAAsB,GAAG,YAAY,YAAY,CAAC;AAC5E,cAAM,IAAI,MAAM,QAAQ,YAAY;AAClC,gBAAM,MAAM,MAAM,EAAE,UAAU,KAAK;AACnC,iBAAO,IAAI,eAAe,cAAc,MAAM;AAAA,QAChD,CAAC;AACD,eAAO,EAAE,oBAAoB,EAAE,KAAK,CAAC;AAAA,MACvC,CAAC;AAAA,IACH,CAAC;AAED,OAAG,oEAAoE,YAAY;AACjF,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,cAAM,OAAiB,CAAC;AACxB,cAAM,KAAK,EAAE,aAAa,OAAO,CAAC,MAAM;AAAE,eAAK,KAAK,EAAE,UAAU;AAAA,QAAE,CAAC;AACnE,cAAM,EAAE,SAAS,OAAO,EAAE,sBAAsB,GAAG,YAAY,WAAW,CAAC;AAC3E,cAAM,QAAQ,MAAM,KAAK,SAAS,UAAU,CAAC;AAC7C,WAAG;AAAA,MACL,CAAC;AAAA,IACH,CAAC;AAED,OAAG,mDAAmD,YAAY;AAChE,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,cAAM,OAAiB,CAAC;AACxB,cAAM,KAAK,EAAE,aAAa,OAAO,CAAC,MAAM;AAAE,eAAK,KAAK,EAAE,UAAU;AAAA,QAAE,CAAC;AACnE,cAAM,EAAE,SAAS,OAAO,EAAE,sBAAsB,GAAG,YAAY,WAAW,CAAC;AAC3E,cAAM,QAAQ,MAAM,KAAK,SAAS,UAAU,CAAC;AAC7C,WAAG;AACH,cAAM,QAAQ,KAAK;AACnB,cAAM,EAAE,SAAS,OAAO,EAAE,sBAAsB,GAAG,YAAY,WAAW,CAAC;AAC3E,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC3C,eAAO,KAAK,QAAQ,kCAAkC,EAAE,KAAK,KAAK;AAAA,MACpE,CAAC;AAAA,IACH,CAAC;AAED,OAAG,iEAAiE,YAAY;AAC9E,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,cAAM,MAAM,KAAK,IAAI;AACrB,cAAM,EAAE,eAAe,OAAO,OAAO,OAAO,GAAG,CAAC;AAChD,cAAM,QAAQ,MAAM,QAAQ,YAAY;AACtC,gBAAM,KAAK,MAAM,EAAE,iBAAiB,OAAO,EAAE,SAAS,KAAQ,KAAK,KAAK,IAAI,EAAE,CAAC;AAC/E,iBAAO,GAAG,KAAK,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,KAAK;AAAA,QACrD,GAAG,GAAI;AACP,eAAO,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,UAAU,KAAK;AAAA,MACtD,CAAC;AAAA,IACH,CAAC;AAED,OAAG,yDAAyD,YAAY;AAItE,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,cAAM,MAAM,KAAK,IAAI;AACrB,cAAM,EAAE,eAAe,OAAO,OAAO,WAAW,MAAM,IAAO,CAAC;AAC9D,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC3C,cAAM,KAAK,MAAM,EAAE,iBAAiB,OAAO,EAAE,SAAS,KAAQ,IAAI,CAAC;AACnE,eAAO,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,UAAU,SAAS;AAAA,MAC3D,CAAC;AAAA,IACH,CAAC;AAED,OAAG,kDAAkD,YAAY;AAC/D,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,YAAI,OAAkC,CAAC;AACvC,cAAM,KAAK,EAAE,gBAAgB,OAAO,CAAC,OAAO;AAAE,iBAAO;AAAA,QAAG,CAAC;AACzD,cAAM,EAAE,eAAe,OAAO,OAAO,SAAS,KAAK,IAAI,CAAC,CAAC;AACzD,cAAM,QAAQ,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,aAAa,OAAO,CAAC;AAC5D,WAAG;AAAA,MACL,CAAC;AAAA,IACH,CAAC;AAED,OAAG,8EAAyE,YAAY;AACtF,YAAM,SAAS,OAAO,GAAG,MAAM;AAC7B,cAAM,EAAE,eAAe,OAAO,OAAO,SAAS,KAAK,IAAI,CAAC,CAAC;AACzD,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC3C,cAAM,QAAQ,MAAM,EAAE,iBAAiB,qBAAqB,EAAE,SAAS,KAAQ,KAAK,KAAK,IAAI,EAAE,CAAC;AAChG,eAAO,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,UAAU,OAAO;AAAA,MAC5D,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noy-db/test-mesh-conformance",
|
|
3
|
+
"version": "0.7.0-pre.0",
|
|
4
|
+
"description": "Parameterized contract tests for noy-db by-* transports — the conformance suite every NoydbMesh implementation must pass",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "vLannaAi <vicio@lanna.ai>",
|
|
7
|
+
"homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/test-mesh-conformance#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/vLannaAi/noy-db.git",
|
|
11
|
+
"directory": "packages/test-mesh-conformance"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/vLannaAi/noy-db/issues"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=22.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"vitest": "^3.0.0",
|
|
36
|
+
"@noy-db/hub": "0.7.0-pre.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"vitest": "^3.0.0",
|
|
40
|
+
"@noy-db/hub": "0.7.0-pre.0"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"noy-db",
|
|
44
|
+
"conformance",
|
|
45
|
+
"coordination",
|
|
46
|
+
"transport",
|
|
47
|
+
"testing"
|
|
48
|
+
],
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"test": "vitest run --passWithNoTests",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
|
+
}
|
|
57
|
+
}
|