@ophirai/sdk 0.1.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.
Files changed (60) hide show
  1. package/README.md +139 -0
  2. package/dist/__tests__/buyer.test.d.ts +1 -0
  3. package/dist/__tests__/buyer.test.js +664 -0
  4. package/dist/__tests__/discovery.test.d.ts +1 -0
  5. package/dist/__tests__/discovery.test.js +188 -0
  6. package/dist/__tests__/escrow.test.d.ts +1 -0
  7. package/dist/__tests__/escrow.test.js +385 -0
  8. package/dist/__tests__/identity.test.d.ts +1 -0
  9. package/dist/__tests__/identity.test.js +222 -0
  10. package/dist/__tests__/integration.test.d.ts +1 -0
  11. package/dist/__tests__/integration.test.js +681 -0
  12. package/dist/__tests__/lockstep.test.d.ts +1 -0
  13. package/dist/__tests__/lockstep.test.js +320 -0
  14. package/dist/__tests__/messages.test.d.ts +1 -0
  15. package/dist/__tests__/messages.test.js +976 -0
  16. package/dist/__tests__/negotiation.test.d.ts +1 -0
  17. package/dist/__tests__/negotiation.test.js +667 -0
  18. package/dist/__tests__/seller.test.d.ts +1 -0
  19. package/dist/__tests__/seller.test.js +767 -0
  20. package/dist/__tests__/server.test.d.ts +1 -0
  21. package/dist/__tests__/server.test.js +239 -0
  22. package/dist/__tests__/signing.test.d.ts +1 -0
  23. package/dist/__tests__/signing.test.js +713 -0
  24. package/dist/__tests__/sla.test.d.ts +1 -0
  25. package/dist/__tests__/sla.test.js +342 -0
  26. package/dist/__tests__/transport.test.d.ts +1 -0
  27. package/dist/__tests__/transport.test.js +197 -0
  28. package/dist/__tests__/x402.test.d.ts +1 -0
  29. package/dist/__tests__/x402.test.js +141 -0
  30. package/dist/buyer.d.ts +190 -0
  31. package/dist/buyer.js +555 -0
  32. package/dist/discovery.d.ts +47 -0
  33. package/dist/discovery.js +51 -0
  34. package/dist/escrow.d.ts +177 -0
  35. package/dist/escrow.js +434 -0
  36. package/dist/identity.d.ts +60 -0
  37. package/dist/identity.js +108 -0
  38. package/dist/index.d.ts +122 -0
  39. package/dist/index.js +43 -0
  40. package/dist/lockstep.d.ts +94 -0
  41. package/dist/lockstep.js +127 -0
  42. package/dist/messages.d.ts +172 -0
  43. package/dist/messages.js +262 -0
  44. package/dist/negotiation.d.ts +113 -0
  45. package/dist/negotiation.js +214 -0
  46. package/dist/seller.d.ts +127 -0
  47. package/dist/seller.js +395 -0
  48. package/dist/server.d.ts +52 -0
  49. package/dist/server.js +149 -0
  50. package/dist/signing.d.ts +98 -0
  51. package/dist/signing.js +165 -0
  52. package/dist/sla.d.ts +95 -0
  53. package/dist/sla.js +187 -0
  54. package/dist/transport.d.ts +41 -0
  55. package/dist/transport.js +127 -0
  56. package/dist/types.d.ts +86 -0
  57. package/dist/types.js +1 -0
  58. package/dist/x402.d.ts +25 -0
  59. package/dist/x402.js +54 -0
  60. package/package.json +40 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,239 @@
