walletpair-sdk 1.0.2 → 1.0.5

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 (74) hide show
  1. package/README.md +13 -0
  2. package/dist/ble/framing.d.ts.map +1 -1
  3. package/dist/ble/framing.js +2 -2
  4. package/dist/ble/framing.js.map +1 -1
  5. package/dist/ble/index.d.ts +2 -2
  6. package/dist/ble/index.d.ts.map +1 -1
  7. package/dist/ble/index.js +2 -2
  8. package/dist/ble/index.js.map +1 -1
  9. package/dist/ble/web-ble-transport.d.ts +1 -1
  10. package/dist/ble/web-ble-transport.d.ts.map +1 -1
  11. package/dist/ble/web-ble-transport.js +23 -12
  12. package/dist/ble/web-ble-transport.js.map +1 -1
  13. package/dist/crypto.d.ts.map +1 -1
  14. package/dist/crypto.js +29 -12
  15. package/dist/crypto.js.map +1 -1
  16. package/dist/dapp-session.d.ts.map +1 -1
  17. package/dist/dapp-session.js +15 -5
  18. package/dist/dapp-session.js.map +1 -1
  19. package/dist/emitter.d.ts +1 -3
  20. package/dist/emitter.d.ts.map +1 -1
  21. package/dist/emitter.js +4 -2
  22. package/dist/emitter.js.map +1 -1
  23. package/dist/evm/eip1193.d.ts +2 -2
  24. package/dist/evm/eip1193.d.ts.map +1 -1
  25. package/dist/evm/eip1193.js +32 -18
  26. package/dist/evm/eip1193.js.map +1 -1
  27. package/dist/evm/index.d.ts +2 -2
  28. package/dist/evm/index.d.ts.map +1 -1
  29. package/dist/evm/index.js.map +1 -1
  30. package/dist/wallet-session.d.ts.map +1 -1
  31. package/dist/wallet-session.js +4 -3
  32. package/dist/wallet-session.js.map +1 -1
  33. package/dist/ws-transport.d.ts +3 -2
  34. package/dist/ws-transport.d.ts.map +1 -1
  35. package/dist/ws-transport.js +13 -4
  36. package/dist/ws-transport.js.map +1 -1
  37. package/package.json +20 -1
  38. package/src/__tests__/adversarial/crypto-attacks.test.ts +240 -233
  39. package/src/__tests__/adversarial/malicious-dapp.test.ts +228 -194
  40. package/src/__tests__/adversarial/malicious-relay.test.ts +292 -220
  41. package/src/__tests__/adversarial/malicious-wallet.test.ts +246 -180
  42. package/src/__tests__/spec-compliance/canonical-json.test.ts +105 -105
  43. package/src/__tests__/spec-compliance/crypto-vectors.test.ts +149 -154
  44. package/src/__tests__/spec-compliance/message-format.test.ts +180 -151
  45. package/src/__tests__/spec-compliance/sequence-numbers.test.ts +142 -149
  46. package/src/__tests__/spec-compliance/state-machine.test.ts +203 -180
  47. package/src/ble/framing.test.ts +122 -114
  48. package/src/ble/framing.ts +48 -51
  49. package/src/ble/index.ts +7 -7
  50. package/src/ble/web-ble-transport.test.ts +93 -84
  51. package/src/ble/web-ble-transport.ts +70 -57
  52. package/src/ble/web-bluetooth.d.ts +19 -19
  53. package/src/canonical-json.test.ts +301 -285
  54. package/src/crypto-directional.test.ts +155 -129
  55. package/src/crypto-hardening.test.ts +292 -283
  56. package/src/crypto.test.ts +364 -346
  57. package/src/crypto.ts +185 -175
  58. package/src/dapp-session.test.ts +522 -385
  59. package/src/dapp-session.ts +17 -11
  60. package/src/emitter.test.ts +122 -122
  61. package/src/emitter.ts +20 -18
  62. package/src/evm/eip1193.test.ts +283 -205
  63. package/src/evm/eip1193.ts +162 -138
  64. package/src/evm/index.ts +5 -5
  65. package/src/evm/wagmi.test.ts +1 -1
  66. package/src/integration.test.ts +329 -201
  67. package/src/security.test.ts +331 -238
  68. package/src/sequence-validation.test.ts +6 -9
  69. package/src/test-helpers.ts +102 -78
  70. package/src/types.test.ts +45 -50
  71. package/src/wallet-session.test.ts +611 -383
  72. package/src/wallet-session.ts +7 -9
  73. package/src/ws-transport.test.ts +141 -139
  74. package/src/ws-transport.ts +52 -41
