@tinyhumansai/tinyplace 0.1.0 → 0.1.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.
- package/package.json +2 -1
- package/src/api/a2a.ts +0 -50
- package/src/api/admin.ts +0 -95
- package/src/api/broadcasts.ts +0 -110
- package/src/api/channels.ts +0 -110
- package/src/api/directory.ts +0 -45
- package/src/api/escrow.ts +0 -163
- package/src/api/events.ts +0 -133
- package/src/api/explorer.ts +0 -48
- package/src/api/groups.ts +0 -64
- package/src/api/inbox.ts +0 -71
- package/src/api/keys.ts +0 -18
- package/src/api/ledger.ts +0 -28
- package/src/api/marketplace.ts +0 -165
- package/src/api/messages.ts +0 -23
- package/src/api/moderation.ts +0 -71
- package/src/api/payments.ts +0 -47
- package/src/api/pricing.ts +0 -122
- package/src/api/profiles.ts +0 -43
- package/src/api/registry.ts +0 -143
- package/src/api/reputation.ts +0 -60
- package/src/api/search.ts +0 -59
- package/src/api/stats.ts +0 -32
- package/src/auth.ts +0 -75
- package/src/client.ts +0 -120
- package/src/crypto.ts +0 -74
- package/src/http.ts +0 -147
- package/src/index.ts +0 -72
- package/src/local-signer.ts +0 -78
- package/src/signal/crypto.ts +0 -229
- package/src/signal/index.ts +0 -28
- package/src/signal/keys.ts +0 -54
- package/src/signal/memory-store.ts +0 -66
- package/src/signal/ratchet.ts +0 -162
- package/src/signal/session.ts +0 -189
- package/src/signal/store.ts +0 -49
- package/src/signal/x3dh.ts +0 -130
- package/src/signer.ts +0 -21
- package/src/types/broadcasts.ts +0 -81
- package/src/types/commerce.ts +0 -206
- package/src/types/directory.ts +0 -98
- package/src/types/escrow.ts +0 -163
- package/src/types/events.ts +0 -155
- package/src/types/explorer.ts +0 -152
- package/src/types/groups.ts +0 -62
- package/src/types/identity.ts +0 -113
- package/src/types/index.ts +0 -16
- package/src/types/ledger.ts +0 -78
- package/src/types/marketplace.ts +0 -166
- package/src/types/messaging.ts +0 -77
- package/src/types/payments.ts +0 -103
- package/src/types/profile.ts +0 -55
- package/src/types/reputation.ts +0 -98
- package/src/types/search.ts +0 -61
- package/src/types/social.ts +0 -186
- package/src/websocket.ts +0 -112
- package/tests/signal.test.ts +0 -353
- package/tests/staging.test.ts +0 -650
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -7
package/tests/signal.test.ts
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
generateX25519KeyPair,
|
|
4
|
-
x25519SharedSecret,
|
|
5
|
-
kdfRootKey,
|
|
6
|
-
kdfChainKey,
|
|
7
|
-
deriveMessageKeys,
|
|
8
|
-
encrypt,
|
|
9
|
-
decrypt,
|
|
10
|
-
ed25519SeedToX25519KeyPair,
|
|
11
|
-
ed25519PubToX25519Pub,
|
|
12
|
-
toBase64,
|
|
13
|
-
fromBase64,
|
|
14
|
-
} from "../src/signal/crypto.js";
|
|
15
|
-
import type { X25519KeyPair } from "../src/signal/crypto.js";
|
|
16
|
-
import {
|
|
17
|
-
x3dhInitiate,
|
|
18
|
-
x3dhRespond,
|
|
19
|
-
buildAssociatedData,
|
|
20
|
-
} from "../src/signal/x3dh.js";
|
|
21
|
-
import { ratchetEncrypt, ratchetDecrypt } from "../src/signal/ratchet.js";
|
|
22
|
-
import type { SessionState } from "../src/signal/store.js";
|
|
23
|
-
import { MemorySessionStore } from "../src/signal/memory-store.js";
|
|
24
|
-
import { SignalSession } from "../src/signal/session.js";
|
|
25
|
-
import {
|
|
26
|
-
generateSignedPreKey,
|
|
27
|
-
generatePreKeys,
|
|
28
|
-
serializeSignedKey,
|
|
29
|
-
serializePreKey,
|
|
30
|
-
} from "../src/signal/keys.js";
|
|
31
|
-
import { LocalSigner } from "../src/local-signer.js";
|
|
32
|
-
import type { KeyBundle } from "../src/types/index.js";
|
|
33
|
-
|
|
34
|
-
describe("signal crypto primitives", () => {
|
|
35
|
-
it("generates X25519 key pairs", () => {
|
|
36
|
-
const kp = generateX25519KeyPair();
|
|
37
|
-
expect(kp.publicKey.length).toBe(32);
|
|
38
|
-
expect(kp.privateKey.length).toBe(32);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("computes shared secret", () => {
|
|
42
|
-
const alice = generateX25519KeyPair();
|
|
43
|
-
const bob = generateX25519KeyPair();
|
|
44
|
-
const sharedA = x25519SharedSecret(alice.privateKey, bob.publicKey);
|
|
45
|
-
const sharedB = x25519SharedSecret(bob.privateKey, alice.publicKey);
|
|
46
|
-
expect(toBase64(sharedA)).toBe(toBase64(sharedB));
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("KDF root key produces 32-byte outputs", () => {
|
|
50
|
-
const rk = new Uint8Array(32).fill(1);
|
|
51
|
-
const dh = new Uint8Array(32).fill(2);
|
|
52
|
-
const result = kdfRootKey(rk, dh);
|
|
53
|
-
expect(result.rootKey.length).toBe(32);
|
|
54
|
-
expect(result.chainKey.length).toBe(32);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("KDF chain key produces different chain and message keys", () => {
|
|
58
|
-
const ck = new Uint8Array(32).fill(3);
|
|
59
|
-
const result = kdfChainKey(ck);
|
|
60
|
-
expect(result.chainKey.length).toBe(32);
|
|
61
|
-
expect(result.messageKey.length).toBe(32);
|
|
62
|
-
expect(toBase64(result.chainKey)).not.toBe(toBase64(result.messageKey));
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("derives enc, mac, and iv from message key", () => {
|
|
66
|
-
const mk = new Uint8Array(32).fill(4);
|
|
67
|
-
const keys = deriveMessageKeys(mk);
|
|
68
|
-
expect(keys.encKey.length).toBe(32);
|
|
69
|
-
expect(keys.macKey.length).toBe(32);
|
|
70
|
-
expect(keys.iv.length).toBe(16);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("encrypts and decrypts with message key", async () => {
|
|
74
|
-
const mk = new Uint8Array(32).fill(5);
|
|
75
|
-
const plaintext = new TextEncoder().encode("hello world");
|
|
76
|
-
const ad = new Uint8Array(64).fill(0);
|
|
77
|
-
const ciphertext = await encrypt(mk, plaintext, ad);
|
|
78
|
-
const decrypted = await decrypt(mk, ciphertext, ad);
|
|
79
|
-
expect(new TextDecoder().decode(decrypted)).toBe("hello world");
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("rejects tampered ciphertext", async () => {
|
|
83
|
-
const mk = new Uint8Array(32).fill(6);
|
|
84
|
-
const plaintext = new TextEncoder().encode("test");
|
|
85
|
-
const ad = new Uint8Array(64);
|
|
86
|
-
const ciphertext = await encrypt(mk, plaintext, ad);
|
|
87
|
-
ciphertext[0]! ^= 0xff;
|
|
88
|
-
await expect(decrypt(mk, ciphertext, ad)).rejects.toThrow("MAC verification failed");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("base64 round-trips", () => {
|
|
92
|
-
const bytes = new Uint8Array([1, 2, 3, 255, 0, 128]);
|
|
93
|
-
expect(fromBase64(toBase64(bytes))).toEqual(bytes);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("derives X25519 from Ed25519 seed deterministically", () => {
|
|
97
|
-
const seed = new Uint8Array(32).fill(7);
|
|
98
|
-
const kp1 = ed25519SeedToX25519KeyPair(seed);
|
|
99
|
-
const kp2 = ed25519SeedToX25519KeyPair(seed);
|
|
100
|
-
expect(toBase64(kp1.publicKey)).toBe(toBase64(kp2.publicKey));
|
|
101
|
-
expect(toBase64(kp1.privateKey)).toBe(toBase64(kp2.privateKey));
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe("X3DH key agreement", () => {
|
|
106
|
-
it("initiator and responder derive the same shared secret", () => {
|
|
107
|
-
const aliceIdentity = generateX25519KeyPair();
|
|
108
|
-
const bobIdentity = generateX25519KeyPair();
|
|
109
|
-
const bobSignedPreKey = generateX25519KeyPair();
|
|
110
|
-
const bobOneTimePreKey = generateX25519KeyPair();
|
|
111
|
-
|
|
112
|
-
const initResult = x3dhInitiate(aliceIdentity, {
|
|
113
|
-
identityKey: bobIdentity.publicKey,
|
|
114
|
-
signedPreKeyId: "spk_1",
|
|
115
|
-
signedPreKey: bobSignedPreKey.publicKey,
|
|
116
|
-
oneTimePreKeyId: "opk_1",
|
|
117
|
-
oneTimePreKey: bobOneTimePreKey.publicKey,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const bobSession = x3dhRespond(
|
|
121
|
-
bobIdentity,
|
|
122
|
-
bobSignedPreKey,
|
|
123
|
-
aliceIdentity.publicKey,
|
|
124
|
-
initResult.ephemeralPublicKey,
|
|
125
|
-
bobOneTimePreKey,
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
expect(toBase64(initResult.session.rootKey)).toBe(toBase64(bobSession.rootKey));
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("works without one-time pre-key", () => {
|
|
132
|
-
const aliceIdentity = generateX25519KeyPair();
|
|
133
|
-
const bobIdentity = generateX25519KeyPair();
|
|
134
|
-
const bobSignedPreKey = generateX25519KeyPair();
|
|
135
|
-
|
|
136
|
-
const initResult = x3dhInitiate(aliceIdentity, {
|
|
137
|
-
identityKey: bobIdentity.publicKey,
|
|
138
|
-
signedPreKeyId: "spk_1",
|
|
139
|
-
signedPreKey: bobSignedPreKey.publicKey,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const bobSession = x3dhRespond(
|
|
143
|
-
bobIdentity,
|
|
144
|
-
bobSignedPreKey,
|
|
145
|
-
aliceIdentity.publicKey,
|
|
146
|
-
initResult.ephemeralPublicKey,
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
expect(toBase64(initResult.session.rootKey)).toBe(toBase64(bobSession.rootKey));
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe("Double Ratchet", () => {
|
|
154
|
-
let aliceSession: SessionState;
|
|
155
|
-
let bobSession: SessionState;
|
|
156
|
-
let ad: Uint8Array;
|
|
157
|
-
|
|
158
|
-
beforeEach(() => {
|
|
159
|
-
const aliceIdentity = generateX25519KeyPair();
|
|
160
|
-
const bobIdentity = generateX25519KeyPair();
|
|
161
|
-
const bobSignedPreKey = generateX25519KeyPair();
|
|
162
|
-
|
|
163
|
-
const initResult = x3dhInitiate(aliceIdentity, {
|
|
164
|
-
identityKey: bobIdentity.publicKey,
|
|
165
|
-
signedPreKeyId: "spk_1",
|
|
166
|
-
signedPreKey: bobSignedPreKey.publicKey,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
aliceSession = initResult.session;
|
|
170
|
-
bobSession = x3dhRespond(
|
|
171
|
-
bobIdentity,
|
|
172
|
-
bobSignedPreKey,
|
|
173
|
-
aliceIdentity.publicKey,
|
|
174
|
-
initResult.ephemeralPublicKey,
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
ad = buildAssociatedData(aliceIdentity.publicKey, bobIdentity.publicKey);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("encrypts and decrypts a single message", async () => {
|
|
181
|
-
const plaintext = new TextEncoder().encode("hello bob");
|
|
182
|
-
const message = await ratchetEncrypt(aliceSession, plaintext, ad);
|
|
183
|
-
|
|
184
|
-
const adBob = buildAssociatedData(
|
|
185
|
-
ad.slice(0, 32),
|
|
186
|
-
ad.slice(32),
|
|
187
|
-
);
|
|
188
|
-
const decrypted = await ratchetDecrypt(bobSession, message, adBob);
|
|
189
|
-
expect(new TextDecoder().decode(decrypted)).toBe("hello bob");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("handles multiple messages in sequence", async () => {
|
|
193
|
-
const adBob = buildAssociatedData(ad.slice(0, 32), ad.slice(32));
|
|
194
|
-
|
|
195
|
-
for (let i = 0; i < 5; i++) {
|
|
196
|
-
const plaintext = new TextEncoder().encode(`message ${i}`);
|
|
197
|
-
const message = await ratchetEncrypt(aliceSession, plaintext, ad);
|
|
198
|
-
const decrypted = await ratchetDecrypt(bobSession, message, adBob);
|
|
199
|
-
expect(new TextDecoder().decode(decrypted)).toBe(`message ${i}`);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("handles back-and-forth conversation", async () => {
|
|
204
|
-
const adBob = buildAssociatedData(ad.slice(0, 32), ad.slice(32));
|
|
205
|
-
|
|
206
|
-
const msg1 = await ratchetEncrypt(aliceSession, new TextEncoder().encode("hello"), ad);
|
|
207
|
-
const dec1 = await ratchetDecrypt(bobSession, msg1, adBob);
|
|
208
|
-
expect(new TextDecoder().decode(dec1)).toBe("hello");
|
|
209
|
-
|
|
210
|
-
const msg2 = await ratchetEncrypt(bobSession, new TextEncoder().encode("hi back"), adBob);
|
|
211
|
-
const dec2 = await ratchetDecrypt(aliceSession, msg2, ad);
|
|
212
|
-
expect(new TextDecoder().decode(dec2)).toBe("hi back");
|
|
213
|
-
|
|
214
|
-
const msg3 = await ratchetEncrypt(aliceSession, new TextEncoder().encode("third"), ad);
|
|
215
|
-
const dec3 = await ratchetDecrypt(bobSession, msg3, adBob);
|
|
216
|
-
expect(new TextDecoder().decode(dec3)).toBe("third");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("handles out-of-order messages via skipped keys", async () => {
|
|
220
|
-
const adBob = buildAssociatedData(ad.slice(0, 32), ad.slice(32));
|
|
221
|
-
|
|
222
|
-
const msg0 = await ratchetEncrypt(aliceSession, new TextEncoder().encode("first"), ad);
|
|
223
|
-
const msg1 = await ratchetEncrypt(aliceSession, new TextEncoder().encode("second"), ad);
|
|
224
|
-
const msg2 = await ratchetEncrypt(aliceSession, new TextEncoder().encode("third"), ad);
|
|
225
|
-
|
|
226
|
-
const dec2 = await ratchetDecrypt(bobSession, msg2, adBob);
|
|
227
|
-
expect(new TextDecoder().decode(dec2)).toBe("third");
|
|
228
|
-
|
|
229
|
-
const dec0 = await ratchetDecrypt(bobSession, msg0, adBob);
|
|
230
|
-
expect(new TextDecoder().decode(dec0)).toBe("first");
|
|
231
|
-
|
|
232
|
-
const dec1 = await ratchetDecrypt(bobSession, msg1, adBob);
|
|
233
|
-
expect(new TextDecoder().decode(dec1)).toBe("second");
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
describe("SignalSession with MemorySessionStore", () => {
|
|
238
|
-
it("full encrypt/decrypt flow between two parties", async () => {
|
|
239
|
-
const aliceIdentity = generateX25519KeyPair();
|
|
240
|
-
const bobIdentity = generateX25519KeyPair();
|
|
241
|
-
const bobSignedPreKey = generateX25519KeyPair();
|
|
242
|
-
const bobOneTimePreKey = generateX25519KeyPair();
|
|
243
|
-
|
|
244
|
-
const aliceStore = new MemorySessionStore(aliceIdentity);
|
|
245
|
-
const bobStore = new MemorySessionStore(bobIdentity);
|
|
246
|
-
|
|
247
|
-
const signer = await LocalSigner.generate();
|
|
248
|
-
const bobSignedPreKeyPair = await generateSignedPreKey(signer, "spk_1");
|
|
249
|
-
|
|
250
|
-
await bobStore.storeSignedPreKey({
|
|
251
|
-
keyId: "spk_1",
|
|
252
|
-
keyPair: bobSignedPreKey,
|
|
253
|
-
signature: bobSignedPreKeyPair.signature,
|
|
254
|
-
});
|
|
255
|
-
const dummySig = await signer.sign(bobOneTimePreKey.publicKey);
|
|
256
|
-
await bobStore.storePreKey({ keyId: "opk_1", keyPair: bobOneTimePreKey, signature: dummySig });
|
|
257
|
-
|
|
258
|
-
const aliceSignal = new SignalSession(aliceStore, aliceIdentity.publicKey);
|
|
259
|
-
const bobSignal = new SignalSession(bobStore, bobIdentity.publicKey);
|
|
260
|
-
|
|
261
|
-
const bundle: KeyBundle = {
|
|
262
|
-
agentId: "bob",
|
|
263
|
-
identityKey: toBase64(bobIdentity.publicKey),
|
|
264
|
-
signedPreKey: {
|
|
265
|
-
keyId: "spk_1",
|
|
266
|
-
publicKey: toBase64(bobSignedPreKey.publicKey),
|
|
267
|
-
},
|
|
268
|
-
oneTimePreKey: {
|
|
269
|
-
keyId: "opk_1",
|
|
270
|
-
publicKey: toBase64(bobOneTimePreKey.publicKey),
|
|
271
|
-
},
|
|
272
|
-
updatedAt: new Date().toISOString(),
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
const encrypted = await aliceSignal.encrypt(
|
|
276
|
-
"bob",
|
|
277
|
-
bobIdentity.publicKey,
|
|
278
|
-
new TextEncoder().encode("hello bob!"),
|
|
279
|
-
bundle,
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
expect(encrypted.type).toBe("PREKEY_BUNDLE");
|
|
283
|
-
expect(encrypted.signal?.ephemeralKey).toBeDefined();
|
|
284
|
-
expect(encrypted.signal?.signedPreKeyId).toBe("spk_1");
|
|
285
|
-
|
|
286
|
-
const envelope = {
|
|
287
|
-
id: "msg_1",
|
|
288
|
-
from: "alice",
|
|
289
|
-
to: "bob",
|
|
290
|
-
timestamp: new Date().toISOString(),
|
|
291
|
-
deviceId: 1,
|
|
292
|
-
type: encrypted.type as "CIPHERTEXT" | "PREKEY_BUNDLE",
|
|
293
|
-
body: encrypted.body,
|
|
294
|
-
signal: encrypted.signal,
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
const decrypted = await bobSignal.decrypt("alice", aliceIdentity.publicKey, envelope);
|
|
298
|
-
expect(new TextDecoder().decode(decrypted)).toBe("hello bob!");
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe("LocalSigner X25519 derivation", () => {
|
|
303
|
-
it("derives deterministic X25519 key pair", async () => {
|
|
304
|
-
const signer = await LocalSigner.generate();
|
|
305
|
-
const kp1 = await signer.getX25519KeyPair();
|
|
306
|
-
const kp2 = await signer.getX25519KeyPair();
|
|
307
|
-
expect(kp1.publicKey.length).toBe(32);
|
|
308
|
-
expect(kp1.privateKey.length).toBe(32);
|
|
309
|
-
expect(toBase64(kp1.publicKey)).toBe(toBase64(kp2.publicKey));
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it("derived key pair works for X25519 ECDH", async () => {
|
|
313
|
-
const signer1 = await LocalSigner.generate();
|
|
314
|
-
const signer2 = await LocalSigner.generate();
|
|
315
|
-
const kp1 = await signer1.getX25519KeyPair();
|
|
316
|
-
const kp2 = await signer2.getX25519KeyPair();
|
|
317
|
-
const shared1 = x25519SharedSecret(kp1.privateKey, kp2.publicKey);
|
|
318
|
-
const shared2 = x25519SharedSecret(kp2.privateKey, kp1.publicKey);
|
|
319
|
-
expect(toBase64(shared1)).toBe(toBase64(shared2));
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it("ed25519PubToX25519Pub matches seed-derived public key", async () => {
|
|
323
|
-
const signer = await LocalSigner.generate();
|
|
324
|
-
const fromSeed = await signer.getX25519KeyPair();
|
|
325
|
-
const fromPub = ed25519PubToX25519Pub(signer.publicKey);
|
|
326
|
-
expect(toBase64(fromSeed.publicKey)).toBe(toBase64(fromPub));
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
describe("key generation helpers", () => {
|
|
331
|
-
it("generates signed pre-key with valid signature", async () => {
|
|
332
|
-
const signer = await LocalSigner.generate();
|
|
333
|
-
const spk = await generateSignedPreKey(signer, "spk_test");
|
|
334
|
-
expect(spk.keyId).toBe("spk_test");
|
|
335
|
-
expect(spk.keyPair.publicKey.length).toBe(32);
|
|
336
|
-
expect(spk.signature.length).toBe(64);
|
|
337
|
-
const serialized = serializeSignedKey(spk);
|
|
338
|
-
expect(serialized.keyId).toBe("spk_test");
|
|
339
|
-
expect(typeof serialized.publicKey).toBe("string");
|
|
340
|
-
expect(typeof serialized.signature).toBe("string");
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it("generates batch of pre-keys", async () => {
|
|
344
|
-
const signer = await LocalSigner.generate();
|
|
345
|
-
const preKeys = await generatePreKeys(signer, 1, 10);
|
|
346
|
-
expect(preKeys.length).toBe(10);
|
|
347
|
-
expect(preKeys[0]!.keyId).toBe("pk_1");
|
|
348
|
-
expect(preKeys[9]!.keyId).toBe("pk_10");
|
|
349
|
-
const serialized = serializePreKey(preKeys[0]!);
|
|
350
|
-
expect(typeof serialized.publicKey).toBe("string");
|
|
351
|
-
expect(typeof serialized.signature).toBe("string");
|
|
352
|
-
});
|
|
353
|
-
});
|