@session-foundation/qa-seeder 0.1.21

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/dist/index.js ADDED
@@ -0,0 +1,1043 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ USERNAME: () => USERNAME,
34
+ buildStateForTest: () => buildStateForTest,
35
+ usernames: () => usernames
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+ var import_lodash5 = require("lodash");
39
+
40
+ // src/requests/seedRequest.ts
41
+ var import_lodash = require("lodash");
42
+ var fetchedSnodesFromSeed = {};
43
+ async function getAllSnodesFromSeed(seedNodeUrl) {
44
+ if (!seedNodeUrl.startsWith("http")) {
45
+ throw new Error("Invalid seed node URL, must start with http");
46
+ }
47
+ if (seedNodeUrl.endsWith("/json_rpc")) {
48
+ throw new Error("Invalid seed node URL, must NOT finish with /json_rpc");
49
+ }
50
+ if (fetchedSnodesFromSeed[seedNodeUrl]?.length) {
51
+ return fetchedSnodesFromSeed[seedNodeUrl];
52
+ }
53
+ const getAll = new GetSnodesFromSeed();
54
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
55
+ console.info(`Fetching snodes from seed node: "${seedNodeUrl}/json_rpc"`);
56
+ try {
57
+ const result = await fetch(`${seedNodeUrl}/json_rpc`, {
58
+ body: JSON.stringify(getAll.build()),
59
+ method: "POST"
60
+ });
61
+ console.info("snode from list result status:", result.status);
62
+ const json = await result.json();
63
+ fetchedSnodesFromSeed[seedNodeUrl] = json.result.service_node_states;
64
+ return fetchedSnodesFromSeed[seedNodeUrl];
65
+ } catch (e) {
66
+ console.warn(e);
67
+ throw e;
68
+ }
69
+ }
70
+ async function randomSnodeFromNetwork(seedNodeUrl) {
71
+ const snodes = await getAllSnodesFromSeed(seedNodeUrl);
72
+ const snode = (0, import_lodash.sample)(snodes);
73
+ if (!snode) {
74
+ throw new Error(`No random snode found on seed node: ${seedNodeUrl}`);
75
+ }
76
+ return snode;
77
+ }
78
+ var GetSnodesFromSeed = class {
79
+ build() {
80
+ return {
81
+ jsonrpc: "2.0",
82
+ id: "0",
83
+ method: "get_n_service_nodes",
84
+ params: {
85
+ active_only: true,
86
+ limit: 20,
87
+ fields: {
88
+ public_ip: true,
89
+ storage_port: true,
90
+ pubkey_x25519: true,
91
+ pubkey_ed25519: true
92
+ }
93
+ }
94
+ };
95
+ }
96
+ };
97
+
98
+ // src/sessionTools/index.ts
99
+ var import_libsession_wasm = __toESM(require("@session-foundation/libsession-wasm/"));
100
+ async function loadSessionTools() {
101
+ const loaded = await (0, import_libsession_wasm.default)();
102
+ return loaded;
103
+ }
104
+
105
+ // src/requests/snodeRequests.ts
106
+ var import_lodash2 = require("lodash");
107
+ var SwarmForSubRequest = class {
108
+ method = "get_swarm";
109
+ pubkey;
110
+ constructor(pubkey) {
111
+ this.pubkey = pubkey;
112
+ }
113
+ async build() {
114
+ return {
115
+ method: this.method,
116
+ params: {
117
+ pubkey: this.pubkey,
118
+ params: {
119
+ active_only: true,
120
+ fields: {
121
+ public_ip: true,
122
+ storage_port: true,
123
+ pubkey_x25519: true,
124
+ pubkey_ed25519: true
125
+ }
126
+ }
127
+ }
128
+ };
129
+ }
130
+ loggingId() {
131
+ return `${this.method}`;
132
+ }
133
+ };
134
+ var StoreUserConfigSubRequest = class {
135
+ method = "store";
136
+ namespace;
137
+ ttlMs;
138
+ encryptedData;
139
+ destination;
140
+ userSigner;
141
+ constructor(args) {
142
+ this.namespace = args.namespace;
143
+ this.ttlMs = args.ttlMs;
144
+ this.encryptedData = args.encryptedData;
145
+ this.destination = args.sessionId;
146
+ this.userSigner = args.userSigner;
147
+ if ((0, import_lodash2.isEmpty)(this.encryptedData)) {
148
+ throw new Error("this.encryptedData cannot be empty");
149
+ }
150
+ if ((0, import_lodash2.isEmpty)(this.destination)) {
151
+ throw new Error("this.destination cannot be empty");
152
+ }
153
+ }
154
+ async build() {
155
+ const encryptedDataBase64 = this.userSigner.sodium.to_base64(
156
+ this.encryptedData,
157
+ this.userSigner.sodium.base64_variants.ORIGINAL
158
+ );
159
+ const signDetails = await this.userSigner.getSnodeSignatureParams({
160
+ getNow: () => Date.now(),
161
+ method: this.method,
162
+ namespace: this.namespace
163
+ });
164
+ if (!signDetails) {
165
+ throw new Error(
166
+ `[StoreUserConfigSubRequest] signing returned an empty result`
167
+ );
168
+ }
169
+ const toRet = {
170
+ method: this.method,
171
+ params: {
172
+ namespace: this.namespace,
173
+ ttl: this.ttlMs,
174
+ data: encryptedDataBase64,
175
+ ...signDetails
176
+ }
177
+ };
178
+ return toRet;
179
+ }
180
+ loggingId() {
181
+ return `${this.method}-${this.destination}-${this.namespace}`;
182
+ }
183
+ getDestination() {
184
+ return this.destination;
185
+ }
186
+ };
187
+ var StoreGroupConfigSubRequest = class {
188
+ method = "store";
189
+ namespace;
190
+ ttlMs;
191
+ encryptedData;
192
+ destination;
193
+ adminGroupSigner;
194
+ constructor(args) {
195
+ this.namespace = args.namespace;
196
+ this.ttlMs = args.ttlMs;
197
+ this.encryptedData = args.encryptedData;
198
+ this.destination = args.groupPk;
199
+ this.adminGroupSigner = args.adminGroupSigner;
200
+ if ((0, import_lodash2.isEmpty)(this.encryptedData)) {
201
+ throw new Error("this.encryptedData cannot be empty");
202
+ }
203
+ if ((0, import_lodash2.isEmpty)(this.destination)) {
204
+ throw new Error("this.destination cannot be empty");
205
+ }
206
+ }
207
+ async build() {
208
+ const encryptedDataBase64 = this.adminGroupSigner.sodium.to_base64(
209
+ this.encryptedData,
210
+ this.adminGroupSigner.sodium.base64_variants.ORIGINAL
211
+ );
212
+ const signDetails = await this.adminGroupSigner.getSnodeSignatureParams({
213
+ getNow: () => Date.now(),
214
+ method: this.method,
215
+ namespace: this.namespace
216
+ });
217
+ if (!signDetails) {
218
+ throw new Error(
219
+ `[StoreGroupConfigSubRequest] signing returned an empty result`
220
+ );
221
+ }
222
+ const toRet = {
223
+ method: this.method,
224
+ params: {
225
+ namespace: this.namespace,
226
+ ttl: this.ttlMs,
227
+ data: encryptedDataBase64,
228
+ ...signDetails
229
+ }
230
+ };
231
+ return toRet;
232
+ }
233
+ loggingId() {
234
+ return `${this.method}-${this.destination}-${this.namespace}`;
235
+ }
236
+ getDestination() {
237
+ return this.destination;
238
+ }
239
+ };
240
+
241
+ // src/sessionUser.ts
242
+ var import_mnemonic = require("@session-foundation/mnemonic");
243
+
244
+ // src/signer/userSigner.ts
245
+ var import_sodium = require("@session-foundation/sodium");
246
+ function getVerificationDataForStoreRetrieve(params) {
247
+ const signatureTimestamp = params.getNow();
248
+ const verificationString = `${params.method}${params.namespace === 0 ? "" : params.namespace}${signatureTimestamp}`;
249
+ const verificationData = params.sodium.from_string(verificationString);
250
+ return {
251
+ toSign: new Uint8Array(verificationData),
252
+ signatureTimestamp
253
+ };
254
+ }
255
+ async function getSnodeSignatureShared(params) {
256
+ const { signatureTimestamp, toSign } = getVerificationDataForStoreRetrieve(params);
257
+ const sodium = await (0, import_sodium.getSodium)();
258
+ const signature = sodium.crypto_sign_detached(toSign, params.privKey);
259
+ const signatureBase64 = params.sodium.to_base64(
260
+ signature,
261
+ params.sodium.base64_variants.ORIGINAL
262
+ );
263
+ return {
264
+ timestamp: signatureTimestamp,
265
+ signature: signatureBase64
266
+ };
267
+ }
268
+ var UserSigner = class {
269
+ ed25519PubKey;
270
+ ed25519PrivKey;
271
+ sodium;
272
+ sessionId;
273
+ constructor({
274
+ ed25519PrivKey,
275
+ ed25519PubKey,
276
+ sessionId,
277
+ sodium
278
+ }) {
279
+ this.ed25519PubKey = ed25519PubKey;
280
+ if (this.ed25519PubKey.length !== 64) {
281
+ console.warn("ed25519PubKey length", ed25519PubKey.length);
282
+ throw new Error("ed25519PubKey not 64 long");
283
+ }
284
+ this.ed25519PrivKey = ed25519PrivKey;
285
+ if (this.ed25519PrivKey.length !== 64) {
286
+ console.warn("ed25519PrivKey length", ed25519PrivKey.length);
287
+ throw new Error("ed25519PrivKey not 64 long");
288
+ }
289
+ this.sessionId = sessionId;
290
+ this.sodium = sodium;
291
+ }
292
+ async getSnodeSignatureParams({
293
+ method,
294
+ namespace,
295
+ getNow
296
+ }) {
297
+ if (!this.ed25519PrivKey || !this.ed25519PubKey) {
298
+ const err = `getSnodeSignatureParams "${method}": User has no getUserED25519KeyPairBytes()`;
299
+ throw new Error(err);
300
+ }
301
+ const sigData = await getSnodeSignatureShared({
302
+ pubKey: this.sessionId,
303
+ method,
304
+ namespace,
305
+ privKey: this.ed25519PrivKey,
306
+ getNow,
307
+ sodium: this.sodium
308
+ });
309
+ return {
310
+ ...sigData,
311
+ pubkey_ed25519: this.ed25519PubKey,
312
+ pubkey: this.sessionId
313
+ };
314
+ }
315
+ };
316
+
317
+ // src/sessionUser.ts
318
+ function buildUserSigner(user, sodium) {
319
+ const userSigner = new UserSigner({
320
+ sessionId: user.sessionId,
321
+ ed25519PrivKey: user.ed25519Sk,
322
+ ed25519PubKey: sodium.to_hex(user.ed25519Pk),
323
+ sodium
324
+ });
325
+ return userSigner;
326
+ }
327
+ function generateMnemonic(opts) {
328
+ const seedSize = 16;
329
+ const seed = opts.sodium.randombytes_buf(seedSize);
330
+ const hex = opts.sodium.to_hex(seed);
331
+ return (0, import_mnemonic.mnEncode)(hex);
332
+ }
333
+ function mnemonicToRawSeed(mnemonic, sodium) {
334
+ let seedHex = (0, import_mnemonic.mnDecode)(mnemonic);
335
+ const privKeyHexLength = 32 * 2;
336
+ if (seedHex.length !== privKeyHexLength) {
337
+ seedHex = seedHex.concat("0".repeat(32));
338
+ seedHex = seedHex.substring(0, privKeyHexLength);
339
+ }
340
+ const seed = sodium.from_hex(seedHex);
341
+ return seed;
342
+ }
343
+ function sessionGenerateKeyPair(opts) {
344
+ const ed25519KeyPair = opts.sodium.crypto_sign_seed_keypair(
345
+ new Uint8Array(opts.seed)
346
+ );
347
+ const x25519PublicKey = opts.sodium.crypto_sign_ed25519_pk_to_curve25519(
348
+ ed25519KeyPair.publicKey
349
+ );
350
+ const origPub = new Uint8Array(x25519PublicKey);
351
+ const prependedX25519PublicKey = new Uint8Array(33);
352
+ prependedX25519PublicKey.set(origPub, 1);
353
+ prependedX25519PublicKey[0] = 5;
354
+ const x25519SecretKey = opts.sodium.crypto_sign_ed25519_sk_to_curve25519(
355
+ ed25519KeyPair.privateKey
356
+ );
357
+ return {
358
+ x25519PublicKeyWith05: prependedX25519PublicKey,
359
+ x25519SecretKey: x25519SecretKey.buffer,
360
+ ed25519KeyPair
361
+ };
362
+ }
363
+ var SessionUser = class {
364
+ sessionId;
365
+ ed25519Pk;
366
+ ed25519Sk;
367
+ seed;
368
+ seedPhrase;
369
+ wrappers;
370
+ userProfile;
371
+ contacts;
372
+ userGroups;
373
+ userSigner;
374
+ sodium;
375
+ constructor({ sessionTools, sodium }) {
376
+ const mnemonic = generateMnemonic({ sodium });
377
+ const seed = mnemonicToRawSeed(mnemonic, sodium);
378
+ const userKeys = sessionGenerateKeyPair({ seed, sodium });
379
+ const userProfile = new sessionTools.UserProfileW(
380
+ userKeys.ed25519KeyPair.privateKey,
381
+ void 0
382
+ );
383
+ const contacts = new sessionTools.ContactsW(
384
+ userKeys.ed25519KeyPair.privateKey,
385
+ void 0
386
+ );
387
+ const userGroups = new sessionTools.UserGroupsW(
388
+ userKeys.ed25519KeyPair.privateKey,
389
+ void 0
390
+ );
391
+ const wrappers = [userProfile, contacts, userGroups];
392
+ this.sessionId = sodium.to_hex(
393
+ userKeys.x25519PublicKeyWith05
394
+ );
395
+ this.ed25519Pk = userKeys.ed25519KeyPair.publicKey;
396
+ this.ed25519Sk = userKeys.ed25519KeyPair.privateKey;
397
+ this.seed = seed;
398
+ this.seedPhrase = (0, import_mnemonic.mnEncode)(sodium.to_hex(seed).slice(0, 32));
399
+ this.wrappers = wrappers;
400
+ this.userProfile = userProfile;
401
+ this.contacts = contacts;
402
+ this.userGroups = userGroups;
403
+ this.sodium = sodium;
404
+ this.userSigner = buildUserSigner(this, this.sodium);
405
+ }
406
+ async pushChangesToSwarm(snode) {
407
+ const storeRequests = this.wrappers.map(
408
+ (wrapper) => new StoreUserConfigSubRequest({
409
+ namespace: wrapper.storageNamespace().value,
410
+ encryptedData: this.sodium.from_hex(
411
+ wrapper.makePushHex().get(0)?.toString() ?? ""
412
+ ),
413
+ sessionId: this.sessionId,
414
+ ttlMs: 1e3 * 3600 * 24,
415
+ // 1 day should be enough for testing and debugging a test?
416
+ userSigner: this.userSigner
417
+ })
418
+ );
419
+ const storeResult = await Promise.all(
420
+ storeRequests.map(async (request) => {
421
+ const builtRequest = await request.build();
422
+ console.info(
423
+ "storing to snode",
424
+ `https://${snode.ip}:${snode.port}/storage_rpc/v1`
425
+ );
426
+ const ret = await fetch(
427
+ `https://${snode.ip}:${snode.port}/storage_rpc/v1`,
428
+ {
429
+ body: JSON.stringify(builtRequest),
430
+ method: "POST"
431
+ }
432
+ );
433
+ return ret.status;
434
+ })
435
+ );
436
+ console.log(`storeStatus for ${this.userProfile.getName()}:`, storeResult);
437
+ }
438
+ freeMemory() {
439
+ this.wrappers.map((wrapper) => wrapper.delete());
440
+ }
441
+ toString() {
442
+ const name = this.userProfile.getName();
443
+ const sessionId = this.sessionId;
444
+ const seedPhrase = this.seedPhrase;
445
+ return `SessionUser: ${JSON.stringify({ name, sessionId, seedPhrase })}`;
446
+ }
447
+ };
448
+ function createRandomUser(details) {
449
+ return new SessionUser(details);
450
+ }
451
+
452
+ // src/signer/groupSigner.ts
453
+ var import_sodium2 = require("@session-foundation/sodium");
454
+ function getVerificationDataForStoreRetrieve2(params) {
455
+ const signatureTimestamp = params.getNow();
456
+ const verificationString = `${params.method}${params.namespace === 0 ? "" : params.namespace}${signatureTimestamp}`;
457
+ const verificationData = params.sodium.from_string(verificationString);
458
+ return {
459
+ toSign: new Uint8Array(verificationData),
460
+ signatureTimestamp
461
+ };
462
+ }
463
+ async function getSnodeSignatureShared2(params) {
464
+ const { signatureTimestamp, toSign } = getVerificationDataForStoreRetrieve2(params);
465
+ const sodium = await (0, import_sodium2.getSodium)();
466
+ const signature = sodium.crypto_sign_detached(toSign, params.privKey);
467
+ const signatureBase64 = params.sodium.to_base64(
468
+ signature,
469
+ params.sodium.base64_variants.ORIGINAL
470
+ );
471
+ return {
472
+ timestamp: signatureTimestamp,
473
+ signature: signatureBase64
474
+ };
475
+ }
476
+ var GroupAdminSigner = class {
477
+ groupPk;
478
+ sodium;
479
+ groupSecretKey;
480
+ constructor({
481
+ groupSecretKey,
482
+ groupPk,
483
+ sodium
484
+ }) {
485
+ this.groupSecretKey = groupSecretKey;
486
+ if (this.groupSecretKey.length !== 64) {
487
+ console.warn("groupSecretKey length", groupSecretKey.length);
488
+ throw new Error("groupSecretKey not 64 long");
489
+ }
490
+ this.groupPk = groupPk;
491
+ this.sodium = sodium;
492
+ }
493
+ async getSnodeSignatureParams({
494
+ method,
495
+ namespace,
496
+ getNow
497
+ }) {
498
+ const sigData = await getSnodeSignatureShared2({
499
+ pubKey: this.groupPk,
500
+ method,
501
+ namespace,
502
+ privKey: this.groupSecretKey,
503
+ getNow,
504
+ sodium: this.sodium
505
+ });
506
+ return {
507
+ ...sigData,
508
+ pubkey: this.groupPk
509
+ };
510
+ }
511
+ };
512
+
513
+ // src/sessionGroup.ts
514
+ var import_lodash3 = require("lodash");
515
+ function buildGroupSigner(group, sodium) {
516
+ if (!group.groupSecretKey) {
517
+ throw new Error(
518
+ "only group admin signer (with admin key) is supported currently"
519
+ );
520
+ }
521
+ return new GroupAdminSigner({
522
+ groupPk: group.groupPk,
523
+ groupSecretKey: group.groupSecretKey,
524
+ sodium
525
+ });
526
+ }
527
+ var SessionGroup = class {
528
+ groupPk;
529
+ groupSecretKey;
530
+ metagroupW;
531
+ adminGroupSigner;
532
+ groupName;
533
+ sodium;
534
+ constructor({
535
+ sessionTools,
536
+ groupName,
537
+ members,
538
+ sodium
539
+ }) {
540
+ if (!members.length) {
541
+ throw new Error("Excepted at least one creator/member");
542
+ }
543
+ this.groupName = groupName;
544
+ const [creator, ...otherMembers] = members;
545
+ if (!creator) {
546
+ throw new Error("Expected at least the creator");
547
+ }
548
+ const newGroup = creator.userGroups.createGroup();
549
+ newGroup.name = groupName;
550
+ newGroup.joinedAtSeconds = BigInt(Math.floor(Date.now() / 1e3));
551
+ this.groupSecretKey = sodium.from_hex(newGroup.adminSecretKey.toString());
552
+ this.metagroupW = new sessionTools.MetaGroupW(
553
+ creator.ed25519Sk,
554
+ sodium.from_hex(newGroup.groupPk.toString().slice(2)).buffer,
555
+ this.groupSecretKey,
556
+ void 0
557
+ );
558
+ [creator, ...otherMembers].forEach((member) => {
559
+ if (member.sessionId !== creator.sessionId) {
560
+ const authDataHex = this.metagroupW.makeSubaccountHex(
561
+ member.sessionId,
562
+ true,
563
+ false
564
+ );
565
+ const groupForUser = member.userGroups.getOrConstructGroup(
566
+ newGroup.groupPk
567
+ );
568
+ groupForUser.name = newGroup.name;
569
+ groupForUser.joinedAtSeconds = newGroup.joinedAtSeconds;
570
+ groupForUser.invited = false;
571
+ groupForUser.priority = 0;
572
+ groupForUser.authData = sodium.from_hex(authDataHex);
573
+ member.userGroups.setGroup(groupForUser);
574
+ } else {
575
+ member.userGroups.setGroup(newGroup);
576
+ }
577
+ });
578
+ this.metagroupW.setNameTruncated(groupName);
579
+ [creator, ...otherMembers].forEach((u) => {
580
+ const member = this.metagroupW.membersGetOrConstruct(u.sessionId);
581
+ if (u.sessionId === creator.sessionId) {
582
+ member.setPromotionAccepted();
583
+ } else {
584
+ member.setInviteAccepted();
585
+ }
586
+ member.setNameTruncated(u.userProfile.getName()?.toString() || "");
587
+ this.metagroupW.membersSet(member);
588
+ });
589
+ this.metagroupW.rekeyHex();
590
+ this.groupPk = newGroup.groupPk.toString();
591
+ this.sodium = sodium;
592
+ this.adminGroupSigner = buildGroupSigner(this, this.sodium);
593
+ }
594
+ async pushChangesToSwarm(snode) {
595
+ const pushHex = this.metagroupW.pushHex();
596
+ const toPush = (0, import_lodash3.compact)([
597
+ pushHex.keysPush,
598
+ pushHex.membersPush,
599
+ pushHex.infosPush
600
+ ]);
601
+ const storeRequests = toPush.map((m) => {
602
+ return new StoreGroupConfigSubRequest({
603
+ namespace: m.storageNamespace.value,
604
+ encryptedData: this.sodium.from_hex(m.dataHex.toString()),
605
+ groupPk: this.groupPk,
606
+ ttlMs: 1e3 * 3600 * 24,
607
+ // 1 day should be enough for testing and debugging a test?
608
+ adminGroupSigner: this.adminGroupSigner
609
+ });
610
+ });
611
+ const storeResult = await Promise.all(
612
+ storeRequests.map(async (request) => {
613
+ const builtRequest = await request.build();
614
+ console.info(
615
+ "storing to snode",
616
+ `https://${snode.ip}:${snode.port}/storage_rpc/v1`
617
+ );
618
+ const ret = await fetch(
619
+ `https://${snode.ip}:${snode.port}/storage_rpc/v1`,
620
+ {
621
+ body: JSON.stringify(builtRequest),
622
+ method: "POST"
623
+ }
624
+ );
625
+ return ret.status;
626
+ })
627
+ );
628
+ console.log(`storeStatus for (group) ${this.groupPk}:`, storeResult);
629
+ }
630
+ freeMemory() {
631
+ this.metagroupW.delete();
632
+ }
633
+ toString() {
634
+ const groupName = this.groupName;
635
+ const groupPk = this.groupPk;
636
+ const groupSecretKeyHex = this.sodium.to_hex(this.groupSecretKey);
637
+ return `SessionGroup: ${JSON.stringify({ groupPk, groupName, groupSecretKeyHex })}`;
638
+ }
639
+ };
640
+
641
+ // src/index.ts
642
+ var import_basic_types = require("@session-foundation/basic-types");
643
+ var import_sodium3 = require("@session-foundation/sodium");
644
+
645
+ // src/actions/fetchSwarmOf.ts
646
+ var import_lodash4 = require("lodash");
647
+ var fetchedSwarms = {};
648
+ async function getSwarmOfUser(sessionId, snode) {
649
+ const swarmRequest = new SwarmForSubRequest(sessionId);
650
+ const swarmResult = await fetch(
651
+ `https://${snode.public_ip}:${snode.storage_port}/storage_rpc/v1`,
652
+ {
653
+ body: JSON.stringify(await swarmRequest.build()),
654
+ method: "POST"
655
+ }
656
+ );
657
+ const swarm = await swarmResult.json();
658
+ if ((0, import_lodash4.isEmpty)(fetchedSwarms[sessionId])) {
659
+ fetchedSwarms[sessionId] = swarm;
660
+ }
661
+ return swarm.snodes;
662
+ }
663
+ async function randomSnodeOnUserSwarm(sessionId, snode) {
664
+ const userSwarm = fetchedSwarms[sessionId] || await getSwarmOfUser(sessionId, snode);
665
+ const randomSnodeOnSwarm = (0, import_lodash4.sample)(userSwarm);
666
+ if (!randomSnodeOnSwarm) {
667
+ throw new Error(`did not find a snode for user: ${sessionId}`);
668
+ }
669
+ console.info(
670
+ `random snode for user: ${sessionId} is snode: ${randomSnodeOnSwarm.pubkey_ed25519}`
671
+ );
672
+ return randomSnodeOnSwarm;
673
+ }
674
+
675
+ // src/index.ts
676
+ var networks = {
677
+ mainnet: "https://seed2.getsession.org:4443",
678
+ testnet: "http://seed2.getsession.org:38157"
679
+ };
680
+ function getSeedNodeUrl(network) {
681
+ if (network === "mainnet" || network === "testnet") {
682
+ return networks[network];
683
+ }
684
+ return network;
685
+ }
686
+ function makeFriendsAndKnown(users) {
687
+ if (users.length < 2) {
688
+ throw new Error("needs at least two users to make them friends");
689
+ }
690
+ console.info(
691
+ `makeFriendsAndKnown: users: [${users.map((m) => m.toString()).join(",\n")}]`
692
+ );
693
+ users.forEach((user1) => {
694
+ users.forEach((user2) => {
695
+ if (user1.sessionId === user2.sessionId) {
696
+ console.info(
697
+ "makeFriendsAndKnown: user1 === user2. Skipping",
698
+ user1.toString()
699
+ );
700
+ return;
701
+ }
702
+ console.info("makeFriendsAndKnown: user1", user1.toString());
703
+ if (user2) {
704
+ console.info("makeFriendsAndKnown: user2", user2.toString());
705
+ user1.contacts.setApproved(user2.sessionId, true);
706
+ user1.contacts.setApprovedMe(user2.sessionId, true);
707
+ user1.contacts.setName(
708
+ user2.sessionId,
709
+ user2.userProfile.getName() || ""
710
+ );
711
+ user2.contacts.setApproved(user1.sessionId, true);
712
+ user2.contacts.setApprovedMe(user1.sessionId, true);
713
+ user2.contacts.setName(
714
+ user1.sessionId,
715
+ user1.userProfile.getName() || ""
716
+ );
717
+ }
718
+ });
719
+ });
720
+ }
721
+ function makeGroupWithMembers({
722
+ members,
723
+ groupName,
724
+ sessionTools,
725
+ sodium
726
+ }) {
727
+ return new SessionGroup({ sessionTools, groupName, members, sodium });
728
+ }
729
+ var PrebuiltStateWithWrappers = class {
730
+ userWrappers;
731
+ groupWrapper;
732
+ constructor(args) {
733
+ this.userWrappers = args.users;
734
+ this.groupWrapper = args.group;
735
+ }
736
+ async pushChangesToSwarms({
737
+ seedNodeUrl
738
+ }) {
739
+ console.info(`Pushing changes to swarms on network "${seedNodeUrl}"`);
740
+ const promises = [];
741
+ if (this.userWrappers && (0, import_lodash5.isArray)(this.userWrappers) && this.userWrappers?.length) {
742
+ const users = this.userWrappers;
743
+ const userPromise = pushUsersChangesToSwarm({ seedNodeUrl, users });
744
+ promises.push(userPromise);
745
+ }
746
+ if (this.groupWrapper && this.groupWrapper instanceof SessionGroup) {
747
+ const randomNetworkSnode = await randomSnodeFromNetwork(seedNodeUrl);
748
+ const groupWrapper = this.groupWrapper;
749
+ const swarmSnode = await randomSnodeOnUserSwarm(
750
+ groupWrapper.groupPk,
751
+ randomNetworkSnode
752
+ );
753
+ const groupPromise = groupWrapper.pushChangesToSwarm(swarmSnode);
754
+ promises.push(groupPromise);
755
+ }
756
+ console.info(`promises`, promises);
757
+ await Promise.all(promises);
758
+ console.info(`Pushed changes to swarms on network "${seedNodeUrl}"`);
759
+ }
760
+ };
761
+ var usernames = ["Alice", "Bob", "Charlie", "Dracula"];
762
+ var USERNAME = {
763
+ ALICE: "Alice",
764
+ BOB: "Bob",
765
+ CHARLIE: "Charlie",
766
+ DRACULA: "Dracula"
767
+ };
768
+ function assertUserCountIsValid(count) {
769
+ if (count > usernames.length) {
770
+ throw new Error(`count should be less than ${usernames.length} currently`);
771
+ }
772
+ if (count < 1) {
773
+ throw new Error("count should be at least 1");
774
+ }
775
+ }
776
+ function createCountOfUsers({
777
+ count,
778
+ sessionTools,
779
+ sodium
780
+ }) {
781
+ return Array.from(
782
+ { length: count },
783
+ () => createRandomUser({ sodium, sessionTools })
784
+ );
785
+ }
786
+ function assignNamesToUsers(users) {
787
+ users.forEach((user, index) => {
788
+ const name = usernames[index];
789
+ if (!name) {
790
+ throw new Error(
791
+ `assignNamesToUsers: username at index ${index} is empty`
792
+ );
793
+ }
794
+ user.userProfile.setName(name);
795
+ return user;
796
+ });
797
+ }
798
+ function createGroupWithMembers({
799
+ groupName,
800
+ users,
801
+ sodium,
802
+ sessionTools
803
+ }) {
804
+ if (!groupName) {
805
+ throw new Error("groupName should be provided");
806
+ }
807
+ const group = makeGroupWithMembers({
808
+ groupName,
809
+ members: users,
810
+ sessionTools,
811
+ sodium
812
+ });
813
+ console.log("creatorMetagroupW info:", group.metagroupW.makeInfoDumpHex());
814
+ console.log(
815
+ "creatorMetagroupW members:",
816
+ group.metagroupW.makeMembersDumpHex()
817
+ );
818
+ console.log("creatorMetagroupW keys:", group.metagroupW.makeKeysDumpHex());
819
+ return {
820
+ group
821
+ };
822
+ }
823
+ async function pushUsersChangesToSwarm({
824
+ users,
825
+ seedNodeUrl
826
+ }) {
827
+ await Promise.all(
828
+ users.map(async (user) => {
829
+ const randomNetworkSnode = await randomSnodeFromNetwork(seedNodeUrl);
830
+ const swarmSnode = await randomSnodeOnUserSwarm(
831
+ user.sessionId,
832
+ randomNetworkSnode
833
+ );
834
+ await user.pushChangesToSwarm(swarmSnode);
835
+ })
836
+ );
837
+ console.info(
838
+ `seed of users:
839
+ ${users.map((u) => `"${u.userProfile.getName()}": "${u.seedPhrase}"`).join("\n ")} `
840
+ );
841
+ }
842
+ function toStateUsers(users) {
843
+ return users.map((user, index) => {
844
+ const userName = user.userProfile.getName()?.toString();
845
+ if (!userName) {
846
+ throw new Error(`userName should be defined for user at index: ${index}`);
847
+ }
848
+ return {
849
+ seed: user.seed,
850
+ seedPhrase: user.seedPhrase,
851
+ sessionId: user.sessionId,
852
+ userName
853
+ };
854
+ });
855
+ }
856
+ function toStateGroup(group) {
857
+ if (!group.groupSecretKey || (0, import_lodash5.isEmpty)(group.groupSecretKey)) {
858
+ throw new Error("groupSecretKey should be defined");
859
+ }
860
+ return {
861
+ groupPk: group.groupPk,
862
+ groupName: group.groupName,
863
+ adminSecretKey: group.groupSecretKey
864
+ };
865
+ }
866
+ function prepareUsers({
867
+ sessionTools,
868
+ sodium,
869
+ usersCount
870
+ }) {
871
+ const users = createCountOfUsers({
872
+ count: usersCount,
873
+ sodium,
874
+ sessionTools
875
+ });
876
+ assignNamesToUsers(users);
877
+ return users;
878
+ }
879
+ function prepareFriends({
880
+ friendsCount,
881
+ sessionTools,
882
+ sodium
883
+ }) {
884
+ assertUserCountIsValid(friendsCount);
885
+ const users = prepareUsers({
886
+ usersCount: friendsCount,
887
+ sessionTools,
888
+ sodium
889
+ });
890
+ makeFriendsAndKnown(users);
891
+ return {
892
+ stateUsers: toStateUsers(users),
893
+ usersWrapper: users
894
+ };
895
+ }
896
+ function prepareFriendsInGroup({
897
+ friendsCount,
898
+ groupName,
899
+ sessionTools,
900
+ sodium
901
+ }) {
902
+ if (!groupName) {
903
+ throw new Error("groupName should be provided");
904
+ }
905
+ const { stateUsers, usersWrapper } = prepareFriends({
906
+ friendsCount,
907
+ sessionTools,
908
+ sodium
909
+ });
910
+ const { group } = createGroupWithMembers({
911
+ groupName,
912
+ users: usersWrapper,
913
+ sodium,
914
+ sessionTools
915
+ });
916
+ return {
917
+ stateUsers,
918
+ usersWrapper,
919
+ stateGroup: toStateGroup(group),
920
+ groupWrapper: group
921
+ };
922
+ }
923
+ async function buildStateForTest(stateToBuild, groupName, network) {
924
+ const seedNodeUrl = getSeedNodeUrl(network);
925
+ console.info(
926
+ `[buildStateForTest] stateToBuild:"${stateToBuild}". seedNodeUrl: "${seedNodeUrl}" (${network})`
927
+ );
928
+ const sodium = await (0, import_sodium3.getSodium)();
929
+ const sessionTools = await loadSessionTools();
930
+ switch (stateToBuild) {
931
+ case "none": {
932
+ return { users: {} };
933
+ }
934
+ case "1user": {
935
+ const users = prepareUsers({ usersCount: 1, sessionTools, sodium });
936
+ await new PrebuiltStateWithWrappers({
937
+ users,
938
+ group: null
939
+ }).pushChangesToSwarms({ seedNodeUrl });
940
+ return {
941
+ users: toStateUsers(users)
942
+ };
943
+ }
944
+ case "2users": {
945
+ const users = prepareUsers({ usersCount: 2, sessionTools, sodium });
946
+ await new PrebuiltStateWithWrappers({
947
+ users,
948
+ group: null
949
+ }).pushChangesToSwarms({ seedNodeUrl });
950
+ return {
951
+ users: toStateUsers(users)
952
+ };
953
+ }
954
+ case "3users": {
955
+ const users = prepareUsers({ usersCount: 3, sessionTools, sodium });
956
+ await new PrebuiltStateWithWrappers({
957
+ users,
958
+ group: null
959
+ }).pushChangesToSwarms({ seedNodeUrl });
960
+ return {
961
+ users: toStateUsers(users)
962
+ };
963
+ }
964
+ case "2friends": {
965
+ const state = prepareFriends({
966
+ friendsCount: 2,
967
+ sessionTools,
968
+ sodium
969
+ });
970
+ await new PrebuiltStateWithWrappers({
971
+ users: state.usersWrapper,
972
+ group: null
973
+ }).pushChangesToSwarms({ seedNodeUrl });
974
+ return {
975
+ users: state.stateUsers
976
+ };
977
+ }
978
+ case "2friendsInGroup": {
979
+ if (!groupName) {
980
+ throw new Error("state 2friendsInGroup needs a groupName");
981
+ }
982
+ const state = prepareFriendsInGroup({
983
+ friendsCount: 2,
984
+ sessionTools,
985
+ sodium,
986
+ groupName
987
+ });
988
+ await new PrebuiltStateWithWrappers({
989
+ users: state.usersWrapper,
990
+ group: state.groupWrapper
991
+ }).pushChangesToSwarms({ seedNodeUrl });
992
+ return {
993
+ users: state.stateUsers,
994
+ group: state.stateGroup
995
+ };
996
+ }
997
+ case "3friends": {
998
+ const state = prepareFriends({
999
+ friendsCount: 3,
1000
+ sessionTools,
1001
+ sodium
1002
+ });
1003
+ await new PrebuiltStateWithWrappers({
1004
+ users: state.usersWrapper,
1005
+ group: null
1006
+ }).pushChangesToSwarms({ seedNodeUrl });
1007
+ return {
1008
+ users: state.stateUsers
1009
+ };
1010
+ }
1011
+ case "3friendsInGroup": {
1012
+ if (!groupName) {
1013
+ throw new Error("state 3friendsInGroup needs a groupName");
1014
+ }
1015
+ const state = prepareFriendsInGroup({
1016
+ friendsCount: 3,
1017
+ sessionTools,
1018
+ sodium,
1019
+ groupName
1020
+ });
1021
+ await new PrebuiltStateWithWrappers({
1022
+ users: state.usersWrapper,
1023
+ group: state.groupWrapper
1024
+ }).pushChangesToSwarms({ seedNodeUrl });
1025
+ return {
1026
+ users: state.stateUsers,
1027
+ group: state.stateGroup
1028
+ };
1029
+ }
1030
+ default:
1031
+ (0, import_basic_types.assertUnreachable)(
1032
+ stateToBuild,
1033
+ `buildStateForTest of "${stateToBuild}" not implemented`
1034
+ );
1035
+ break;
1036
+ }
1037
+ }
1038
+ // Annotate the CommonJS export names for ESM import in node:
1039
+ 0 && (module.exports = {
1040
+ USERNAME,
1041
+ buildStateForTest,
1042
+ usernames
1043
+ });