@@ -6,8 +6,8 @@
6
6
  * that invalid messages are properly detected.
7
7
  */
8
8
 
9
- import { describe, expect, it } from 'vitest';
10
- import { generateChannelId, b64urlEncode, generateX25519KeyPair } from '../../crypto.js';
9
+ import { describe, expect, it } from 'vitest'
10
+ import { b64urlEncode, generateChannelId, generateX25519KeyPair } from '../../crypto.js'
11
11
 
12
12
  // ---------------------------------------------------------------------------
13
13
  // Helpers
@@ -22,71 +22,72 @@ function validEnvelope(overrides: Partial<Record<string, unknown>> = {}): Record
22
22
  from: b64urlEncode(generateX25519KeyPair().publicKey),
23
23
  body: {},
24
24
  ...overrides,
25
- };
25
+ }
26
26
  }
27
27
 
28
28
  /** Validate that the envelope has all required fields per Section 4.1. */
29
29
  function validateEnvelope(msg: Record<string, unknown>): string[] {
30
- const errors: string[] = [];
31
- if (msg.v !== 1) errors.push('v must be 1');
32
- if (typeof msg.t !== 'string' || msg.t.length === 0) errors.push('t must be a non-empty string');
30
+ const errors: string[] = []
31
+ if (msg.v !== 1) errors.push('v must be 1')
32
+ if (typeof msg.t !== 'string' || msg.t.length === 0) errors.push('t must be a non-empty string')
33
33
  if (typeof msg.ch !== 'string' || !/^[0-9a-f]{64}$/.test(msg.ch as string)) {
34
- errors.push('ch must be 64 lowercase hex chars');
34
+ errors.push('ch must be 64 lowercase hex chars')
35
35
  }
36
- if (typeof msg.ts !== 'number') errors.push('ts must be a number');
36
+ if (typeof msg.ts !== 'number') errors.push('ts must be a number')
37
37
  if (typeof msg.from !== 'string' || (msg.from as string).length === 0) {
38
- errors.push('from must be a non-empty string');
38
+ errors.push('from must be a non-empty string')
39
39
  }
40
40
  if (typeof msg.body !== 'object' || msg.body === null || Array.isArray(msg.body)) {
41
- errors.push('body must be a non-null object');
41
+ errors.push('body must be a non-null object')
42
42
  }
43
- return errors;
43
+ return errors
44
44
  }
45
45
 
46
46
  /** Validate body schema per Section 4.2. */
47
47
  function validateBody(t: string, body: Record<string, unknown>): string[] {
48
- const errors: string[] = [];
48
+ const errors: string[] = []
49
49
  switch (t) {
50
50
  case 'create':
51
- if (!body.meta || typeof body.meta !== 'object') errors.push('create body requires meta object');
52
- break;
51
+ if (!body.meta || typeof body.meta !== 'object')
52
+ errors.push('create body requires meta object')
53
+ break
53
54
  case 'join':
54
- if (!('sealed_join' in body)) errors.push('join body requires sealed_join field');
55
- break;
55
+ if (!('sealed_join' in body)) errors.push('join body requires sealed_join field')
56
+ break
56
57
  case 'accept':
57
- if (typeof body.target !== 'string') errors.push('accept body requires target string');
58
- break;
58
+ if (typeof body.target !== 'string') errors.push('accept body requires target string')
59
+ break
59
60
  case 'ready':
60
61
  for (const field of ['state', 'role', 'self', 'remote', 'reconnect']) {
61
- if (!(field in body)) errors.push(`ready body requires ${field}`);
62
+ if (!(field in body)) errors.push(`ready body requires ${field}`)
62
63
  }
63
- break;
64
+ break
64
65
  case 'req':
65
66
  case 'res':
66
67
  case 'evt':
67
- if (typeof body.id !== 'string') errors.push(`${t} body requires id string`);
68
- if (typeof body.sealed !== 'string') errors.push(`${t} body requires sealed string`);
69
- break;
68
+ if (typeof body.id !== 'string') errors.push(`${t} body requires id string`)
69
+ if (typeof body.sealed !== 'string') errors.push(`${t} body requires sealed string`)
70
+ break
70
71
  case 'ping':
71
72
  case 'pong':
72
73
  // empty body is valid
73
- break;
74
+ break
74
75
  case 'close':
75
- if (typeof body.reason !== 'string') errors.push('close body requires reason string');
76
- break;
76
+ if (typeof body.reason !== 'string') errors.push('close body requires reason string')
77
+ break
77
78
  case 'terminate':
78
- if (typeof body.reason !== 'string') errors.push('terminate body requires reason string');
79
- break;
79
+ if (typeof body.reason !== 'string') errors.push('terminate body requires reason string')
80
+ break
80
81
  default:
81
- errors.push(`unknown message type: ${t}`);
82
+ errors.push(`unknown message type: ${t}`)
82
83
  }
83
- return errors;
84
+ return errors
84
85
  }
