mirror of https://codeberg.org/pzp/pzp-dict.git
rename this package from record=>dict
This commit is contained in:
parent
edaffa7626
commit
46dafa436e
|
@ -3,6 +3,7 @@ node_modules
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
package-lock.json
|
package-lock.json
|
||||||
coverage
|
coverage
|
||||||
|
**/*.d.ts
|
||||||
*~
|
*~
|
||||||
|
|
||||||
# For misc scripts and experiments:
|
# For misc scripts and experiments:
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
We're not on npm yet. In your package.json, include this as
|
We're not on npm yet. In your package.json, include this as
|
||||||
|
|
||||||
```js
|
```js
|
||||||
"ppppp-record": "github:staltz/ppppp-record"
|
"ppppp-dict": "github:staltz/ppppp-dict"
|
||||||
```
|
```
|
||||||
|
|
86
lib/index.js
86
lib/index.js
|
@ -1,6 +1,6 @@
|
||||||
const MsgV3 = require('ppppp-db/msg-v3')
|
const MsgV3 = require('ppppp-db/msg-v3')
|
||||||
|
|
||||||
const PREFIX = 'record_v1__'
|
const PREFIX = 'dict_v1__'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {ReturnType<import('ppppp-db').init>} PPPPPDB
|
* @typedef {ReturnType<import('ppppp-db').init>} PPPPPDB
|
||||||
|
@ -18,11 +18,13 @@ const PREFIX = 'record_v1__'
|
||||||
* @typedef {string} MsgID
|
* @typedef {string} MsgID
|
||||||
* @typedef {`${Subdomain}.${string}`} SubdomainField
|
* @typedef {`${Subdomain}.${string}`} SubdomainField
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* update: Record<string, any>,
|
* update: {
|
||||||
|
* [field in string]: any
|
||||||
|
* },
|
||||||
* supersedes: Array<MsgID>,
|
* supersedes: Array<MsgID>,
|
||||||
* }} RecordData
|
* }} DictMsgData
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* record?: {
|
* dict?: {
|
||||||
* ghostSpan?: number
|
* ghostSpan?: number
|
||||||
* }
|
* }
|
||||||
* }} Config
|
* }} Config
|
||||||
|
@ -65,18 +67,18 @@ function fromSubdomain(subdomain) {
|
||||||
* @returns {asserts peer is { db: PPPPPDB, close: ClosableHook }}
|
* @returns {asserts peer is { db: PPPPPDB, close: ClosableHook }}
|
||||||
*/
|
*/
|
||||||
function assertDBPlugin(peer) {
|
function assertDBPlugin(peer) {
|
||||||
if (!peer.db) throw new Error('record plugin requires ppppp-db plugin')
|
if (!peer.db) throw new Error('dict plugin requires ppppp-db plugin')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ db: PPPPPDB | null, close: ClosableHook }} peer
|
* @param {{ db: PPPPPDB | null, close: ClosableHook }} peer
|
||||||
* @param {Config} config
|
* @param {Config} config
|
||||||
*/
|
*/
|
||||||
function initRecord(peer, config) {
|
function initDict(peer, config) {
|
||||||
assertDBPlugin(peer)
|
assertDBPlugin(peer)
|
||||||
|
|
||||||
const ghostSpan = config.record?.ghostSpan ?? 32
|
const ghostSpan = config.dict?.ghostSpan ?? 32
|
||||||
if (ghostSpan < 1) throw new Error('config.record.ghostSpan must be >= 0')
|
if (ghostSpan < 1) throw new Error('config.dict.ghostSpan must be >= 0')
|
||||||
|
|
||||||
//#region state
|
//#region state
|
||||||
let accountID = /** @type {string | null} */ (null)
|
let accountID = /** @type {string | null} */ (null)
|
||||||
|
@ -96,10 +98,10 @@ function initRecord(peer, config) {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {string} subdomain
|
* @param {string} subdomain
|
||||||
* @returns {Record<string, Array<MsgID>>}
|
* @returns {{[field in string]: Array<MsgID>}}
|
||||||
*/
|
*/
|
||||||
getAll(subdomain) {
|
getAll(subdomain) {
|
||||||
const out = /** @type {Record<string, Array<MsgID>>} */ ({})
|
const out = /** @type {{[field in string]: Array<MsgID>}} */ ({})
|
||||||
for (const [key, value] of this._map.entries()) {
|
for (const [key, value] of this._map.entries()) {
|
||||||
if (key.startsWith(subdomain + '.')) {
|
if (key.startsWith(subdomain + '.')) {
|
||||||
const field = key.slice(subdomain.length + 1)
|
const field = key.slice(subdomain.length + 1)
|
||||||
|
@ -160,7 +162,7 @@ function initRecord(peer, config) {
|
||||||
* @param {Msg | null | undefined} msg
|
* @param {Msg | null | undefined} msg
|
||||||
* @returns {msg is Msg}
|
* @returns {msg is Msg}
|
||||||
*/
|
*/
|
||||||
function isValidRecordMoot(msg) {
|
function isValidDictMoot(msg) {
|
||||||
if (!msg) return false
|
if (!msg) return false
|
||||||
if (msg.metadata.account !== accountID) return false
|
if (msg.metadata.account !== accountID) return false
|
||||||
const domain = msg.metadata.domain
|
const domain = msg.metadata.domain
|
||||||
|
@ -171,9 +173,9 @@ function initRecord(peer, config) {
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {Msg | null | undefined} msg
|
* @param {Msg | null | undefined} msg
|
||||||
* @returns {msg is Msg<RecordData>}
|
* @returns {msg is Msg<DictMsgData>}
|
||||||
*/
|
*/
|
||||||
function isValidRecordMsg(msg) {
|
function isValidDictMsg(msg) {
|
||||||
if (!msg) return false
|
if (!msg) return false
|
||||||
if (!msg.data) return false
|
if (!msg.data) return false
|
||||||
if (msg.metadata.account !== accountID) return false
|
if (msg.metadata.account !== accountID) return false
|
||||||
|
@ -190,7 +192,7 @@ function initRecord(peer, config) {
|
||||||
* @param {string} mootID
|
* @param {string} mootID
|
||||||
* @param {Msg} moot
|
* @param {Msg} moot
|
||||||
*/
|
*/
|
||||||
function learnRecordMoot(mootID, moot) {
|
function learnDictMoot(mootID, moot) {
|
||||||
const subdomain = toSubdomain(moot.metadata.domain)
|
const subdomain = toSubdomain(moot.metadata.domain)
|
||||||
const tangle = tangles.get(subdomain) ?? new MsgV3.Tangle(mootID)
|
const tangle = tangles.get(subdomain) ?? new MsgV3.Tangle(mootID)
|
||||||
tangle.add(mootID, moot)
|
tangle.add(mootID, moot)
|
||||||
|
@ -200,9 +202,9 @@ function initRecord(peer, config) {
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} msgID
|
* @param {string} msgID
|
||||||
* @param {Msg<RecordData>} msg
|
* @param {Msg<DictMsgData>} msg
|
||||||
*/
|
*/
|
||||||
function learnRecordUpdate(msgID, msg) {
|
function learnDictUpdate(msgID, msg) {
|
||||||
const { account, domain } = msg.metadata
|
const { account, domain } = msg.metadata
|
||||||
const mootID = MsgV3.getMootID(account, domain)
|
const mootID = MsgV3.getMootID(account, domain)
|
||||||
const subdomain = toSubdomain(domain)
|
const subdomain = toSubdomain(domain)
|
||||||
|
@ -232,14 +234,14 @@ function initRecord(peer, config) {
|
||||||
* @param {string} msgID
|
* @param {string} msgID
|
||||||
* @param {Msg} msg
|
* @param {Msg} msg
|
||||||
*/
|
*/
|
||||||
function maybeLearnAboutRecord(msgID, msg) {
|
function maybeLearnAboutDict(msgID, msg) {
|
||||||
if (msg.metadata.account !== accountID) return
|
if (msg.metadata.account !== accountID) return
|
||||||
if (isValidRecordMoot(msg)) {
|
if (isValidDictMoot(msg)) {
|
||||||
learnRecordMoot(msgID, msg)
|
learnDictMoot(msgID, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isValidRecordMsg(msg)) {
|
if (isValidDictMsg(msg)) {
|
||||||
learnRecordUpdate(msgID, msg)
|
learnDictUpdate(msgID, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +281,7 @@ function initRecord(peer, config) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} subdomain
|
* @param {string} subdomain
|
||||||
* @param {Record<string, any>} update
|
* @param {{[field in string]: any}} update
|
||||||
* @param {CB<boolean>} cb
|
* @param {CB<boolean>} cb
|
||||||
*/
|
*/
|
||||||
function forceUpdate(subdomain, update, cb) {
|
function forceUpdate(subdomain, update, cb) {
|
||||||
|
@ -298,7 +300,7 @@ function initRecord(peer, config) {
|
||||||
{ account: accountID, domain, data: { update, supersedes } },
|
{ account: accountID, domain, data: { update, supersedes } },
|
||||||
(err, rec) => {
|
(err, rec) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return cb(new Error('Failed to create msg when force updating Record', { cause: err }))
|
if (err) return cb(new Error('Failed to create msg when force-updating Dict', { cause: err }))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
cb(null, true)
|
cb(null, true)
|
||||||
}
|
}
|
||||||
|
@ -317,12 +319,12 @@ function initRecord(peer, config) {
|
||||||
loadPromise = new Promise((resolve, reject) => {
|
loadPromise = new Promise((resolve, reject) => {
|
||||||
for (const rec of peer.db.records()) {
|
for (const rec of peer.db.records()) {
|
||||||
if (!rec.msg) continue
|
if (!rec.msg) continue
|
||||||
maybeLearnAboutRecord(rec.id, rec.msg)
|
maybeLearnAboutDict(rec.id, rec.msg)
|
||||||
}
|
}
|
||||||
cancelOnRecordAdded = peer.db.onRecordAdded(
|
cancelOnRecordAdded = peer.db.onRecordAdded(
|
||||||
(/** @type {RecPresent} */ rec) => {
|
(/** @type {RecPresent} */ rec) => {
|
||||||
try {
|
try {
|
||||||
maybeLearnAboutRecord(rec.id, rec.msg)
|
maybeLearnAboutDict(rec.id, rec.msg)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
|
@ -355,7 +357,7 @@ function initRecord(peer, config) {
|
||||||
|
|
||||||
const domain = rootMsg.metadata.domain
|
const domain = rootMsg.metadata.domain
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!domain.startsWith(PREFIX)) throw new Error(`"${tangleID}" is not a record moot`)
|
if (!domain.startsWith(PREFIX)) throw new Error(`"${tangleID}" is not a dict moot`)
|
||||||
|
|
||||||
// Discover field roots
|
// Discover field roots
|
||||||
const fieldRoots = new Set()
|
const fieldRoots = new Set()
|
||||||
|
@ -393,7 +395,7 @@ function initRecord(peer, config) {
|
||||||
* @public
|
* @public
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @param {string} subdomain
|
* @param {string} subdomain
|
||||||
* @returns {Record<string, any> | null}
|
* @returns {{[field in string]: any} | null}
|
||||||
*/
|
*/
|
||||||
function read(id, subdomain) {
|
function read(id, subdomain) {
|
||||||
assertDBPlugin(peer)
|
assertDBPlugin(peer)
|
||||||
|
@ -405,15 +407,15 @@ function initRecord(peer, config) {
|
||||||
else return null
|
else return null
|
||||||
}
|
}
|
||||||
const msgIDs = tangle.topoSort()
|
const msgIDs = tangle.topoSort()
|
||||||
const record = /** @type {Record<string, any>}*/ ({})
|
const dict = /** @type {{[field in string]: any}} */ ({})
|
||||||
for (const msgID of msgIDs) {
|
for (const msgID of msgIDs) {
|
||||||
const msg = peer.db.get(msgID)
|
const msg = peer.db.get(msgID)
|
||||||
if (isValidRecordMsg(msg)) {
|
if (isValidDictMsg(msg)) {
|
||||||
const { update } = msg.data
|
const { update } = msg.data
|
||||||
Object.assign(record, update)
|
Object.assign(dict, update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return record
|
return dict
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -445,7 +447,7 @@ function initRecord(peer, config) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!msg) throw new Error(`isGhostable() msgID "${ghostableMsgID}" does not exist in the database`)
|
if (!msg) throw new Error(`isGhostable() msgID "${ghostableMsgID}" does not exist in the database`)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!isValidRecordMoot(tangle.root)) throw new Error(`isGhostable() tangleID "${tangleID}" is not a record`)
|
if (!isValidDictMoot(tangle.root)) throw new Error(`isGhostable() tangleID "${tangleID}" is not a dict`)
|
||||||
|
|
||||||
// Discover field roots
|
// Discover field roots
|
||||||
const fieldRootIDs = new Set()
|
const fieldRootIDs = new Set()
|
||||||
|
@ -486,7 +488,7 @@ function initRecord(peer, config) {
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
* @param {string} subdomain
|
* @param {string} subdomain
|
||||||
* @param {Record<string, any>} update
|
* @param {{[field in string]: any}} update
|
||||||
* @param {CB<boolean>} cb
|
* @param {CB<boolean>} cb
|
||||||
*/
|
*/
|
||||||
function update(subdomain, update, cb) {
|
function update(subdomain, update, cb) {
|
||||||
|
@ -494,13 +496,13 @@ function initRecord(peer, config) {
|
||||||
|
|
||||||
loaded(() => {
|
loaded(() => {
|
||||||
if (!accountID) return cb(new Error('Expected account to be loaded'))
|
if (!accountID) return cb(new Error('Expected account to be loaded'))
|
||||||
const record = read(accountID, subdomain)
|
const dict = read(accountID, subdomain)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!record) return cb(new Error(`Cannot update non-existent record "${subdomain}`))
|
if (!dict) return cb(new Error(`Cannot update non-existent dict "${subdomain}`))
|
||||||
|
|
||||||
let hasChanges = false
|
let hasChanges = false
|
||||||
for (const [field, value] of Object.entries(update)) {
|
for (const [field, value] of Object.entries(update)) {
|
||||||
if (value !== record[field]) {
|
if (value !== dict[field]) {
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -521,12 +523,12 @@ function initRecord(peer, config) {
|
||||||
|
|
||||||
loaded(() => {
|
loaded(() => {
|
||||||
if (!accountID) return cb(new Error('Expected account to be loaded'))
|
if (!accountID) return cb(new Error('Expected account to be loaded'))
|
||||||
const record = read(accountID, subdomain)
|
const dict = read(accountID, subdomain)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!record) return cb(new Error(`Cannot squeeze non-existent record "${subdomain}`))
|
if (!dict) return cb(new Error(`Cannot squeeze non-existent Dict "${subdomain}"`))
|
||||||
forceUpdate(subdomain, record, (err, _forceUpdated) => {
|
forceUpdate(subdomain, dict, (err, _forceUpdated) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return cb(new Error('Failed to force update when squeezing Record', { cause: err }))
|
if (err) return cb(new Error(`Failed to force update when squeezing Dict "${subdomain}"`, { cause: err }))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
cb(null, true)
|
cb(null, true)
|
||||||
})
|
})
|
||||||
|
@ -550,5 +552,5 @@ function initRecord(peer, config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.name = 'record'
|
exports.name = 'dict'
|
||||||
exports.init = initRecord
|
exports.init = initDict
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "ppppp-record",
|
"name": "ppppp-dict",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Record data structure over append-only logs with pruning",
|
"description": "Dictionary data structure over append-only logs with pruning",
|
||||||
"author": "Andre Staltz <contact@staltz.com>",
|
"author": "Andre Staltz <contact@staltz.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/staltz/ppppp-record",
|
"homepage": "https://github.com/staltz/ppppp-dict",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@github.com:staltz/ppppp-record.git"
|
"url": "git@github.com:staltz/ppppp-dict.git"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
|
12
protospec.md
12
protospec.md
|
@ -18,12 +18,12 @@ E-->D & C
|
||||||
classDef default fill:#bbb,stroke:#fff0,color:#000
|
classDef default fill:#bbb,stroke:#fff0,color:#000
|
||||||
```
|
```
|
||||||
|
|
||||||
Reducing the tangle above in a topological sort allows you to build a record
|
Reducing the tangle above in a topological sort allows you to build a dict
|
||||||
(a JSON object) `{age, name}`.
|
(a JSON object) `{age, name}`.
|
||||||
|
|
||||||
## Msg metadata domain
|
## Msg metadata domain
|
||||||
|
|
||||||
`msg.metadata.domain` MUST start with `record_v1__`. E.g. `record_v1__profile`.
|
`msg.metadata.domain` MUST start with `dict_v1__`. E.g. `dict_v1__profile`.
|
||||||
|
|
||||||
## Msg data
|
## Msg data
|
||||||
|
|
||||||
|
@ -31,7 +31,9 @@ Reducing the tangle above in a topological sort allows you to build a record
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface MsgData {
|
interface MsgData {
|
||||||
update: Record<string, any>,
|
update: {
|
||||||
|
[field in string]: any,
|
||||||
|
},
|
||||||
supersedes: Array<MsgHash>,
|
supersedes: Array<MsgHash>,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -40,11 +42,11 @@ RECOMMENDED that the `msg.data.update` is as flat as possible (no nesting).
|
||||||
|
|
||||||
## Supersedes links
|
## Supersedes links
|
||||||
|
|
||||||
When you update a field in a record, in the `supersedes` array you MUST point
|
When you update a field in a dict, in the `supersedes` array you MUST point
|
||||||
to the currently-known highest-depth msg that updated that field.
|
to the currently-known highest-depth msg that updated that field.
|
||||||
|
|
||||||
The set of *not-transitively-superseded-by-anyone* msgs comprise the
|
The set of *not-transitively-superseded-by-anyone* msgs comprise the
|
||||||
"field roots" of the record. To allow pruning the tangle, we can delete
|
"field roots" of the dict. To allow pruning the tangle, we can delete
|
||||||
(or, if we want to keep metadata, "erase") all msgs preceding the field roots.
|
(or, if we want to keep metadata, "erase") all msgs preceding the field roots.
|
||||||
|
|
||||||
Suppose the tangle is grown in the order below, then the field roots are
|
Suppose the tangle is grown in the order below, then the field roots are
|
||||||
|
|
|
@ -8,7 +8,7 @@ const Keypair = require('ppppp-keypair')
|
||||||
const p = require('util').promisify
|
const p = require('util').promisify
|
||||||
const { createPeer } = require('./util')
|
const { createPeer } = require('./util')
|
||||||
|
|
||||||
const DIR = path.join(os.tmpdir(), 'ppppp-record')
|
const DIR = path.join(os.tmpdir(), 'ppppp-dict')
|
||||||
rimraf.sync(DIR)
|
rimraf.sync(DIR)
|
||||||
|
|
||||||
const aliceKeypair = Keypair.generate('ed25519', 'alice')
|
const aliceKeypair = Keypair.generate('ed25519', 'alice')
|
||||||
|
@ -19,7 +19,7 @@ test('setup', async (t) => {
|
||||||
peer = createPeer({
|
peer = createPeer({
|
||||||
keypair: aliceKeypair,
|
keypair: aliceKeypair,
|
||||||
path: DIR,
|
path: DIR,
|
||||||
record: { ghostSpan: 4 },
|
dict: { ghostSpan: 4 },
|
||||||
})
|
})
|
||||||
|
|
||||||
await peer.db.loaded()
|
await peer.db.loaded()
|
||||||
|
@ -28,143 +28,143 @@ test('setup', async (t) => {
|
||||||
domain: 'account',
|
domain: 'account',
|
||||||
_nonce: 'alice',
|
_nonce: 'alice',
|
||||||
})
|
})
|
||||||
await p(peer.record.load)(aliceID)
|
await p(peer.dict.load)(aliceID)
|
||||||
|
|
||||||
assert.equal(peer.record.getGhostSpan(), 4, 'getGhostSpan')
|
assert.equal(peer.dict.getGhostSpan(), 4, 'getGhostSpan')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Record update() and get()', async (t) => {
|
test('Dict update() and get()', async (t) => {
|
||||||
assert(
|
assert(
|
||||||
await p(peer.record.update)('profile', { name: 'alice' }),
|
await p(peer.dict.update)('profile', { name: 'alice' }),
|
||||||
'update .name'
|
'update .name'
|
||||||
)
|
)
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.record.read(aliceID, 'profile'),
|
peer.dict.read(aliceID, 'profile'),
|
||||||
{ name: 'alice' },
|
{ name: 'alice' },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
|
||||||
const fieldRoots1 = peer.record._getFieldRoots('profile')
|
const fieldRoots1 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
fieldRoots1,
|
fieldRoots1,
|
||||||
{ name: ['PbwnLbJS4oninQ1RPCdgRn'] },
|
{ name: ['QZSb3GMTRWWUUVLtueNB7Q'] },
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert(await p(peer.record.update)('profile', { age: 20 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 20 }), 'update .age')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.record.read(aliceID, 'profile'),
|
peer.dict.read(aliceID, 'profile'),
|
||||||
{ name: 'alice', age: 20 },
|
{ name: 'alice', age: 20 },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
|
||||||
const fieldRoots2 = peer.record._getFieldRoots('profile')
|
const fieldRoots2 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
fieldRoots2,
|
fieldRoots2,
|
||||||
{ name: ['PbwnLbJS4oninQ1RPCdgRn'], age: ['9iTTqNabtnXmw4AiZxNMRq'] },
|
{ name: ['QZSb3GMTRWWUUVLtueNB7Q'], age: ['98QTF8Zip6NYJgmcf96L2K'] },
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await p(peer.record.update)('profile', { name: 'alice' }),
|
await p(peer.dict.update)('profile', { name: 'alice' }),
|
||||||
false,
|
false,
|
||||||
'redundant update .name'
|
'redundant update .name'
|
||||||
)
|
)
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.record.read(aliceID, 'profile'),
|
peer.dict.read(aliceID, 'profile'),
|
||||||
{ name: 'alice', age: 20 },
|
{ name: 'alice', age: 20 },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await p(peer.record.update)('profile', { name: 'Alice' }),
|
await p(peer.dict.update)('profile', { name: 'Alice' }),
|
||||||
true,
|
true,
|
||||||
'update .name'
|
'update .name'
|
||||||
)
|
)
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.record.read(aliceID, 'profile'),
|
peer.dict.read(aliceID, 'profile'),
|
||||||
{ name: 'Alice', age: 20 },
|
{ name: 'Alice', age: 20 },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
|
||||||
const fieldRoots3 = peer.record._getFieldRoots('profile')
|
const fieldRoots3 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
fieldRoots3,
|
fieldRoots3,
|
||||||
{ age: ['9iTTqNabtnXmw4AiZxNMRq'], name: ['M2JhM7TE2KX5T5rfnxBh6M'] },
|
{ age: ['98QTF8Zip6NYJgmcf96L2K'], name: ['49rg6mJFDgdq6kZTE8uedr'] },
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Record squeeze', async (t) => {
|
test('Dict squeeze', async (t) => {
|
||||||
assert(await p(peer.record.update)('profile', { age: 21 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 21 }), 'update .age')
|
||||||
assert(await p(peer.record.update)('profile', { age: 22 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 22 }), 'update .age')
|
||||||
assert(await p(peer.record.update)('profile', { age: 23 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 23 }), 'update .age')
|
||||||
|
|
||||||
const fieldRoots4 = peer.record._getFieldRoots('profile')
|
const fieldRoots4 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
fieldRoots4,
|
fieldRoots4,
|
||||||
{ name: ['M2JhM7TE2KX5T5rfnxBh6M'], age: ['S3xiydrT6Y34Bp1vg6wN7P'] },
|
{ name: ['49rg6mJFDgdq6kZTE8uedr'], age: ['GE9KcJc5efunBhSTDjy6zX'] },
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
peer.record._squeezePotential('profile'),
|
peer.dict._squeezePotential('profile'),
|
||||||
3,
|
3,
|
||||||
'squeezePotential=3'
|
'squeezePotential=3'
|
||||||
)
|
)
|
||||||
assert.equal(await p(peer.record.squeeze)('profile'), true, 'squeezed')
|
assert.equal(await p(peer.dict.squeeze)('profile'), true, 'squeezed')
|
||||||
|
|
||||||
const fieldRoots5 = peer.record._getFieldRoots('profile')
|
const fieldRoots5 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
fieldRoots5,
|
fieldRoots5,
|
||||||
{ name: ['Y4JkpPCHN8Avtz4VALaAmK'], age: ['Y4JkpPCHN8Avtz4VALaAmK'] },
|
{ name: ['Xr7DZdwaANzPByUdRYGb2E'], age: ['Xr7DZdwaANzPByUdRYGb2E'] },
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
peer.record._squeezePotential('profile'),
|
peer.dict._squeezePotential('profile'),
|
||||||
0,
|
0,
|
||||||
'squeezePotential=0'
|
'squeezePotential=0'
|
||||||
)
|
)
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await p(peer.record.squeeze)('profile'),
|
await p(peer.dict.squeeze)('profile'),
|
||||||
false,
|
false,
|
||||||
'squeeze idempotent'
|
'squeeze idempotent'
|
||||||
)
|
)
|
||||||
|
|
||||||
const fieldRoots6 = peer.record._getFieldRoots('profile')
|
const fieldRoots6 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(fieldRoots6, fieldRoots5, 'fieldRoots')
|
assert.deepEqual(fieldRoots6, fieldRoots5, 'fieldRoots')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Record isGhostable', (t) => {
|
test('Dict isGhostable', (t) => {
|
||||||
const moot = MsgV3.createMoot(aliceID, 'record_v1__profile', aliceKeypair)
|
const moot = MsgV3.createMoot(aliceID, 'dict_v1__profile', aliceKeypair)
|
||||||
const mootID = MsgV3.getMsgID(moot)
|
const mootID = MsgV3.getMsgID(moot)
|
||||||
|
|
||||||
assert.equal(mootID, peer.record.getFeedID('profile'), 'getFeedID')
|
assert.equal(mootID, peer.dict.getFeedID('profile'), 'getFeedID')
|
||||||
|
|
||||||
const tangle = peer.db.getTangle(mootID)
|
const tangle = peer.db.getTangle(mootID)
|
||||||
const msgIDs = tangle.topoSort()
|
const msgIDs = tangle.topoSort()
|
||||||
|
|
||||||
const fieldRoots = peer.record._getFieldRoots('profile')
|
const fieldRoots = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(fieldRoots.age, [msgIDs[7]])
|
assert.deepEqual(fieldRoots.age, [msgIDs[7]])
|
||||||
|
|
||||||
// Remember from the setup, that ghostSpan=4
|
// Remember from the setup, that ghostSpan=4
|
||||||
assert.equal(msgIDs.length, 8)
|
assert.equal(msgIDs.length, 8)
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[0], mootID), false) // moot
|
assert.equal(peer.dict.isGhostable(msgIDs[0], mootID), false) // moot
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[1], mootID), false)
|
assert.equal(peer.dict.isGhostable(msgIDs[1], mootID), false)
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[2], mootID), false)
|
assert.equal(peer.dict.isGhostable(msgIDs[2], mootID), false)
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[3], mootID), true) // in ghostSpan
|
assert.equal(peer.dict.isGhostable(msgIDs[3], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[4], mootID), true) // in ghostSpan
|
assert.equal(peer.dict.isGhostable(msgIDs[4], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[5], mootID), true) // in ghostSpan
|
assert.equal(peer.dict.isGhostable(msgIDs[5], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[6], mootID), true) // in ghostSpan
|
assert.equal(peer.dict.isGhostable(msgIDs[6], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.record.isGhostable(msgIDs[7], mootID), false) // field root
|
assert.equal(peer.dict.isGhostable(msgIDs[7], mootID), false) // field root
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Record receives old branched update', async (t) => {
|
test('Dict receives old branched update', async (t) => {
|
||||||
const moot = MsgV3.createMoot(aliceID, 'record_v1__profile', aliceKeypair)
|
const moot = MsgV3.createMoot(aliceID, 'dict_v1__profile', aliceKeypair)
|
||||||
const mootID = MsgV3.getMsgID(moot)
|
const mootID = MsgV3.getMsgID(moot)
|
||||||
|
|
||||||
assert.equal(peer.record.minRequiredDepth(mootID), 7, 'minRequiredDepth')
|
assert.equal(peer.dict.minRequiredDepth(mootID), 7, 'minRequiredDepth')
|
||||||
|
|
||||||
const tangle = new MsgV3.Tangle(mootID)
|
const tangle = new MsgV3.Tangle(mootID)
|
||||||
tangle.add(mootID, moot)
|
tangle.add(mootID, moot)
|
||||||
|
@ -172,7 +172,7 @@ test('Record receives old branched update', async (t) => {
|
||||||
|
|
||||||
const msg = MsgV3.create({
|
const msg = MsgV3.create({
|
||||||
keypair: aliceKeypair,
|
keypair: aliceKeypair,
|
||||||
domain: 'record_v1__profile',
|
domain: 'dict_v1__profile',
|
||||||
account: aliceID,
|
account: aliceID,
|
||||||
accountTips: [aliceID],
|
accountTips: [aliceID],
|
||||||
data: { update: { age: 2 }, supersedes: [] },
|
data: { update: { age: 2 }, supersedes: [] },
|
||||||
|
@ -181,22 +181,22 @@ test('Record receives old branched update', async (t) => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const rec = await p(peer.db.add)(msg, mootID)
|
const rec = await p(peer.db.add)(msg, mootID)
|
||||||
assert.equal(rec.id, 'XZWr3DZFG253awsWXgSkS2', 'msg ID')
|
assert.equal(rec.id, 'PBq5dgfK9icRVx7SLhyaC5', 'msg ID')
|
||||||
|
|
||||||
const fieldRoots7 = peer.record._getFieldRoots('profile')
|
const fieldRoots7 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
fieldRoots7,
|
fieldRoots7,
|
||||||
{
|
{
|
||||||
name: ['Y4JkpPCHN8Avtz4VALaAmK'],
|
name: ['Xr7DZdwaANzPByUdRYGb2E'],
|
||||||
age: ['Y4JkpPCHN8Avtz4VALaAmK', rec.id],
|
age: ['Xr7DZdwaANzPByUdRYGb2E', rec.id],
|
||||||
},
|
},
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(peer.record.minRequiredDepth(mootID), 1, 'minRequiredDepth')
|
assert.equal(peer.dict.minRequiredDepth(mootID), 1, 'minRequiredDepth')
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
peer.record._squeezePotential('profile'),
|
peer.dict._squeezePotential('profile'),
|
||||||
6,
|
6,
|
||||||
'squeezePotential=6'
|
'squeezePotential=6'
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue