From b3a96dbb6d5e1ff3bd9fc5f6e5b61238310eb1a2 Mon Sep 17 00:00:00 2001 From: Andre Staltz Date: Wed, 13 Sep 2023 19:04:12 +0300 Subject: [PATCH] update to ppppp-db with MsgV3 --- lib/index.js | 246 +++++++++++++++++++++++---------------------- package.json | 19 ++-- test/index.test.js | 144 +++++++++++++------------- test/util.js | 45 +++++++-- tsconfig.json | 16 +++ 5 files changed, 258 insertions(+), 212 deletions(-) create mode 100644 tsconfig.json diff --git a/lib/index.js b/lib/index.js index 722bc9d..1b65345 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,77 +1,71 @@ -const FeedV1 = require('ppppp-db/feed-v1') +const MsgV3 = require('ppppp-db/msg-v3') const PREFIX = 'record_v1__' -/** @typedef {string} Subtype */ - -/** @typedef {string} MsgHash */ - -/** @typedef {`${Subtype}.${string}`} SubtypeField */ +/** + * @typedef {string} Subdomain + * @typedef {string} MsgID + * @typedef {`${Subdomain}.${string}`} SubdomainField + */ /** - * @param {string} type - * @returns {Subtype} + * @param {string} domain + * @returns {Subdomain} */ -function toSubtype(type) { - return type.slice(PREFIX.length) +function toSubdomain(domain) { + return domain.slice(PREFIX.length) } /** - * @param {Subtype} subtype + * @param {Subdomain} subdomain * @returns {string} */ -function fromSubtype(subtype) { - return PREFIX + subtype +function fromSubdomain(subdomain) { + return PREFIX + subdomain } module.exports = { name: 'record', - manifest: { - update: 'async', - get: 'sync', - getFieldRoots: 'sync', - squeeze: 'async', - }, + manifest: {}, init(peer, config) { //#region state - const myWho = FeedV1.stripAuthor(config.keys.id) + let accountID = /** @type {string | null} */ (null) let cancelListeningToRecordAdded = null - - /** @type {Map} */ - const tangles = new Map() + let loadPromise = /** @type {Promise | null} */ (null) + const tangles = /** @type {Map} */ (new Map()) const fieldRoots = { - /** @type {Map} */ + /** @type {Map} */ _map: new Map(), - _getKey(subtype, field) { - return subtype + '.' + field + _getKey(subdomain, field) { + return subdomain + '.' + field }, - get(subtype, field = null) { + get(subdomain, field = null) { if (field) { - const key = this._getKey(subtype, field) + const key = this._getKey(subdomain, field) return this._map.get(key) } else { const out = {} for (const [key, value] of this._map.entries()) { - if (key.startsWith(subtype + '.')) { - const field = key.slice(subtype.length + 1) + if (key.startsWith(subdomain + '.')) { + const field = key.slice(subdomain.length + 1) out[field] = [...value] } } return out } }, - add(subtype, field, msgHash) { - const key = this._getKey(subtype, field) + add(subdomain, field, msgID) { + const key = this._getKey(subdomain, field) const set = this._map.get(key) ?? new Set() - set.add(msgHash) + set.add(msgID) return this._map.set(key, set) }, - del(subtype, field, msgHash) { - const key = this._getKey(subtype, field) + del(subdomain, field, msgID) { + const key = this._getKey(subdomain, field) const set = this._map.get(key) if (!set) return false - set.delete(msgHash) + set.delete(msgID) if (set.size === 0) this._map.delete(key) return true }, @@ -82,16 +76,6 @@ module.exports = { //#endregion //#region active processes - const loadPromise = new Promise((resolve, reject) => { - for (const { hash, msg } of peer.db.records()) { - maybeLearnAboutRecord(hash, msg) - } - cancelListeningToRecordAdded = peer.db.onRecordAdded(({ hash, msg }) => { - maybeLearnAboutRecord(hash, msg) - }) - resolve() - }) - peer.close.hook(function (fn, args) { cancelListeningToRecordAdded() fn.apply(this, args) @@ -101,65 +85,64 @@ module.exports = { //#region internal methods function isValidRecordRootMsg(msg) { if (!msg) return false - if (msg.metadata.who !== myWho) return false - const type = msg.metadata.type - if (!type.startsWith(PREFIX)) return false - return FeedV1.isFeedRoot(msg, config.keys.id, type) + if (msg.metadata.account !== accountID) return false + const domain = msg.metadata.domain + if (!domain.startsWith(PREFIX)) return false + return MsgV3.isMoot(msg, accountID, domain) } function isValidRecordMsg(msg) { if (!msg) return false - if (!msg.content) return false - if (msg.metadata.who !== myWho) return false - if (!msg.metadata.type.startsWith(PREFIX)) return false - if (!msg.content.update) return false - if (typeof msg.content.update !== 'object') return false - if (Array.isArray(msg.content.update)) return false - if (!Array.isArray(msg.content.supersedes)) return false + if (!msg.data) return false + if (msg.metadata.account !== accountID) return false + if (!msg.metadata.domain.startsWith(PREFIX)) return false + if (!msg.data.update) return false + if (typeof msg.data.update !== 'object') return false + if (Array.isArray(msg.data.update)) return false + if (!Array.isArray(msg.data.supersedes)) return false return true } - function learnRecordRoot(hash, msg) { - const { type } = msg.metadata - const subtype = toSubtype(type) - const tangle = tangles.get(subtype) ?? new FeedV1.Tangle(hash) - tangle.add(hash, msg) - tangles.set(subtype, tangle) + function learnRecordRoot(rootID, root) { + const subdomain = toSubdomain(root.metadata.domain) + const tangle = tangles.get(subdomain) ?? new MsgV3.Tangle(rootID) + tangle.add(rootID, root) + tangles.set(subdomain, tangle) } - function learnRecordUpdate(hash, msg) { - const { who, type } = msg.metadata - const rootHash = FeedV1.getFeedRootHash(who, type) - const subtype = toSubtype(type) - const tangle = tangles.get(subtype) ?? new FeedV1.Tangle(rootHash) - tangle.add(hash, msg) - tangles.set(subtype, tangle) + function learnRecordUpdate(msgID, msg) { + const { account, domain } = msg.metadata + const rootID = MsgV3.getMootID(account, domain) + const subdomain = toSubdomain(domain) + const tangle = tangles.get(subdomain) ?? new MsgV3.Tangle(rootID) + tangle.add(msgID, msg) + tangles.set(subdomain, tangle) - for (const field in msg.content.update) { - const existing = fieldRoots.get(subtype, field) + for (const field in msg.data.update) { + const existing = fieldRoots.get(subdomain, field) if (!existing) { - fieldRoots.add(subtype, field, hash) + fieldRoots.add(subdomain, field, msgID) } else { - for (const existingHash of existing) { - if (tangle.precedes(existingHash, hash)) { - fieldRoots.del(subtype, field, existingHash) - fieldRoots.add(subtype, field, hash) + for (const existingID of existing) { + if (tangle.precedes(existingID, msgID)) { + fieldRoots.del(subdomain, field, existingID) + fieldRoots.add(subdomain, field, msgID) } else { - fieldRoots.add(subtype, field, hash) + fieldRoots.add(subdomain, field, msgID) } } } } } - function maybeLearnAboutRecord(hash, msg) { - if (msg.metadata.who !== myWho) return + function maybeLearnAboutRecord(msgID, msg) { + if (msg.metadata.account !== accountID) return if (isValidRecordRootMsg(msg)) { - learnRecordRoot(hash, msg) + learnRecordRoot(msgID, msg) return } if (isValidRecordMsg(msg)) { - learnRecordUpdate(hash, msg) + learnRecordUpdate(msgID, msg) return } } @@ -169,73 +152,92 @@ module.exports = { else loadPromise.then(() => cb(null), cb) } - function _squeezePotential(subtype) { + function _squeezePotential(subdomain) { // TODO: improve this so that the squeezePotential is the size of the // tangle suffix built as a slice from the fieldRoots - const rootHash = FeedV1.getFeedRootHash(myWho, fromSubtype(subtype)) - const tangle = peer.db.getTangle(rootHash) - const maxDepth = tangle.getMaxDepth() - const fieldRoots = getFieldRoots(myWho, subtype) + const mootID = MsgV3.getMootID(accountID, fromSubdomain(subdomain)) + const tangle = peer.db.getTangle(mootID) + const maxDepth = tangle.maxDepth + const fieldRoots = getFieldRoots(accountID, subdomain) let minDepth = Infinity for (const field in fieldRoots) { - for (const msgHash of fieldRoots[field]) { - const depth = tangle.getDepth(msgHash) + for (const msgID of fieldRoots[field]) { + const depth = tangle.getDepth(msgID) if (depth < minDepth) minDepth = depth } } return maxDepth - minDepth } - function forceUpdate(subtype, update, cb) { - const type = fromSubtype(subtype) + function forceUpdate(subdomain, update, cb) { + const domain = fromSubdomain(subdomain) // Populate supersedes const supersedes = [] for (const field in update) { - const existing = fieldRoots.get(subtype, field) + const existing = fieldRoots.get(subdomain, field) if (existing) supersedes.push(...existing) } - peer.db.create({ type, content: { update, supersedes } }, (err, rec) => { - // prettier-ignore - if (err) return cb(new Error('Failed to create msg when force updating Record', { cause: err })) - cb(null, true) - }) + peer.db.feed.publish( + { account: accountID, domain, data: { update, supersedes } }, + (err, rec) => { + // prettier-ignore + if (err) return cb(new Error('Failed to create msg when force updating Record', { cause: err })) + cb(null, true) + } + ) } //#endregion //#region public methods - function getFieldRoots(authorId, subtype) { - const who = FeedV1.stripAuthor(authorId) - // prettier-ignore - if (who !== myWho) return cb(new Error('Cannot getFieldRoots for another user\'s record. Given "authorId" was ' + authorId)) - return fieldRoots.get(subtype) + + /** + * @param {string} id + */ + function load(id, cb) { + accountID = id + loadPromise = new Promise((resolve, reject) => { + for (const { id, msg } of peer.db.records()) { + maybeLearnAboutRecord(id, msg) + } + cancelListeningToRecordAdded = peer.db.onRecordAdded(({ id, msg }) => { + maybeLearnAboutRecord(id, msg) + }) + resolve() + cb() + }) } - function get(authorId, subtype) { - const type = fromSubtype(subtype) - const rootHash = FeedV1.getFeedRootHash(authorId, type) - const tangle = peer.db.getTangle(rootHash) - if (!tangle || tangle.size() === 0) return {} - const msgHashes = tangle.topoSort() + function getFieldRoots(id, subdomain) { + // prettier-ignore + if (id !== accountID) return cb(new Error(`Cannot getFieldRoots for another user's record. Given ID was "${id}"`)) + return fieldRoots.get(subdomain) + } + + function get(id, subdomain) { + const domain = fromSubdomain(subdomain) + const mootID = MsgV3.getMootID(id, domain) + const tangle = peer.db.getTangle(mootID) + if (!tangle || tangle.size === 0) return {} + const msgIDs = tangle.topoSort() const record = {} - for (const msgHash of msgHashes) { - const msg = peer.db.get(msgHash) + for (const msgID of msgIDs) { + const msg = peer.db.get(msgID) if (isValidRecordMsg(msg)) { - const { update } = msg.content + const { update } = msg.data Object.assign(record, update) } } return record } - function update(authorId, subtype, update, cb) { - const who = FeedV1.stripAuthor(authorId) + function update(id, subdomain, update, cb) { // prettier-ignore - if (who !== myWho) return cb(new Error('Cannot update another user\'s record. Given "authorId" was ' + authorId)) + if (id !== accountID) return cb(new Error(`Cannot update another user's record. Given ID was "${id}"`)) loaded(() => { - const record = get(authorId, subtype) + const record = get(id, subdomain) let hasChanges = false for (const [field, value] of Object.entries(update)) { @@ -245,20 +247,19 @@ module.exports = { } } if (!hasChanges) return cb(null, false) - forceUpdate(subtype, update, cb) + forceUpdate(subdomain, update, cb) }) } - function squeeze(authorId, subtype, cb) { - const who = FeedV1.stripAuthor(authorId) + function squeeze(id, subdomain, cb) { // prettier-ignore - if (who !== myWho) return cb(new Error('Cannot squeeze another user\'s record. Given "authorId" was ' + authorId)) - const potential = _squeezePotential(subtype) + if (id !== accountID) return cb(new Error(`Cannot squeeze another user's record. Given ID was "${id}"`)) + const potential = _squeezePotential(subdomain) if (potential < 1) return cb(null, false) loaded(() => { - const record = get(authorId, subtype) - forceUpdate(subtype, record, (err) => { + const record = get(id, subdomain) + forceUpdate(subdomain, record, (err) => { // prettier-ignore if (err) return cb(new Error('Failed to force update when squeezing Record', { cause: err })) cb(null, true) @@ -268,6 +269,7 @@ module.exports = { //#endregion return { + load, update, get, getFieldRoots, diff --git a/package.json b/package.json index 136fd4b..7d983a7 100644 --- a/package.json +++ b/package.json @@ -28,19 +28,20 @@ "devDependencies": { "bs58": "^5.0.0", "c8": "7", - "ppppp-db": "github:staltz/ppppp-db#rev1", + "ppppp-db": "github:staltz/ppppp-db", + "ppppp-caps": "github:staltz/ppppp-caps", + "ppppp-keypair": "github:staltz/ppppp-keypair", "rimraf": "^4.4.0", - "secret-stack": "^6.4.1", + "secret-stack": "~7.1.0", + "secret-handshake-ext": "^0.0.8", "ssb-box": "^1.0.1", - "ssb-caps": "^1.1.0", - "ssb-classic": "^1.1.0", - "ssb-keys": "^8.5.0", - "ssb-uri2": "^2.4.1", - "tap-arc": "^0.3.5", - "tape": "^5.6.3" + "typescript": "^5.1.3" }, "scripts": { - "test": "tape test/*.js | tap-arc --bail", + "clean-check": "tsc --build --clean", + "prepublishOnly": "npm run clean-check && tsc --build", + "postpublish": "npm run clean-check", + "test": "npm run clean-check && node --test", "format-code": "prettier --write \"(lib|test)/**/*.js\"", "format-code-staged": "pretty-quick --staged --pattern \"(lib|test)/**/*.js\"", "coverage": "c8 --reporter=lcov npm run test" diff --git a/test/index.test.js b/test/index.test.js index ee27689..9d7cd26 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,145 +1,149 @@ -const test = require('tape') +const test = require('node:test') +const assert = require('node:assert') const path = require('path') const os = require('os') const rimraf = require('rimraf') -const SecretStack = require('secret-stack') -const FeedV1 = require('ppppp-db/feed-v1') -const caps = require('ssb-caps') +const MsgV3 = require('ppppp-db/msg-v3') +const Keypair = require('ppppp-keypair') const p = require('util').promisify -const { generateKeypair } = require('./util') +const { createPeer } = require('./util') const DIR = path.join(os.tmpdir(), 'ppppp-record') rimraf.sync(DIR) -const aliceKeys = generateKeypair('alice') -const who = aliceKeys.id +const aliceKeypair = Keypair.generate('ed25519', 'alice') let peer +let aliceID test('setup', async (t) => { - peer = SecretStack({ appKey: caps.shs }) - .use(require('ppppp-db')) - .use(require('ssb-box')) - .use(require('../lib')) - .call(null, { - keys: aliceKeys, - path: DIR, - }) + peer = createPeer({ keypair: aliceKeypair, path: DIR }) await peer.db.loaded() + + aliceID = await p(peer.db.account.create)({ + domain: 'account', + _nonce: 'alice', + }) + await p(peer.record.load)(aliceID) }) test('Record update() and get()', async (t) => { - t.ok( - await p(peer.record.update)(who, 'profile', { name: 'alice' }), + assert( + await p(peer.record.update)(aliceID, 'profile', { name: 'alice' }), 'update .name' ) - t.deepEqual(peer.record.get(who, 'profile'), { name: 'alice' }, 'get') + assert.deepEqual(peer.record.get(aliceID, 'profile'), { name: 'alice' }, 'get') - const fieldRoots1 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals(fieldRoots1, { name: ['Pt4YwxksvCLir45Tmw3hXK'] }, 'fieldRoots') + const fieldRoots1 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual(fieldRoots1, { name: ['PbwnLbJS4oninQ1RPCdgRn'] }, 'fieldRoots') - t.ok(await p(peer.record.update)(who, 'profile', { age: 20 }), 'update .age') - t.deepEqual( - peer.record.get(who, 'profile'), + assert(await p(peer.record.update)(aliceID, 'profile', { age: 20 }), 'update .age') + assert.deepEqual( + peer.record.get(aliceID, 'profile'), { name: 'alice', age: 20 }, 'get' ) - const fieldRoots2 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals( + const fieldRoots2 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual( fieldRoots2, - { name: ['Pt4YwxksvCLir45Tmw3hXK'], age: ['XqkG9Uz1eQcxv9R1f3jgKS'] }, + { name: ['PbwnLbJS4oninQ1RPCdgRn'], age: ['9iTTqNabtnXmw4AiZxNMRq'] }, 'fieldRoots' ) - t.false( - await p(peer.record.update)(who, 'profile', { name: 'alice' }), + assert.equal( + await p(peer.record.update)(aliceID, 'profile', { name: 'alice' }), + false, 'redundant update .name' ) - t.deepEqual( - peer.record.get(who, 'profile'), + assert.deepEqual( + peer.record.get(aliceID, 'profile'), { name: 'alice', age: 20 }, 'get' ) - t.true( - await p(peer.record.update)(who, 'profile', { name: 'Alice' }), + assert.equal( + await p(peer.record.update)(aliceID, 'profile', { name: 'Alice' }), + true, 'update .name' ) - t.deepEqual( - peer.record.get(who, 'profile'), + assert.deepEqual( + peer.record.get(aliceID, 'profile'), { name: 'Alice', age: 20 }, 'get' ) - const fieldRoots3 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals( + const fieldRoots3 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual( fieldRoots3, - { name: ['WGDGt1UEGPpRyutfDyC2we'], age: ['XqkG9Uz1eQcxv9R1f3jgKS'] }, + { age: ['9iTTqNabtnXmw4AiZxNMRq'], name: ['M2JhM7TE2KX5T5rfnxBh6M'] }, 'fieldRoots' ) }) test('Record squeeze', async (t) => { - t.ok(await p(peer.record.update)(who, 'profile', { age: 21 }), 'update .age') - t.ok(await p(peer.record.update)(who, 'profile', { age: 22 }), 'update .age') - t.ok(await p(peer.record.update)(who, 'profile', { age: 23 }), 'update .age') + assert(await p(peer.record.update)(aliceID, 'profile', { age: 21 }), 'update .age') + assert(await p(peer.record.update)(aliceID, 'profile', { age: 22 }), 'update .age') + assert(await p(peer.record.update)(aliceID, 'profile', { age: 23 }), 'update .age') - const fieldRoots4 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals( + const fieldRoots4 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual( fieldRoots4, - { name: ['WGDGt1UEGPpRyutfDyC2we'], age: ['6qu5mbLbFPJHCFge7QtU48'] }, + { name: ['M2JhM7TE2KX5T5rfnxBh6M'], age: ['S3xiydrT6Y34Bp1vg6wN7P'] }, 'fieldRoots' ) - t.equals(peer.record._squeezePotential('profile'), 3, 'squeezePotential=3') - t.true(await p(peer.record.squeeze)(who, 'profile'), 'squeezed') + assert.equal(peer.record._squeezePotential('profile'), 3, 'squeezePotential=3') + assert.equal(await p(peer.record.squeeze)(aliceID, 'profile'), true, 'squeezed') - const fieldRoots5 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals( + const fieldRoots5 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual( fieldRoots5, - { name: ['Ba96TjutuuPbdMMvNS4BbL'], age: ['Ba96TjutuuPbdMMvNS4BbL'] }, + { name: ['Y4JkpPCHN8Avtz4VALaAmK'], age: ['Y4JkpPCHN8Avtz4VALaAmK'] }, 'fieldRoots' ) - t.equals(peer.record._squeezePotential('profile'), 0, 'squeezePotential=0') - t.false(await p(peer.record.squeeze)(who, 'profile'), 'squeeze idempotent') + assert.equal(peer.record._squeezePotential('profile'), 0, 'squeezePotential=0') + assert.equal(await p(peer.record.squeeze)(aliceID, 'profile'), false,'squeeze idempotent') - const fieldRoots6 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals(fieldRoots6, fieldRoots5, 'fieldRoots') + const fieldRoots6 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual(fieldRoots6, fieldRoots5, 'fieldRoots') }) test('Record receives old branched update', async (t) => { - const rootMsg = FeedV1.createRoot(aliceKeys, 'record_v1__profile') - const rootHash = FeedV1.getMsgHash(rootMsg) + const moot = MsgV3.createMoot(aliceID, 'record_v1__profile', aliceKeypair) + const mootID = MsgV3.getMsgID(moot) - const tangle = new FeedV1.Tangle(rootHash) - tangle.add(rootHash, rootMsg) + const tangle = new MsgV3.Tangle(mootID) + tangle.add(mootID, moot) + await p(peer.db.add)(moot, mootID) - const msg = FeedV1.create({ - keys: aliceKeys, - type: 'record_v1__profile', - content: { update: { age: 2 }, supersedes: [] }, + const msg = MsgV3.create({ + keypair: aliceKeypair, + domain: 'record_v1__profile', + account: aliceID, + accountTips: [aliceID], + data: { update: { age: 2 }, supersedes: [] }, tangles: { - [rootHash]: tangle, + [mootID]: tangle, }, }) - const rec = await p(peer.db.add)(msg, rootHash) - t.equals(rec.hash, 'JXvFSXE9s1DF77cSu5XUm', 'msg hash') + const rec = await p(peer.db.add)(msg, mootID) + assert.equal(rec.id, 'XZWr3DZFG253awsWXgSkS2', 'msg ID') - const fieldRoots7 = peer.record.getFieldRoots(who, 'profile') - t.deepEquals( + const fieldRoots7 = peer.record.getFieldRoots(aliceID, 'profile') + assert.deepEqual( fieldRoots7, { - name: ['Ba96TjutuuPbdMMvNS4BbL'], - age: ['Ba96TjutuuPbdMMvNS4BbL', rec.hash], + name: ['Y4JkpPCHN8Avtz4VALaAmK'], + age: ['Y4JkpPCHN8Avtz4VALaAmK', rec.id], }, 'fieldRoots' ) - t.equals(peer.record._squeezePotential('profile'), 6, 'squeezePotential=6') + assert.equal(peer.record._squeezePotential('profile'), 6, 'squeezePotential=6') }) -test('teardown', (t) => { - peer.close(true, t.end) +test('teardown', async (t) => { + await p(peer.close)(true) }) diff --git a/test/util.js b/test/util.js index 51feb19..0d8e070 100644 --- a/test/util.js +++ b/test/util.js @@ -1,14 +1,37 @@ -const ssbKeys = require('ssb-keys') -const SSBURI = require('ssb-uri2') -const base58 = require('bs58') +const os = require('node:os') +const path = require('node:path') +const rimraf = require('rimraf') +const caps = require('ppppp-caps') +const Keypair = require('ppppp-keypair') -function generateKeypair(seed) { - const keys = ssbKeys.generate('ed25519', seed, 'buttwoo-v1') - const { data } = SSBURI.decompose(keys.id) - keys.id = `ppppp:feed/v1/${base58.encode(Buffer.from(data, 'base64'))}` - return keys +function createPeer(opts) { + if (opts.name) { + opts.path ??= path.join(os.tmpdir(), 'tanglesync-' + opts.name) + opts.keypair ??= Keypair.generate('ed25519', opts.name) + opts.name = undefined + } + if (!opts.path) throw new Error('need opts.path in createPeer()') + if (!opts.keypair) throw new Error('need opts.keypair in createPeer()') + + rimraf.sync(opts.path) + return require('secret-stack/bare')() + .use(require('secret-stack/plugins/net')) + .use(require('secret-handshake-ext/secret-stack')) + .use(require('ppppp-db')) + .use(require('ssb-box')) + .use(require('../lib')) + .call(null, { + caps, + connections: { + incoming: { + net: [{ scope: 'device', transform: 'shse', port: null }], + }, + outgoing: { + net: [{ transform: 'shse' }], + }, + }, + ...opts, + }) } -module.exports = { - generateKeypair, -} +module.exports = { createPeer } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bd2acd5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": ["lib/**/*.js"], + "exclude": ["coverage/", "node_modules/", "test/"], + "compilerOptions": { + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "exactOptionalPropertyTypes": true, + "forceConsistentCasingInFileNames": true, + "lib": ["es2022", "dom"], + "module": "node16", + "skipLibCheck": true, + "strict": true, + "target": "es2021" + } +} \ No newline at end of file