85
86
 
86
87
  /** Check wire size limit per Section 15 rule 10. */
87
88
  function checkSizeLimit(msg: Record<string, unknown>): boolean {
88
- const wire = JSON.stringify(msg);
89
- return new TextEncoder().encode(wire).length <= 64 * 1024;
89
+ const wire = JSON.stringify(msg)
90
+ return new TextEncoder().encode(wire).length <= 64 * 1024
90
91
  }
91
92
 
92
93
  // ---------------------------------------------------------------------------
@@ -95,50 +96,66 @@ function checkSizeLimit(msg: Record<string, unknown>): boolean {
95
96
 
96
97
  describe('Section 4.1 — Envelope required fields', () => {
97
98
  it('valid envelope passes validation', () => {
98
- expect(validateEnvelope(validEnvelope())).toEqual([]);
99
- });
99
+ expect(validateEnvelope(validEnvelope())).toEqual([])
100
+ })
100
101
 
101
102
  it('v must be 1', () => {
102
- const errors = validateEnvelope(validEnvelope({ v: 2 }));
103
- expect(errors).toContain('v must be 1');
104
- });
103
+ const errors = validateEnvelope(validEnvelope({ v: 2 }))
104
+ expect(errors).toContain('v must be 1')
105
+ })
105
106
 
106
107
  it('v=0 is rejected', () => {
107
- const errors = validateEnvelope(validEnvelope({ v: 0 }));
108
- expect(errors).toContain('v must be 1');
109
- });
108
+ const errors = validateEnvelope(validEnvelope({ v: 0 }))
109
+ expect(errors).toContain('v must be 1')
110
+ })
110
111
 
111
112
  it('t must be a non-empty string', () => {
112
- expect(validateEnvelope(validEnvelope({ t: '' }))).toContain('t must be a non-empty string');
113
- expect(validateEnvelope(validEnvelope({ t: 123 }))).toContain('t must be a non-empty string');
114
- });
113
+ expect(validateEnvelope(validEnvelope({ t: '' }))).toContain('t must be a non-empty string')
114
+ expect(validateEnvelope(validEnvelope({ t: 123 }))).toContain('t must be a non-empty string')
115
+ })
115
116
 
116
117
  it('ch must be 64 lowercase hex chars', () => {
117
- expect(validateEnvelope(validEnvelope({ ch: 'ABC' }))).toContain('ch must be 64 lowercase hex chars');
118
- expect(validateEnvelope(validEnvelope({ ch: 'zz'.repeat(32) }))).toContain('ch must be 64 lowercase hex chars');
119
- expect(validateEnvelope(validEnvelope({ ch: 'aa'.repeat(31) }))).toContain('ch must be 64 lowercase hex chars');
120
- });
118
+ expect(validateEnvelope(validEnvelope({ ch: 'ABC' }))).toContain(
119
+ 'ch must be 64 lowercase hex chars',
120
+ )
121
+ expect(validateEnvelope(validEnvelope({ ch: 'zz'.repeat(32) }))).toContain(
122
+ 'ch must be 64 lowercase hex chars',
123
+ )
124
+ expect(validateEnvelope(validEnvelope({ ch: 'aa'.repeat(31) }))).toContain(
125
+ 'ch must be 64 lowercase hex chars',
126
+ )
127
+ })
121
128
 
