@ggui-ai/protocol-conformance 0.1.0-rc.1

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.
Files changed (107) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +89 -0
  3. package/dist/cli.d.ts +15 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +193 -0
  6. package/dist/conformance-host.d.ts +155 -0
  7. package/dist/conformance-host.d.ts.map +1 -0
  8. package/dist/conformance-host.js +45 -0
  9. package/dist/fixtures/bootstrap-protocol/bootstrap-bundle-fetch-failed.json +26 -0
  10. package/dist/fixtures/bootstrap-protocol/bootstrap-meta-missing.json +32 -0
  11. package/dist/fixtures/bootstrap-protocol/bootstrap-success.json +17 -0
  12. package/dist/fixtures/bootstrap-protocol/index.d.ts +5 -0
  13. package/dist/fixtures/bootstrap-protocol/index.d.ts.map +1 -0
  14. package/dist/fixtures/bootstrap-protocol/index.js +24 -0
  15. package/dist/fixtures/canvas-mode-wire-shapes/canvas-bootstrap-mutual-exclusion.json +25 -0
  16. package/dist/fixtures/canvas-mode-wire-shapes/canvas-lifecycle-channel-emits-handshake-started.json +30 -0
  17. package/dist/fixtures/canvas-mode-wire-shapes/canvas-navigated-updates-active-stack-item.json +30 -0
  18. package/dist/fixtures/canvas-mode-wire-shapes/host-context-observed-persists.json +22 -0
  19. package/dist/fixtures/canvas-mode-wire-shapes/index.d.ts +5 -0
  20. package/dist/fixtures/canvas-mode-wire-shapes/index.d.ts.map +1 -0
  21. package/dist/fixtures/canvas-mode-wire-shapes/index.js +47 -0
  22. package/dist/fixtures/index.d.ts +78 -0
  23. package/dist/fixtures/index.d.ts.map +1 -0
  24. package/dist/fixtures/index.js +87 -0
  25. package/dist/fixtures/observability-events/index.d.ts +5 -0
  26. package/dist/fixtures/observability-events/index.d.ts.map +1 -0
  27. package/dist/fixtures/observability-events/index.js +22 -0
  28. package/dist/fixtures/observability-events/observability-contract-error-emitted.json +29 -0
  29. package/dist/fixtures/observability-events/observability-wired-tool-invoked.json +28 -0
  30. package/dist/fixtures/refresh-semantics/index.d.ts +5 -0
  31. package/dist/fixtures/refresh-semantics/index.d.ts.map +1 -0
  32. package/dist/fixtures/refresh-semantics/index.js +19 -0
  33. package/dist/fixtures/refresh-semantics/stream-refresh-success.json +40 -0
  34. package/dist/fixtures/reserved-channel-authority/index.d.ts +4 -0
  35. package/dist/fixtures/reserved-channel-authority/index.d.ts.map +1 -0
  36. package/dist/fixtures/reserved-channel-authority/index.js +21 -0
  37. package/dist/fixtures/reserved-channel-authority/props-update-roundtrip.json +34 -0
  38. package/dist/fixtures/reserved-channel-authority/stream-schema-violation.json +45 -0
  39. package/dist/fixtures/schema-version-handshake/index.d.ts +4 -0
  40. package/dist/fixtures/schema-version-handshake/index.d.ts.map +1 -0
  41. package/dist/fixtures/schema-version-handshake/index.js +20 -0
  42. package/dist/fixtures/schema-version-handshake/version-match.json +16 -0
  43. package/dist/fixtures/schema-version-handshake/version-mismatch.json +26 -0
  44. package/dist/fixtures/wired-action-dispatch/index.d.ts +5 -0
  45. package/dist/fixtures/wired-action-dispatch/index.d.ts.map +1 -0
  46. package/dist/fixtures/wired-action-dispatch/index.js +29 -0
  47. package/dist/fixtures/wired-action-dispatch/wired-action-success.json +36 -0
  48. package/dist/fixtures/wired-action-dispatch/wired-action-tool-not-found.json +31 -0
  49. package/dist/fixtures/wired-action-dispatch/wired-action-tool-threw.json +32 -0
  50. package/dist/fixtures/wired-action-dispatch/wired-action-tool-timeout.json +32 -0
  51. package/dist/index.d.ts +28 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +40 -0
  54. package/dist/loader.d.ts +19 -0
  55. package/dist/loader.d.ts.map +1 -0
  56. package/dist/loader.js +46 -0
  57. package/dist/match-behavior.d.ts +121 -0
  58. package/dist/match-behavior.d.ts.map +1 -0
  59. package/dist/match-behavior.js +385 -0
  60. package/dist/registration-conformance/cases/gate-export-not-registered.json +19 -0
  61. package/dist/registration-conformance/cases/gate-no-gadgets-accept.json +13 -0
  62. package/dist/registration-conformance/cases/gate-package-mismatch.json +19 -0
  63. package/dist/registration-conformance/cases/gate-priority-not-registered-outranks-mismatch.json +20 -0
  64. package/dist/registration-conformance/cases/gate-registered-resolves-accept.json +25 -0
  65. package/dist/registration-conformance/cases/gate-stdlib-hook-accept.json +27 -0
  66. package/dist/registration-conformance/cases/gate-typo-classifies-as-not-registered.json +27 -0
  67. package/dist/registration-conformance/index.d.ts +114 -0
  68. package/dist/registration-conformance/index.d.ts.map +1 -0
  69. package/dist/registration-conformance/index.js +63 -0
  70. package/dist/reporter.d.ts +32 -0
  71. package/dist/reporter.d.ts.map +1 -0
  72. package/dist/reporter.js +151 -0
  73. package/dist/resolution-conformance/cases/resolve-bundle-host-computed.json +13 -0
  74. package/dist/resolution-conformance/cases/resolve-default-host.json +12 -0
  75. package/dist/resolution-conformance/cases/resolve-explicit-bundle-and-style-url.json +14 -0
  76. package/dist/resolution-conformance/cases/resolve-explicit-bundle-url.json +12 -0
  77. package/dist/resolution-conformance/cases/resolve-loopback-host-http-scheme.json +13 -0
  78. package/dist/resolution-conformance/cases/resolve-package-only-no-version.json +8 -0
  79. package/dist/resolution-conformance/cases/resolve-stdlib-no-urls.json +10 -0
  80. package/dist/resolution-conformance/index.d.ts +76 -0
  81. package/dist/resolution-conformance/index.d.ts.map +1 -0
  82. package/dist/resolution-conformance/index.js +81 -0
  83. package/dist/run-conformance.d.ts +58 -0
  84. package/dist/run-conformance.d.ts.map +1 -0
  85. package/dist/run-conformance.js +286 -0
  86. package/dist/schema-conformance/cases/wire-bad-export-name-reject.json +11 -0
  87. package/dist/schema-conformance/cases/wire-bad-package-name-reject.json +11 -0
  88. package/dist/schema-conformance/cases/wire-component-export-accept.json +10 -0
  89. package/dist/schema-conformance/cases/wire-empty-package-reject.json +11 -0
  90. package/dist/schema-conformance/cases/wire-minimal-accept.json +10 -0
  91. package/dist/schema-conformance/cases/wire-multi-package-accept.json +14 -0
  92. package/dist/schema-conformance/cases/wire-prose-accept.json +15 -0
  93. package/dist/schema-conformance/cases/wire-registry-metadata-reject.json +13 -0
  94. package/dist/schema-conformance/cases/wire-stale-libraries-reject.json +14 -0
  95. package/dist/schema-conformance/cases/wire-stdlib-accept.json +10 -0
  96. package/dist/schema-conformance/cases/wire-transport-field-reject.json +15 -0
  97. package/dist/schema-conformance/cases/wire-version-field-reject.json +13 -0
  98. package/dist/schema-conformance/index.d.ts +71 -0
  99. package/dist/schema-conformance/index.d.ts.map +1 -0
  100. package/dist/schema-conformance/index.js +103 -0
  101. package/dist/types.d.ts +434 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +62 -0
  104. package/dist/ws-transport.d.ts +42 -0
  105. package/dist/ws-transport.d.ts.map +1 -0
  106. package/dist/ws-transport.js +135 -0
  107. package/package.json +83 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Minimal WebSocket transport for the conformance runner.