1
+ import { describe, it, expect, afterEach, beforeEach } from 'vitest';
2
+ import { NegotiationServer } from '../server.js';
3
+ import { OphirError, OphirErrorCode } from '@ophirai/protocol';
4
+ function jsonrpcRequest(url, body) {
5
+ return fetch(url, {
6
+ method: 'POST',
7
+ headers: { 'Content-Type': 'application/json' },
8
+ body: JSON.stringify(body),
9
+ });
10
+ }
11
+ describe('NegotiationServer', () => {
12
+ let server;
13
+ let baseUrl;
14
+ beforeEach(async () => {
15
+ server = new NegotiationServer();
16
+ });
17
+ afterEach(async () => {
18
+ await server.close();
19
+ });
20
+ async function startServer() {
21
+ await server.listen(0);
22
+ const port = server.getPort();
23
+ return `http://localhost:${port}`;
24
+ }
25
+ // 1. Returns -32600 for invalid jsonrpc version
26
+ it('returns -32600 for invalid jsonrpc version', async () => {
27
+ baseUrl = await startServer();
28
+ const res = await jsonrpcRequest(baseUrl, {
29
+ jsonrpc: '1.0',
30
+ method: 'test',
31
+ id: 1,
32
+ });
33
+ const json = await res.json();
34
+ expect(json).toEqual({
35
+ jsonrpc: '2.0',
36
+ id: 1,
37
+ error: { code: -32600, message: 'Invalid Request: jsonrpc must be "2.0"' },
38
+ });
39
+ });
40
+ // 2. Returns -32601 for unknown method
41
+ it('returns -32601 for unknown method', async () => {
42
+ baseUrl = await startServer();
43
+ const res = await jsonrpcRequest(baseUrl, {
44
+ jsonrpc: '2.0',
45
+ method: 'nonexistent',
46
+ id: 42,
47
+ });
48
+ const json = await res.json();
49
+ expect(json).toEqual({
50
+ jsonrpc: '2.0',
51
+ id: 42,
52
+ error: { code: -32601, message: 'Method not found: nonexistent' },
53
+ });
54
+ });
55
+ // 3. Returns 204 for notifications (no id) with unknown method
56
+ it('returns 204 for notification with unknown method', async () => {
57
+ baseUrl = await startServer();
58
+ const res = await jsonrpcRequest(baseUrl, {
59
+ jsonrpc: '2.0',
60
+ method: 'nonexistent',
61
+ });
62
+ expect(res.status).toBe(204);
63
+ const body = await res.text();
64
+ expect(body).toBe('');
65
+ });
66
+ // 4. Returns 204 for notifications with known method
67
+ it('returns 204 for notification with known method', async () => {
68
+ baseUrl = await startServer();
69
+ server.handle('ping', async () => 'pong');
70
+ const res = await jsonrpcRequest(baseUrl, {
71
+ jsonrpc: '2.0',
72
+ method: 'ping',
73
+ });
74
+ expect(res.status).toBe(204);
75
+ const body = await res.text();
76
+ expect(body).toBe('');
77
+ });
78
+ // 5. Returns result for valid request with handler
79
+ it('returns result for valid request with handler', async () => {
80
+ server.handle('add', async (params) => {
81
+ const { a, b } = params;
82
+ return a + b;
83
+ });
84
+ baseUrl = await startServer();
85
+ const res = await jsonrpcRequest(baseUrl, {
86
+ jsonrpc: '2.0',
87
+ method: 'add',
88
+ params: { a: 3, b: 7 },
89
+ id: 1,
90
+ });
91
+ const json = await res.json();
92
+ expect(json).toEqual({
93
+ jsonrpc: '2.0',
94
+ id: 1,
95
+ result: 10,
96
+ });
97
+ });
98
+ // 6. Returns OphirError with code -32000 when handler throws OphirError
99
+ it('returns -32000 with OphirError code when handler throws OphirError', async () => {
100
+ server.handle('fail', async () => {
101
+ throw new OphirError(OphirErrorCode.INVALID_MESSAGE, 'bad message');
102
+ });
103
+ baseUrl = await startServer();
104
+ const res = await jsonrpcRequest(baseUrl, {
105
+ jsonrpc: '2.0',
106
+ method: 'fail',
107
+ id: 5,
108
+ });
109
+ const json = await res.json();
110
+ expect(json).toEqual({
111
+ jsonrpc: '2.0',
112
+ id: 5,
113
+ error: {
114
+ code: -32000,
115
+ message: 'bad message',
116
+ data: { ophir_code: OphirErrorCode.INVALID_MESSAGE },
117
+ },
118
+ });
119
+ });
120
+ // 7. Returns generic error with code -32603 when handler throws non-OphirError
121
+ it('returns -32603 when handler throws a generic Error', async () => {
122
+ server.handle('boom', async () => {
123
+ throw new Error('something broke');
124
+ });
125
+ baseUrl = await startServer();
126
+ const res = await jsonrpcRequest(baseUrl, {
127
+ jsonrpc: '2.0',
128
+ method: 'boom',
129
+ id: 6,
130
+ });
131
+ const json = await res.json();
132
+ expect(json).toEqual({
133
+ jsonrpc: '2.0',
134
+ id: 6,
135
+ error: {
136
+ code: -32603,
137
+ message: 'something broke',
138
+ data: undefined,
139
+ },
140
+ });
141
+ });
142
+ // 8. handle() registers method handlers
143
+ it('handle() registers method handlers that are callable', async () => {
144
+ server.handle('echo', async (params) => params);
145
+ baseUrl = await startServer();
146
+ const res = await jsonrpcRequest(baseUrl, {
147
+ jsonrpc: '2.0',
148
+ method: 'echo',
149
+ params: { hello: 'world' },
150
+ id: 1,
151
+ });
152
+ const json = await res.json();
153
+ expect(json.result).toEqual({ hello: 'world' });
154
+ });
155
+ // 9. getPort() returns the bound port after listen
156
+ it('getPort() returns the bound port after listen', async () => {
157
+ await server.listen(0);
158
+ const port = server.getPort();
159
+ expect(port).toBeDefined();
160
+ expect(typeof port).toBe('number');
161
+ expect(port).toBeGreaterThan(0);
162
+ });
163
+ // 10. getPort() returns undefined before listen
164
+ it('getPort() returns undefined before listen', () => {
165
+ expect(server.getPort()).toBeUndefined();
166
+ });
167
+ // 11. close() resolves when server not started
168
+ it('close() resolves when server not started', async () => {
169
+ await expect(server.close()).resolves.toBeUndefined();
170
+ });
171
+ // 12. close() stops the server
172
+ it('close() stops the server', async () => {
173
+ baseUrl = await startServer();
174
+ // Verify server is running
175
+ const res = await jsonrpcRequest(baseUrl, {
176
+ jsonrpc: '2.0',
177
+ method: 'ping',
178
+ id: 1,
179
+ });
180
+ expect(res.status).toBe(200);
181
+ await server.close();
182
+ // Prevent afterEach from double-closing (which would throw "Server is not running")
183
+ server = new NegotiationServer();
184
+ // Verify server is stopped — fetch should reject
185
+ try {
186
+ await jsonrpcRequest(baseUrl, {
187
+ jsonrpc: '2.0',
188
+ method: 'ping',
189
+ id: 2,
190
+ });
191
+ expect.fail('Request should have failed after server close');
192
+ }
193
+ catch {
194
+ // Expected — connection refused or similar error
195
+ }
196
+ });
197
+ // 13. Returns null id when jsonrpc is wrong and no id provided
198
+ it('returns null id when jsonrpc is invalid and no id provided', async () => {
199
+ baseUrl = await startServer();
200
+ const res = await jsonrpcRequest(baseUrl, {
201
+ jsonrpc: '1.0',
202
+ method: 'test',
203
+ });
204
+ const json = await res.json();
205
+ expect(json).toEqual({
206
+ jsonrpc: '2.0',
207
+ id: null,
208
+ error: { code: -32600, message: 'Invalid Request: jsonrpc must be "2.0"' },
209
+ });
210
+ });
211
+ // 14. Multiple handlers can be registered
212
+ it('multiple handlers can be registered and dispatched independently', async () => {
213
+ server.handle('greet', async (params) => {
214
+ const { name } = params;
215
+ return `Hello, ${name}`;
216
+ });
217
+ server.handle('multiply', async (params) => {
218
+ const { x, y } = params;
219
+ return x * y;
220
+ });
221
+ baseUrl = await startServer();
222
+ const [greetRes, multiplyRes] = await Promise.all([
223
+ jsonrpcRequest(baseUrl, {
224
+ jsonrpc: '2.0',
225
+ method: 'greet',
226
+ params: { name: 'Ophir' },
227
+ id: 1,
228
+ }).then((r) => r.json()),
229
+ jsonrpcRequest(baseUrl, {
230
+ jsonrpc: '2.0',
231
+ method: 'multiply',
232
+ params: { x: 4, y: 5 },
233
+ id: 2,
234
+ }).then((r) => r.json()),
235
+ ]);
236
+ expect(greetRes.result).toBe('Hello, Ophir');
237
+ expect(multiplyRes.result).toBe(20);
238
+ });
239
+ });
@@ -0,0 +1 @@
1
+ export {};