122
129
  it('valid 64-hex-char ch passes', () => {
123
- const msg = validEnvelope({ ch: 'ab'.repeat(32) });
124
- expect(validateEnvelope(msg)).toEqual([]);
125
- });
130
+ const msg = validEnvelope({ ch: 'ab'.repeat(32) })
131
+ expect(validateEnvelope(msg)).toEqual([])
132
+ })
126
133
 
127
134
  it('ts must be a number', () => {
128
- expect(validateEnvelope(validEnvelope({ ts: 'not a number' }))).toContain('ts must be a number');
129
- });
135
+ expect(validateEnvelope(validEnvelope({ ts: 'not a number' }))).toContain('ts must be a number')
136
+ })
130
137
 
131
138
  it('from must be a non-empty string', () => {
132
- expect(validateEnvelope(validEnvelope({ from: '' }))).toContain('from must be a non-empty string');
133
- expect(validateEnvelope(validEnvelope({ from: 123 }))).toContain('from must be a non-empty string');
134
- });
139
+ expect(validateEnvelope(validEnvelope({ from: '' }))).toContain(
140
+ 'from must be a non-empty string',
141
+ )
142
+ expect(validateEnvelope(validEnvelope({ from: 123 }))).toContain(
143
+ 'from must be a non-empty string',
144
+ )
145
+ })
135
146
 
136
147
  it('body must be a non-null object', () => {
137
- expect(validateEnvelope(validEnvelope({ body: null }))).toContain('body must be a non-null object');
138
- expect(validateEnvelope(validEnvelope({ body: [1, 2] }))).toContain('body must be a non-null object');
139
- expect(validateEnvelope(validEnvelope({ body: 'string' }))).toContain('body must be a non-null object');
140
- });
141
- });
148
+ expect(validateEnvelope(validEnvelope({ body: null }))).toContain(
149
+ 'body must be a non-null object',
150
+ )
151
+ expect(validateEnvelope(validEnvelope({ body: [1, 2] }))).toContain(
152
+ 'body must be a non-null object',
153
+ )
154
+ expect(validateEnvelope(validEnvelope({ body: 'string' }))).toContain(
155
+ 'body must be a non-null object',
156
+ )
157
+ })
158
+ })
142
159
 
143
160
  // ---------------------------------------------------------------------------
144
161
  // Channel ID validation (Section 3)
@@ -146,30 +163,30 @@ describe('Section 4.1 — Envelope required fields', () => {
146
163
 
147
164
  describe('Section 3 — Channel ID validation', () => {
148
165
  it('valid channel ID: 64 lowercase hex chars', () => {
149
- const ch = generateChannelId();
150
- expect(ch).toMatch(/^[0-9a-f]{64}$/);
151
- });
166
+ const ch = generateChannelId()
167
+ expect(ch).toMatch(/^[0-9a-f]{64}$/)
168
+ })
152
169
 
153
170
  it('rejects uppercase hex', () => {
154
- const ch = 'AB'.repeat(32);
155
- expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false);
156
- });
171
+ const ch = 'AB'.repeat(32)
172
+ expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false)
173
+ })
157
174
 
158
175
  it('rejects 63 chars', () => {
159
- const ch = 'a'.repeat(63);
160
- expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false);
161
- });
176
+ const ch = 'a'.repeat(63)
177
+ expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false)
178
+ })
162
179
 
163
180
  it('rejects 65 chars', () => {
164
- const ch = 'a'.repeat(65);
165
- expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false);
166
- });
181
+ const ch = 'a'.repeat(65)
182
+ expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false)
183
+ })
167
184
 
168
185
  it('rejects non-hex chars', () => {
169
- const ch = 'g'.repeat(64);
170
- expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false);
171
- });
172
- });
186
+ const ch = 'g'.repeat(64)
187
+ expect(/^[0-9a-f]{64}$/.test(ch)).toBe(false)
188
+ })
189
+ })
173
190
 
174
191
  // ---------------------------------------------------------------------------
175
192
  // Body schema validation (Section 4.2)
