Comparing version 4.6.3 to 5.0.0
# Changelog | ||
## v5.0.0 | ||
Breaking: | ||
- `crut.list` now supports more options for `opts.orderBy`, but to support this needed to | ||
change the API of `opts.read` for that method | ||
## v4.0.0 | ||
@@ -4,0 +10,0 @@ |
24
index.js
@@ -69,2 +69,3 @@ const Strategy = require('@tangle/strategy') | ||
this.createStream = CreateStream(ssb, this.spec) | ||
this.getRecordId = GetRecordId(this.spec) | ||
} | ||
@@ -228,12 +229,15 @@ | ||
opts.groupId | ||
? pull.filter(msg => ( | ||
msg.value.content.recps && | ||
msg.value.content.recps[0] === opts.groupId | ||
)) | ||
? pull.filter(msg => { | ||
if (!msg.value.content.recps) return false | ||
return msg.value.content.recps[0] === opts.groupId | ||
}) | ||
: null, | ||
pull.map(this.getRecordId), | ||
pull.unique(), | ||
// map rootMsg => record (and filter out the ones which were junk) | ||
paraMap( | ||
opts.read || ((msg, cb) => { | ||
this.read(msg.key, (err, record) => { | ||
opts.read || ((id, cb) => { | ||
this.read(id, (err, record) => { | ||
if (err) cb(null, false) | ||
@@ -373,1 +377,9 @@ else cb(null, record) | ||
} | ||
function GetRecordId (spec) { | ||
return function getRecordId (msg) { | ||
return msg.value.content.tangles[spec.tangle].root === null | ||
? msg.key | ||
: msg.value.content.tangles[spec.tangle].root | ||
} | ||
} |
@@ -0,48 +1,101 @@ | ||
/* eslint brace-style: ["error", "stroustrup", { "allowSingleLine": true }] */ | ||
const RECEIVE_TIME = 'receiveTime' | ||
const UPDATE_TIME = 'updateTime' | ||
const CREATE_TIME = 'createTime' | ||
// creates a stream of potential recordIds | ||
module.exports = function CreateStream (ssb, spec) { | ||
if (ssb.db) { | ||
const { where, and, slowEqual, type, descending, sortByArrival, toPullStream } = require('ssb-db2/operators') | ||
if (ssb.db) return CreateDB2Stream(ssb, spec) | ||
else return CreateDB1Stream(ssb, spec) | ||
} | ||
return function createStreamDB2 (opts) { | ||
return ssb.db.query( | ||
where( | ||
and( | ||
type(spec.type), | ||
slowEqual(`value.content.tangles.${spec.tangle}.root`, null) | ||
) | ||
), | ||
opts.descending === false ? null : descending(), | ||
(opts.orderBy || opts.sortBy) === CREATE_TIME ? null : sortByArrival(), | ||
toPullStream() | ||
) | ||
function CreateDB1Stream (ssb, spec) { | ||
return function createDB1Stream (opts) { | ||
const orderBy = opts.orderBy || opts.sortBy | ||
let $filter | ||
if (orderBy === RECEIVE_TIME || orderBy === undefined) { | ||
$filter = { | ||
timestamp: { $gt: 0 }, // stimulates orderBy received time | ||
value: { | ||
content: { | ||
type: spec.type, | ||
tangles: { | ||
[spec.tangle]: { root: null } // root messages | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} // eslint-disable-line | ||
else { | ||
return function createStreamDB1 (opts) { | ||
const query = [{ | ||
$filter: { | ||
value: { | ||
content: { | ||
type: spec.type, | ||
tangles: { | ||
[spec.tangle]: { root: null } | ||
} | ||
else if (orderBy === CREATE_TIME) { | ||
$filter = { | ||
value: { | ||
timestamp: { $gt: 0 }, // stimulates orderBy (asserted) create time | ||
content: { | ||
type: spec.type, | ||
tangles: { | ||
[spec.tangle]: { root: null } // root messages | ||
} | ||
} | ||
} | ||
}] | ||
if ((opts.orderBy || opts.sortBy) === CREATE_TIME) { | ||
query[0].$filter.value.timestamp = { $gt: 0 } | ||
// this is a common ssb-query hack to stimulate the tya index to be used | ||
// tya = "type, asserted timestamp" (otherwise tyr = "type, received timestamp" is used) | ||
} | ||
} | ||
else if (orderBy === UPDATE_TIME) { | ||
$filter = { | ||
timestamp: { $gt: 0 }, // stimulates orderBy received time | ||
value: { | ||
content: { | ||
type: spec.type, | ||
tangles: { | ||
[spec.tangle]: { $is: 'object' } // either root/update! | ||
} | ||
} | ||
} | ||
} | ||
} | ||
else throw new Error(`unknown opts.orderBy ${orderBy}`) | ||
// these are a common ssb-query hack to stimulate the the right time-ordering index | ||
// tya = "type, asserted timestamp" | ||
// tyr = "type, received timestamp" | ||
return ssb.query.read({ | ||
query, | ||
// ...opts, | ||
reverse: opts.descending !== false | ||
}) | ||
} | ||
return ssb.query.read({ | ||
query: [{ $filter }], | ||
// ...opts, | ||
reverse: opts.descending !== false | ||
}) | ||
} | ||
} | ||
function CreateDB2Stream (ssb, spec) { | ||
const { | ||
where, and, | ||
slowPredicate, slowEqual, type, | ||
descending, sortByArrival, | ||
toPullStream | ||
} = require('ssb-db2/operators') | ||
return function createDB2Stream (opts) { | ||
return ssb.db.query( | ||
where( | ||
and( | ||
type(spec.type), | ||
(opts.orderBy === UPDATE_TIME) | ||
? slowPredicate(`value.content.tangles.${spec.tangle}.root`, isAny) | ||
: slowEqual(`value.content.tangles.${spec.tangle}.root`, null) | ||
// NOTE this could be better written, can look at it later | ||
) | ||
), | ||
opts.descending === false ? null : descending(), | ||
(opts.orderBy || opts.sortBy) === CREATE_TIME ? null : sortByArrival(), | ||
toPullStream() | ||
) | ||
} | ||
} | ||
function isAny (root) { | ||
return ( | ||
root === null || | ||
typeof root === 'string' | ||
) | ||
} |
{ | ||
"name": "ssb-crut", | ||
"version": "4.6.3", | ||
"version": "5.0.0", | ||
"description": "easy CRUT methods for secure scuttlebutt", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -328,4 +328,6 @@ # ssb-crut | ||
- define what order to pull the records in by, looks at the times recorded on the **initial message** of the record. | ||
- options: `receiveTime|createTime` | ||
- default: `receiveTime` | ||
- options: | ||
- `receiveTime` = order by when the record was first received/ written (default) | ||
- `updateTime` = order by how recently writes (create/update) to the record were received | ||
- `createTime` = order by when the record was created (as asserted by the author) | ||
- `opts.descending` *Boolean* | ||
@@ -342,4 +344,4 @@ - sets whether the receiveTime/createTime/updateTime is _descending_ throughour the results | ||
```js | ||
(msg, cb) => { | ||
crut.read(msg.key, (err, record) => { | ||
(id, cb) => { | ||
crut.read(id, (err, record) => { | ||
if (err) cb(null, null) // prevents one failed read from causing whole like to fail | ||
@@ -350,2 +352,3 @@ else cb(null, record) | ||
``` | ||
- where `recordId` is the id of the root message of the record tangle | ||
- `opts.width` *Integer* | ||
@@ -352,0 +355,0 @@ - how many `read` functions to run in parallel |
@@ -83,3 +83,3 @@ const test = require('tape') | ||
t.true(profile.tombstone, 'isTombstoned') | ||
t.deepEqual(profile.states.length, 1, 'forceTombstone merges the branched states') | ||
t.equal(profile.states.length, 1, 'forceTombstone merges the branched states') | ||
@@ -86,0 +86,0 @@ server1.close() |
@@ -8,11 +8,12 @@ const test = require('tape') | ||
test('list', async t => { | ||
const spec = { | ||
type: 'profile/test', | ||
tangle: 'profile', | ||
props: { | ||
name: Overwrite() | ||
} | ||
const spec = { | ||
type: 'profile/test', | ||
tangle: 'profile', | ||
props: { | ||
name: Overwrite() | ||
} | ||
} | ||
test('list', async t => { | ||
/* setup */ | ||
// make a friend who has made a record first | ||
@@ -25,12 +26,20 @@ const friend = SSB() | ||
const crut = new CRUT(me, spec) | ||
const create = (name, recps) => crut.create({ name, recps }) | ||
const create = async (name, recps) => { | ||
const id = await crut.create({ name, recps }) | ||
if (process.env.DB2) { | ||
await promisify(setTimeout)(50) | ||
// HACK db2 gets orderBy=createTime wrong sometimes without this | ||
} | ||
return id | ||
} | ||
await create(1) | ||
const recordId = await create(2) | ||
await crut.update(recordId, { name: 2.0 }) | ||
const secondId = await create(2) | ||
await create(3) | ||
await crut.update(secondId, { name: 2.0 }) | ||
// order of creation: 3, 2, 1, 0 (descending) | ||
// order of update: 2, 3, 1, 0 (descending) | ||
let list | ||
list = await crut.list() | ||
/* opts = {} */ | ||
let list = await crut.list() | ||
t.deepEqual( | ||
@@ -42,3 +51,3 @@ list.map(record => record.name), | ||
// limit | ||
/* opts.limit */ | ||
list = await crut.list({ limit: 2 }) | ||
@@ -51,2 +60,3 @@ t.deepEqual( | ||
/* opts.descending */ | ||
// limit fail | ||
@@ -67,4 +77,4 @@ try { | ||
// startFrom | ||
list = await crut.list({ startFrom: recordId }) | ||
/* opts.startFrom */ | ||
list = await crut.list({ startFrom: secondId }) | ||
t.deepEqual( | ||
@@ -76,12 +86,19 @@ list.map(record => record.name), | ||
// orderBy | ||
/* opts.orderBy */ | ||
await replicate({ from: friend, to: me }) | ||
list = await crut.list() | ||
list = await crut.list() // orderBy: receiveTime | ||
t.deepEqual( | ||
list.map(record => record.name), | ||
[0, 3, 2.0, 1], | ||
'opts.orderBy = receivedTime (after replicate))' | ||
'opts.orderBy = receiveTime (after replicate))' | ||
) | ||
list = await crut.list({ orderBy: 'updateTime' }) | ||
t.deepEqual( | ||
list.map(record => record.name), | ||
[0, 2.0, 3, 1], | ||
'opts.orderBy = updateTime (after replicate))' | ||
) | ||
list = await crut.list({ orderBy: 'createTime' }) | ||
@@ -91,3 +108,3 @@ t.deepEqual( | ||
[3, 2.0, 1, 0], | ||
'opts.orderBy = createdTime (after replicate))' | ||
'opts.orderBy = createTime (after replicate))' | ||
) | ||
@@ -97,4 +114,6 @@ | ||
list = await crut.list({ | ||
filter: record => Math.floor(record.name % 2 === 0) | ||
}) | ||
filter: record => { | ||
return Math.floor(record.name) % 2 === 0 | ||
} | ||
}).catch(t.error) | ||
t.deepEqual( | ||
@@ -108,4 +127,4 @@ list.map(record => record.name), | ||
list = await crut.list({ | ||
read: (msg, cb) => { | ||
crut.read(msg.key, (err, record) => { | ||
read: (id, cb) => { | ||
crut.read(id, (err, record) => { | ||
if (err) return cb(null, null) | ||
@@ -120,3 +139,4 @@ | ||
// but could also check a cache etc | ||
}) | ||
}).catch(t.error) | ||
t.deepEqual( | ||
@@ -129,3 +149,3 @@ list.map(record => record.fancyName), | ||
// tombstone | ||
await crut.tombstone(recordId, {}) | ||
await crut.tombstone(secondId, {}) | ||
list = await crut.list() | ||
@@ -132,0 +152,0 @@ t.deepEqual( |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
136506
3719
370
10