3
+ *
4
+ * Opens a live-channel connection to a ggui-protocol server (SPEC §12.2),
5
+ * sends subscribe + action frames verbatim, and exposes an async
6
+ * `observe()` that collects inbound frames into a buffer for the
7
+ * matcher.
8
+ *
9
+ * Scope discipline: this file is the `kind: 'ws'` arm of
10
+ * `TransportConfig`. Post-v1.1 transports (stdio MCP, HTTP long-poll)
11
+ * land as sibling modules with the same `Transport` shape. The
12
+ * transport is NOT a full MCP client — it does not try to be
13
+ * compatible with the MCP SDK, and it does not implement stack
14
+ * management. The kit runs against an already-provisioned session
15
+ * (the host's `create-session` setup step ran first) and treats the
16
+ * wire as opaque after that.
17
+ *
18
+ * Auth:
19
+ * - `bearer` token → `Authorization: Bearer <token>` header on
20
+ * the upgrade request.
21
+ * - `session-cookie` → `Cookie: <cookie>` header.
22
+ */
23
+ import WebSocket from 'ws';
24
+ /**
25
+ * Open a WS transport against `config.url`, wait for the socket to
26
+ * reach `OPEN` state, and return the transport handle. Throws if the
27
+ * socket errors or closes before reaching OPEN.
28
+ */
29
+ export async function openWsTransport(config) {
30
+ const headers = authHeaders(config.auth);
31
+ const socket = new WebSocket(config.url, { headers });
32
+ const buffer = [];
33
+ let flushWaiter = null;
34
+ socket.on('message', (raw) => {
35
+ const text = raw.toString('utf8');
36
+ const parsed = safeParse(text);
37
+ const frame = parsed.ok === true
38
+ ? { kind: 'frame', raw: text, parsed: parsed.value }
39
+ : { kind: 'unparseable', raw: text, error: parsed.error };
40
+ buffer.push(frame);
41
+ // Fire any pending observer synchronously so predicate-driven
42
+ // early-return works without a polling loop.
43
+ const waiter = flushWaiter;
44
+ if (waiter !== null) {
45
+ flushWaiter = null;
46
+ waiter(frame);
47
+ }
48
+ });
49
+ await new Promise((done, fail) => {
50
+ const onOpen = () => {
51
+ socket.off('error', onError);
52
+ socket.off('close', onClose);
53
+ done();
54
+ };
55
+ const onError = (err) => {
56
+ socket.off('open', onOpen);
57
+ socket.off('close', onClose);
58
+ fail(err);
59
+ };
60
+ const onClose = (code, reason) => {
61
+ socket.off('open', onOpen);
62
+ socket.off('error', onError);
63
+ fail(new Error(`protocol-conformance: ws closed before open — code=${code} reason=${reason.toString('utf8')}`));
64
+ };
65
+ socket.once('open', onOpen);
66
+ socket.once('error', onError);
67
+ socket.once('close', onClose);
68
+ });
69
+ return {
70
+ send(frame) {
71
+ socket.send(JSON.stringify(frame));
72
+ },
73
+ async observe({ timeoutMs, predicate }) {
74
+ // Drain any already-buffered frames first; the timeout only
75
+ // bounds the wait for NEW frames.
76
+ if (predicate !== undefined) {
77
+ const existing = buffer.find((f) => predicate(f));
78
+ if (existing !== undefined)
79
+ return snapshot(buffer);
80
+ }
81
+ await new Promise((done) => {
82
+ const timer = setTimeout(() => {
83
+ flushWaiter = null;
84
+ done();
85
+ }, timeoutMs);
86
+ const onFrame = (frame) => {
87
+ if (predicate !== undefined && predicate(frame)) {
88
+ clearTimeout(timer);
89
+ done();
90
+ return;
91
+ }
92
+ flushWaiter = onFrame;
93
+ };
94
+ flushWaiter = onFrame;
95
+ });
96
+ return snapshot(buffer);
97
+ },
98
+ async close() {
99
+ if (socket.readyState === WebSocket.CLOSED ||
100
+ socket.readyState === WebSocket.CLOSING) {
101
+ return;
102
+ }
103
+ await new Promise((done) => {
104
+ socket.once('close', () => done());
105
+ socket.close();
106
+ });
107
+ },
108
+ };
109
+ }
110
+ // =============================================================================
111
+ // Helpers
112
+ // =============================================================================
113
+ function authHeaders(auth) {
114
+ switch (auth.kind) {
115
+ case 'bearer':
116
+ return { Authorization: `Bearer ${auth.token}` };
117
+ case 'session-cookie':
118
+ return { Cookie: auth.cookie };
119
+ }
120
+ }
121
+ function safeParse(text) {
122
+ try {
123
+ const parsed = JSON.parse(text);
124
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
125
+ return { ok: false, error: 'frame is not a JSON object' };
126
+ }
127
+ return { ok: true, value: parsed };
128
+ }
129
+ catch (err) {
130
+ return { ok: false, error: String(err.message ?? err) };
131
+ }
132
+ }
133
+ function snapshot(buffer) {
134
+ return buffer.slice();
135
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@ggui-ai/protocol-conformance",
3
+ "version": "0.1.0-rc.1",
4
+ "description": "Conformance test kit for the ggui protocol. Drives a behavioral fixture catalog against a live implementation over WebSocket, plus three pure-function catalogs (schema, registration, resolution) for the gadget obligations, and reports a scorecard-style pass / fail / skip. MCP builders adopt this kit to prove their implementation is protocol-conformant.",
5
+ "license": "Apache-2.0",
6
+ "keywords": [
7
+ "ggui",
8
+ "mcp",
9
+ "protocol",
10
+ "conformance",
11
+ "test-kit"
12
+ ],
13
+ "files": [
14
+ "dist",
15
+ "README.md"
16
+ ],
17
+ "type": "module",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "bin": {
21
+ "ggui-protocol-conformance": "./dist/cli.js"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "default": "./dist/index.js"
28
+ },
29
+ "./fixtures": {
30
+ "types": "./dist/fixtures/index.d.ts",
31
+ "import": "./dist/fixtures/index.js",
32
+ "default": "./dist/fixtures/index.js"
33
+ },
34
+ "./schema-conformance": {
35
+ "types": "./dist/schema-conformance/index.d.ts",
36
+ "import": "./dist/schema-conformance/index.js",
37
+ "default": "./dist/schema-conformance/index.js"
38
+ },
39
+ "./registration-conformance": {
40
+ "types": "./dist/registration-conformance/index.d.ts",
41
+ "import": "./dist/registration-conformance/index.js",
42
+ "default": "./dist/registration-conformance/index.js"
43
+ },
44
+ "./resolution-conformance": {
45
+ "types": "./dist/resolution-conformance/index.d.ts",
46
+ "import": "./dist/resolution-conformance/index.js",
47
+ "default": "./dist/resolution-conformance/index.js"
48
+ }
49
+ },
50
+ "dependencies": {
51
+ "ws": "^8.20.1",
52
+ "@ggui-ai/protocol": "0.1.0-rc.1"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.0.0",
56
+ "@types/ws": "^8.5.10",
57
+ "typescript": "^5.0.0",
58
+ "vitest": "^3.0.0"
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "https://github.com/ggui-ai/ggui.git",
63
+ "directory": "packages/protocol-conformance"
64
+ },
65
+ "homepage": "https://github.com/ggui-ai/ggui/tree/main/packages/protocol-conformance",
66
+ "bugs": {
67
+ "url": "https://github.com/ggui-ai/ggui/issues"
68
+ },
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
72
+ "engines": {
73
+ "node": ">=20.0.0"
74
+ },
75
+ "author": "ggui contributors <hello@ggui.ai>",
76
+ "scripts": {
77
+ "build": "tsc -p tsconfig.build.json && node ../scripts/fix-esm-imports.mjs dist && chmod +x dist/cli.js",
78
+ "dev": "tsc --watch",
79
+ "typecheck": "tsc --noEmit",
80
+ "test": "vitest run --passWithNoTests",
81
+ "test:watch": "vitest"
82
+ }
83
+ }