@@ -177,56 +194,58 @@ describe('Section 3 — Channel ID validation', () => {
177
194
 
178
195
  describe('Section 4.2 — Body schemas', () => {
179
196
  it('create: requires meta object', () => {
180
- expect(validateBody('create', { meta: { name: 'X', description: 'Y', url: 'Z', icon: 'W' } })).toEqual([]);
181
- expect(validateBody('create', {})).toContain('create body requires meta object');
182
- expect(validateBody('create', { meta: null })).toContain('create body requires meta object');
183
- });
197
+ expect(
198
+ validateBody('create', { meta: { name: 'X', description: 'Y', url: 'Z', icon: 'W' } }),
199
+ ).toEqual([])
200
+ expect(validateBody('create', {})).toContain('create body requires meta object')
201
+ expect(validateBody('create', { meta: null })).toContain('create body requires meta object')
202
+ })
184
203
 
185
204
  it('join: requires sealed_join field (may be null for reconnect)', () => {
186
- expect(validateBody('join', { sealed_join: 'abc123' })).toEqual([]);
187
- expect(validateBody('join', { sealed_join: null })).toEqual([]);
188
- expect(validateBody('join', {})).toContain('join body requires sealed_join field');
189
- });
205
+ expect(validateBody('join', { sealed_join: 'abc123' })).toEqual([])
206
+ expect(validateBody('join', { sealed_join: null })).toEqual([])
207
+ expect(validateBody('join', {})).toContain('join body requires sealed_join field')
208
+ })
190
209
 
191
210
  it('accept: requires target string', () => {
192
- expect(validateBody('accept', { target: 'pubkey_b64' })).toEqual([]);
193
- expect(validateBody('accept', {})).toContain('accept body requires target string');
194
- expect(validateBody('accept', { target: 123 })).toContain('accept body requires target string');
195
- });
211
+ expect(validateBody('accept', { target: 'pubkey_b64' })).toEqual([])
212
+ expect(validateBody('accept', {})).toContain('accept body requires target string')
213
+ expect(validateBody('accept', { target: 123 })).toContain('accept body requires target string')
214
+ })
196
215
 
197
216
  it('ready: requires state, role, self, remote, reconnect', () => {
198
- const valid = { state: 'connected', role: 'dapp', self: 'pk1', remote: 'pk2', reconnect: false };
199
- expect(validateBody('ready', valid)).toEqual([]);
217
+ const valid = { state: 'connected', role: 'dapp', self: 'pk1', remote: 'pk2', reconnect: false }
218
+ expect(validateBody('ready', valid)).toEqual([])
200
219
  for (const field of ['state', 'role', 'self', 'remote', 'reconnect']) {
201
- const missing = { ...valid };
202
- delete (missing as any)[field];
203
- expect(validateBody('ready', missing)).toContain(`ready body requires ${field}`);
220
+ const missing = { ...valid }
221
+ delete (missing as Record<string, unknown>)[field]
222
+ expect(validateBody('ready', missing)).toContain(`ready body requires ${field}`)
204
223
  }
205
- });
224
+ })
206
225
 
207
226
  it('req/res/evt: requires id and sealed strings', () => {
208
227
  for (const t of ['req', 'res', 'evt']) {
209
- expect(validateBody(t, { id: 'uuid', sealed: 'base64data' })).toEqual([]);
210
- expect(validateBody(t, { sealed: 'base64data' })).toContain(`${t} body requires id string`);
211
- expect(validateBody(t, { id: 'uuid' })).toContain(`${t} body requires sealed string`);
228
+ expect(validateBody(t, { id: 'uuid', sealed: 'base64data' })).toEqual([])
229
+ expect(validateBody(t, { sealed: 'base64data' })).toContain(`${t} body requires id string`)
230
+ expect(validateBody(t, { id: 'uuid' })).toContain(`${t} body requires sealed string`)
212
231
  }
213
- });
232
+ })
214
233
 
215
234
  it('ping/pong: empty body is valid', () => {
216
- expect(validateBody('ping', {})).toEqual([]);
217
- expect(validateBody('pong', {})).toEqual([]);
218
- });
235
+ expect(validateBody('ping', {})).toEqual([])
236
+ expect(validateBody('pong', {})).toEqual([])
237
+ })
219
238
 
220
239
  it('close: requires reason string', () => {
221
- expect(validateBody('close', { reason: 'normal' })).toEqual([]);
222
- expect(validateBody('close', {})).toContain('close body requires reason string');
223
- });
240
+ expect(validateBody('close', { reason: 'normal' })).toEqual([])
241
+ expect(validateBody('close', {})).toContain('close body requires reason string')
242
+ })
224
243
 
