mock-ssb.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. const crypto = require('crypto');
  2. const pull = require('../../src/server/node_modules/pull-stream');
  3. const ssbKeys = require('../../src/server/node_modules/ssb-keys');
  4. let pullPushable;
  5. try {
  6. pullPushable = require('../../src/server/node_modules/pull-pushable');
  7. } catch (_) {
  8. try { pullPushable = require('../../src/server/node_modules/@krakenslab/pull-pushable'); } catch (__) {}
  9. }
  10. const generateMsgKey = () => '%' + crypto.randomBytes(32).toString('base64').replace(/=+$/, '') + '.sha256';
  11. function makeNetwork() {
  12. const log = [];
  13. const liveListeners = new Set();
  14. return {
  15. log,
  16. publish(msg) {
  17. log.push(msg);
  18. for (const cb of liveListeners) {
  19. try { cb(msg); } catch (_) {}
  20. }
  21. },
  22. listen(cb) {
  23. liveListeners.add(cb);
  24. return () => liveListeners.delete(cb);
  25. },
  26. reset() {
  27. log.length = 0;
  28. liveListeners.clear();
  29. }
  30. };
  31. }
  32. function makeNode(network, keypair, opts = {}) {
  33. const seqByAuthor = new Map();
  34. const node = {
  35. id: keypair.id,
  36. keys: keypair,
  37. publish(content, cb) {
  38. let actualContent = content;
  39. if (content && typeof content === 'object' && Array.isArray(content.recps) && content.recps.length) {
  40. try {
  41. actualContent = ssbKeys.box(content, content.recps);
  42. } catch (e) {
  43. if (cb) cb(e);
  44. return;
  45. }
  46. }
  47. const key = generateMsgKey();
  48. const prev = seqByAuthor.get(keypair.id) || 0;
  49. const sequence = prev + 1;
  50. seqByAuthor.set(keypair.id, sequence);
  51. const ts = Date.now();
  52. const msg = {
  53. key,
  54. value: {
  55. previous: null,
  56. sequence,
  57. author: keypair.id,
  58. timestamp: ts,
  59. hash: 'sha256',
  60. content: actualContent,
  61. signature: 'mock-sig'
  62. },
  63. timestamp: ts
  64. };
  65. network.publish(msg);
  66. if (cb) cb(null, { key, value: msg.value });
  67. },
  68. createLogStream(opt = {}) {
  69. const { limit, reverse, live, old } = opt;
  70. const items = network.log.slice();
  71. const baseItems = old !== false ? items : [];
  72. let prepared = baseItems;
  73. if (reverse) prepared = prepared.slice().reverse();
  74. if (limit) prepared = prepared.slice(0, limit);
  75. if (!live) return pull.values(prepared);
  76. if (!pullPushable) {
  77. const initial = pull.values(prepared);
  78. return initial;
  79. }
  80. const p = pullPushable();
  81. for (const m of prepared) p.push(m);
  82. const off = network.listen(m => p.push(m));
  83. const origAbort = p.end;
  84. p.end = (err) => { off(); if (origAbort) origAbort.call(p, err); };
  85. return p;
  86. },
  87. createUserStream(opt = {}) {
  88. const { id, reverse, limit } = opt;
  89. let items = network.log.filter(m => m.value && m.value.author === id);
  90. if (reverse) items = items.slice().reverse();
  91. if (limit) items = items.slice(0, limit);
  92. return pull.values(items);
  93. },
  94. get(key, cb) {
  95. const m = network.log.find(x => x.key === key);
  96. if (!m) return cb(new Error('not found'));
  97. cb(null, m.value);
  98. },
  99. private: {
  100. publish(content, recps, cb) {
  101. let actualContent;
  102. try {
  103. actualContent = ssbKeys.box(content, recps);
  104. } catch (e) { if (cb) cb(e); return; }
  105. const key = generateMsgKey();
  106. const prev = seqByAuthor.get(keypair.id) || 0;
  107. const sequence = prev + 1;
  108. seqByAuthor.set(keypair.id, sequence);
  109. const ts = Date.now();
  110. const msg = { key, value: { previous: null, sequence, author: keypair.id, timestamp: ts, hash: 'sha256', content: actualContent, signature: 'mock-sig' }, timestamp: ts };
  111. network.publish(msg);
  112. if (cb) cb(null, { key, value: msg.value });
  113. },
  114. unbox(arg) {
  115. const c = arg && arg.value ? arg.value.content : arg;
  116. if (typeof c !== 'string' || !c.endsWith('.box')) return null;
  117. try {
  118. const decoded = ssbKeys.unbox(c, keypair);
  119. if (!decoded) return null;
  120. if (arg && arg.value) {
  121. return { key: arg.key, value: { ...arg.value, content: decoded }, timestamp: arg.timestamp };
  122. }
  123. return decoded;
  124. } catch (_) { return null; }
  125. }
  126. },
  127. blobs: { has(_url, cb) { cb(null, true); } },
  128. conn: { hub() { return { listen: () => null }; } },
  129. replicate: { upto(cb) { if (cb) cb(null, {}); } },
  130. whoami(cb) { cb(null, { id: keypair.id }); },
  131. links(opts = {}) {
  132. const out = [];
  133. for (const m of network.log) {
  134. const c = m.value && m.value.content;
  135. if (!c) continue;
  136. if (opts.dest && c.target !== opts.dest && c.root !== opts.dest && (!c.branch || (Array.isArray(c.branch) ? !c.branch.includes(opts.dest) : c.branch !== opts.dest))) continue;
  137. if (opts.rel === 'target' && c.target !== opts.dest) continue;
  138. if (opts.values) out.push(m);
  139. else out.push({ source: m.value.author, dest: opts.dest, key: m.key });
  140. }
  141. return pull.values(out);
  142. },
  143. messagesByType(opts = {}) {
  144. const wantedType = typeof opts === 'string' ? opts : opts.type;
  145. const items = network.log.filter(m => {
  146. const c = m.value && m.value.content;
  147. return c && typeof c === 'object' && c.type === wantedType;
  148. });
  149. return pull.values(items);
  150. }
  151. };
  152. return node;
  153. }
  154. function makeCooler(node) {
  155. return { open: async () => node };
  156. }
  157. function generateKeypair() {
  158. return ssbKeys.generate();
  159. }
  160. module.exports = { makeNetwork, makeNode, makeCooler, generateKeypair };