225
244
  it('terminate: requires reason string', () => {
226
- expect(validateBody('terminate', { reason: 'timeout' })).toEqual([]);
227
- expect(validateBody('terminate', {})).toContain('terminate body requires reason string');
228
- });
229
- });
245
+ expect(validateBody('terminate', { reason: 'timeout' })).toEqual([])
246
+ expect(validateBody('terminate', {})).toContain('terminate body requires reason string')
247
+ })
248
+ })
230
249
 
231
250
  // ---------------------------------------------------------------------------
232
251
  // from = "_adapter" rejection (Section 2)
@@ -235,39 +254,49 @@ describe('Section 4.2 — Body schemas', () => {
235
254
  describe('Section 2 — _adapter from rejection', () => {
236
255
  it('"_adapter" is reserved for adapter-sent messages only', () => {
237
256
  // Peer message types that MUST NOT have from = "_adapter"
238
- const peerTypes = ['create', 'join', 'accept', 'req', 'res', 'evt', 'ping', 'pong', 'close'];
257
+ const peerTypes = ['create', 'join', 'accept', 'req', 'res', 'evt', 'ping', 'pong', 'close']
239
258
  for (const t of peerTypes) {
240
- const msg = validEnvelope({ t, from: '_adapter' });
259
+ const msg = validEnvelope({ t, from: '_adapter' })
241
260
  // Peers MUST reject any peer-sent message where from = "_adapter"
242
- expect(msg.from).toBe('_adapter');
261
+ expect(msg.from).toBe('_adapter')
243
262
  // Validation: from="_adapter" is only valid for adapter-sent types
244
- const isAdapterType = t === 'ready' || t === 'terminate';
245
- expect(isAdapterType).toBe(false);
263
+ const isAdapterType = t === 'ready' || t === 'terminate'
264
+ expect(isAdapterType).toBe(false)
246
265
  }
247
- });
266
+ })
248
267
 
249
268
  it('"_adapter" is valid for ready and terminate messages', () => {
250
- const adapterTypes = ['ready', 'terminate'];
269
+ const adapterTypes = ['ready', 'terminate']
251
270
  for (const t of adapterTypes) {
252
- const isAdapterType = t === 'ready' || t === 'terminate';
253
- expect(isAdapterType).toBe(true);
271
+ const isAdapterType = t === 'ready' || t === 'terminate'
272
+ expect(isAdapterType).toBe(true)
254
273
  }
255
- });
274
+ })
256
275
 
257
276
  it('from = "_adapter" MUST be rejected for peer message types', () => {
258
277
  // This test documents the rule: implementations MUST reject peer messages
259
278
  // with from = "_adapter" to prevent adapter impersonation
260
279
  function isPeerMessageWithAdapterFrom(msg: { t: string; from: string }): boolean {
261
- const peerTypes = new Set(['create', 'join', 'accept', 'req', 'res', 'evt', 'ping', 'pong', 'close']);
262
- return peerTypes.has(msg.t) && msg.from === '_adapter';
280
+ const peerTypes = new Set([
281
+ 'create',
282
+ 'join',
283
+ 'accept',
284
+ 'req',
285
+ 'res',
286
+ 'evt',
287
+ 'ping',
288
+ 'pong',
289
+ 'close',
290
+ ])
291
+ return peerTypes.has(msg.t) && msg.from === '_adapter'
263
292
  }
264
293
 
265
- expect(isPeerMessageWithAdapterFrom({ t: 'req', from: '_adapter' })).toBe(true);
266
- expect(isPeerMessageWithAdapterFrom({ t: 'req', from: 'some_pubkey' })).toBe(false);
267
- expect(isPeerMessageWithAdapterFrom({ t: 'ready', from: '_adapter' })).toBe(false);
268
- expect(isPeerMessageWithAdapterFrom({ t: 'terminate', from: '_adapter' })).toBe(false);
269
- });
270
- });
294
+ expect(isPeerMessageWithAdapterFrom({ t: 'req', from: '_adapter' })).toBe(true)
295
+ expect(isPeerMessageWithAdapterFrom({ t: 'req', from: 'some_pubkey' })).toBe(false)
296
+ expect(isPeerMessageWithAdapterFrom({ t: 'ready', from: '_adapter' })).toBe(false)
297
+ expect(isPeerMessageWithAdapterFrom({ t: 'terminate', from: '_adapter' })).toBe(false)
298
+ })
299
+ })
271
300
 
272
301
  // ---------------------------------------------------------------------------
273
302
  // Message size limit (Section 15 rule 10)
@@ -275,26 +304,26 @@ describe('Section 2 — _adapter from rejection', () => {
275
304
 
276
305
  describe('Section 15 rule 10 — Message size limit (64 KB)', () => {
277
306
  it('a normal message is within the limit', () => {
278
- const msg = validEnvelope();
279
- expect(checkSizeLimit(msg)).toBe(true);
280
- });
307
+ const msg = validEnvelope()
308
+ expect(checkSizeLimit(msg)).toBe(true)
309
+ })
281
310
 
282
311
  it('a message exceeding 64 KB is rejected', () => {
283
312
  const msg = validEnvelope({
284
313
  body: { sealed: 'x'.repeat(70000) },
285
- });
286
- expect(checkSizeLimit(msg)).toBe(false);
287
- });
314
+ })
315
+ expect(checkSizeLimit(msg)).toBe(false)
316
+ })
288
317
 
289
318
  it('exactly 64 KB is within the limit', () => {
290
319
  // Build a message that is exactly at the boundary
291
- const base = validEnvelope({ body: { sealed: '' } });
292
- const baseSize = new TextEncoder().encode(JSON.stringify(base)).length;
293
- const remaining = 64 * 1024 - baseSize;
294
- const msg = validEnvelope({ body: { sealed: 'a'.repeat(remaining) } });
295
- expect(checkSizeLimit(msg)).toBe(true);
296
- });
297
- });
320
+ const base = validEnvelope({ body: { sealed: '' } })
321
+ const baseSize = new TextEncoder().encode(JSON.stringify(base)).length
322
+ const remaining = 64 * 1024 - baseSize
323
+ const msg = validEnvelope({ body: { sealed: 'a'.repeat(remaining) } })
324
+ expect(checkSizeLimit(msg)).toBe(true)
325
+ })
326
+ })
298
327
 
299
328
  // ---------------------------------------------------------------------------
300
329
  // Close / terminate reasons (Section 12.3)
@@ -313,7 +342,7 @@ describe('Section 12.3 — Close and terminate reasons', () => {
313
342
  'rate_limited',
314
343
  'payload_too_large',
315
344
  'protocol_error',
316
- ];
345
+ ]
317
346
 
318
347
  const adapterReasons = [
319
348
  'channel_not_found',
@@ -325,21 +354,21 @@ describe('Section 12.3 — Close and terminate reasons', () => {
325
354
  'rate_limited',
326
355
  'payload_too_large',
327
356
  'protocol_error',
328
- ];
357
+ ]
329
358
 
330
359
  it('all peer close reasons are valid strings', () => {
331
360
  for (const reason of peerReasons) {
332
- expect(typeof reason).toBe('string');
333
- expect(reason.length).toBeGreaterThan(0);
361
+ expect(typeof reason).toBe('string')
362
+ expect(reason.length).toBeGreaterThan(0)
334
363
  }
335
- });
364
+ })
336
365
 
337
366
  it('all adapter terminate reasons are valid strings', () => {
338
367
  for (const reason of adapterReasons) {
339
- expect(typeof reason).toBe('string');
340
- expect(reason.length).toBeGreaterThan(0);
368
+ expect(typeof reason).toBe('string')
369
+ expect(reason.length).toBeGreaterThan(0)
341
370
  }
342
- });
371
+ })
343
372
 
344
373
  it('terminate is adapter-only (from = "_adapter")', () => {
345
374
  // Section 12.2: Only the adapter sends terminate. Peers MUST NOT send it.
@@ -350,7 +379,7 @@ describe('Section 12.3 — Close and terminate reasons', () => {
350
379
  ts: Date.now(),
351
380
  from: '_adapter',
352
381
  body: { reason: 'timeout' },
353
- };
354
- expect(validTerminate.from).toBe('_adapter');
355
- });
356
- });
382
+ }
383
+ expect(validTerminate.from).toBe('_adapter')
384
+ })
385
+ })