Browse Source

Chore: swapped xo for standard

Signed-off-by: Jytesh <44925963+Jytesh@users.noreply.github.com>
master
Jytesh 3 years ago
parent
commit
1536a94052
  1. 5
      package.json
  2. 5
      packages/keyv-mongo/package.json
  3. 86
      packages/keyv-mongo/src/index.js
  4. 29
      packages/keyv-mongo/test/test.js
  5. 5
      packages/keyv-mysql/package.json
  6. 20
      packages/keyv-mysql/src/index.js
  7. 16
      packages/keyv-mysql/test/test.js
  8. 5
      packages/keyv-postgres/package.json
  9. 20
      packages/keyv-postgres/src/index.js
  10. 12
      packages/keyv-postgres/test/test.js
  11. 5
      packages/keyv-redis/package.json
  12. 78
      packages/keyv-redis/src/index.js
  13. 38
      packages/keyv-redis/test/test.js
  14. 5
      packages/keyv-sql/package.json
  15. 88
      packages/keyv-sql/src/index.js
  16. 32
      packages/keyv-sql/test/test.js
  17. 5
      packages/keyv-sqlite/package.json
  18. 28
      packages/keyv-sqlite/src/index.js
  19. 12
      packages/keyv-sqlite/test/test.js
  20. 6
      packages/keyv-test-suite/package.json
  21. 164
      packages/keyv-test-suite/src/api.js
  22. 22
      packages/keyv-test-suite/src/index.js
  23. 36
      packages/keyv-test-suite/src/iteration.js
  24. 102
      packages/keyv-test-suite/src/namespace.js
  25. 88
      packages/keyv-test-suite/src/values.js
  26. 10
      packages/keyv-test-suite/test/unit.js
  27. 9
      packages/keyv/package.json
  28. 108
      packages/keyv/src/index.js
  29. 228
      packages/keyv/test/keyv.js
  30. 12
      packages/keyv/test/storage-adapters/mongodb.js
  31. 12
      packages/keyv/test/storage-adapters/mysql.js
  32. 12
      packages/keyv/test/storage-adapters/postgresql.js
  33. 12
      packages/keyv/test/storage-adapters/redis.js
  34. 12
      packages/keyv/test/storage-adapters/sqlite.js

5
package.json

@ -6,16 +6,15 @@
"coveralls": "^3.0.0",
"delay": "^4.3.0",
"dotenv": "^8.2.0",
"eslint-config-xo-lukechilds": "^1.0.0",
"knex": "^0.95.4",
"lerna": "^4.0.0",
"nyc": "^15.1.0",
"pify": "^5.0.0",
"requirable": "^1.0.1",
"sqlite3": "^5.0.2",
"standard": "^16.0.3",
"this": "^1.0.2",
"timekeeper": "^2.0.0",
"xo": "^0.38.2"
"timekeeper": "^2.0.0"
},
"author": "Jytesh & Kikobeats",
"scripts": {

5
packages/keyv-mongo/package.json

@ -32,15 +32,12 @@
"src"
],
"scripts": {
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava"
},
"license": "MIT",
"ava": {
"require": [
"requirable"
]
},
"xo": {
"extends": "xo-lukechilds"
}
}

86
packages/keyv-mongo/src/index.js

@ -1,111 +1,111 @@
'use strict';
'use strict'
const EventEmitter = require('events');
const mongodb = require('mongodb');
const pify = require('pify');
const EventEmitter = require('events')
const mongodb = require('mongodb')
const pify = require('pify')
class KeyvMongo extends EventEmitter {
constructor(url, options) {
super();
this.ttlSupport = false;
url = url || {};
constructor (url, options) {
super()
this.ttlSupport = false
url = url || {}
if (typeof url === 'string') {
url = { url };
url = { url }
}
if (url.uri) {
url = Object.assign({ url: url.uri }, url);
url = Object.assign({ url: url.uri }, url)
}
this.options = Object.assign({
url: 'mongodb://127.0.0.1:27017',
collection: 'keyv'
}, url, options);
}, url, options)
this.options.mongoOptions = Object.assign({
useNewUrlParser: true,
useUnifiedTopology: true
}, this.options.mongoOptions);
}, this.options.mongoOptions)
try {
this.client = new mongodb.MongoClient(this.options.url, this.options.mongoOptions);
this.client = new mongodb.MongoClient(this.options.url, this.options.mongoOptions)
} catch (error) {
this.emit('error', error);
this.emit('error', error)
}
this.mongo = {};
let listeningEvents = false;
this.mongo = {}
let listeningEvents = false
// Implementation from sql by lukechilds,
this.connect = new Promise(resolve => {
this.client.connect()
.then(client => {
this.db = client.db(this.options.db);
this.store = this.db.collection(this.options.collection);
this.db = client.db(this.options.db)
this.store = this.db.collection(this.options.collection)
this.store.createIndex({ key: 1 }, {
unique: true,
background: true
});
})
this.store.createIndex({ expiresAt: 1 }, {
expireAfterSeconds: 0,
background: true
});
})
for (const method of ['updateOne', 'findOne', 'deleteOne', 'deleteMany']) {
this.store[method] = pify(this.store[method].bind(this.store));
this.store[method] = pify(this.store[method].bind(this.store))
}
if (!listeningEvents) {
this.client.on('error', error => this.emit('error', error));
listeningEvents = true;
this.client.on('error', error => this.emit('error', error))
listeningEvents = true
}
resolve(this.store);
resolve(this.store)
})
.catch(error => this.emit('error', error))
})
.catch(error => this.emit('error', error));
});
}
get(key) {
get (key) {
return this.connect
.then(store => store.findOne({ key: { $eq: key } })
.then(doc => {
if (doc === null) {
return undefined;
return undefined
}
return doc.value;
return doc.value
})
);
)
}
set(key, value, ttl) {
const expiresAt = (typeof ttl === 'number') ? new Date(Date.now() + ttl) : null;
set (key, value, ttl) {
const expiresAt = (typeof ttl === 'number') ? new Date(Date.now() + ttl) : null
return this.connect
.then(store => store.updateOne({ key: { $eq: key } }, { $set: { key, value, expiresAt } }, { upsert: true }));
.then(store => store.updateOne({ key: { $eq: key } }, { $set: { key, value, expiresAt } }, { upsert: true }))
}
delete(key) {
delete (key) {
if (typeof key !== 'string') {
return Promise.resolve(false);
return Promise.resolve(false)
}
return this.connect
.then(store => store.deleteOne({ key: { $eq: key } })
.then(object => object.deletedCount > 0)
);
)
}
clear() {
clear () {
return this.connect
.then(store => store.deleteMany({ key: new RegExp(`^${this.namespace + ':'}`) })
.then(() => undefined));
.then(() => undefined))
}
async * iterator() {
async * iterator () {
const iterator = await this.connect
.then(store => store.find({ key: new RegExp(`^${this.namespace ? this.namespace + ':' : '.*'}`) }).map(x => {
return [x.key, x.value];
}));
yield * iterator;
return [x.key, x.value]
}))
yield * iterator
}
}
module.exports = KeyvMongo;
module.exports = KeyvMongo

29
packages/keyv-mongo/test/test.js

@ -1,14 +1,14 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('@keyvhq/keyv');
const KeyvMongo = require('this');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('@keyvhq/keyv')
const KeyvMongo = require('this')
require('dotenv').config();
const mongoURL = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017';
const store = () => new KeyvMongo(mongoURL);
keyvTestSuite(test, Keyv, store);
require('dotenv').config()
const mongoURL = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017'
const store = () => new KeyvMongo(mongoURL)
keyvTestSuite(test, Keyv, store)
test('Collection option merges into default options', t => {
const store = new KeyvMongo({ collection: 'foo' });
const store = new KeyvMongo({ collection: 'foo' })
t.deepEqual(store.options, {
url: 'mongodb://127.0.0.1:27017',
mongoOptions: {
@ -16,11 +16,11 @@ test('Collection option merges into default options', t => {
useUnifiedTopology: true
},
collection: 'foo'
});
});
})
})
test('Collection option merges into default options if URL is passed', t => {
const store = new KeyvMongo(mongoURL, { collection: 'foo' });
const store = new KeyvMongo(mongoURL, { collection: 'foo' })
t.deepEqual(store.options, {
url: mongoURL,
mongoOptions: {
@ -28,6 +28,5 @@ test('Collection option merges into default options if URL is passed', t => {
useUnifiedTopology: true
},
collection: 'foo'
});
});
})
})

5
packages/keyv-mysql/package.json

@ -33,15 +33,12 @@
"src"
],
"scripts": {
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava"
},
"license": "MIT",
"ava": {
"require": [
"requirable"
]
},
"xo": {
"extends": "xo-lukechilds"
}
}

20
packages/keyv-mysql/src/index.js

@ -1,28 +1,28 @@
'use strict';
'use strict'
const KeyvSql = require('@keyvhq/keyv-sql');
const mysql = require('mysql2/promise');
const KeyvSql = require('@keyvhq/keyv-sql')
const mysql = require('mysql2/promise')
class KeyvMysql extends KeyvSql {
constructor(options) {
constructor (options) {
if (typeof options === 'string') {
options = { uri: options };
options = { uri: options }
}
options = Object.assign({
dialect: 'mysql',
uri: 'mysql://localhost'
}, options);
}, options)
options.connect = () => Promise.resolve()
.then(() => mysql.createConnection(options.uri))
.then(connection => {
return sql => connection.execute(sql)
.then(data => data[0]);
});
.then(data => data[0])
})
super(options);
super(options)
}
}
module.exports = KeyvMysql;
module.exports = KeyvMysql

16
packages/keyv-mysql/test/test.js

@ -1,9 +1,9 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('@keyvhq/keyv');
const KeyvMysql = require('this');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('@keyvhq/keyv')
const KeyvMysql = require('this')
require('dotenv').config();
const dbUrl = process.env.MYSQL_URL || 'mysql://root:root@localhost/keyv_test';
const store = () => new KeyvMysql(dbUrl);
keyvTestSuite(test, Keyv, store);
require('dotenv').config()
const dbUrl = process.env.MYSQL_URL || 'mysql://root:root@localhost/keyv_test'
const store = () => new KeyvMysql(dbUrl)
keyvTestSuite(test, Keyv, store)

5
packages/keyv-postgres/package.json

@ -33,15 +33,12 @@
"src"
],
"scripts": {
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava --serial"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava --serial"
},
"license": "MIT",
"ava": {
"require": [
"requirable"
]
},
"xo": {
"extends": "xo-lukechilds"
}
}

20
packages/keyv-postgres/src/index.js

@ -1,27 +1,27 @@
'use strict';
'use strict'
const KeyvSql = require('@keyvhq/keyv-sql');
const Pool = require('pg').Pool;
const KeyvSql = require('@keyvhq/keyv-sql')
const Pool = require('pg').Pool
class KeyvPostgres extends KeyvSql {
constructor(options) {
constructor (options) {
options = Object.assign({
dialect: 'postgres',
uri: 'postgresql://localhost:5432'
}, options);
}, options)
options.connect = () => Promise.resolve()
.then(() => {
const pool = new Pool({
connectionString: options.uri,
ssl: options.ssl
});
})
return sql => pool.query(sql)
.then(data => data.rows);
});
.then(data => data.rows)
})
super(options);
super(options)
}
}
module.exports = KeyvPostgres;
module.exports = KeyvPostgres

12
packages/keyv-postgres/test/test.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('@keyvhq/keyv');
const KeyvPostgres = require('this');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('@keyvhq/keyv')
const KeyvPostgres = require('this')
const store = () => new KeyvPostgres({ uri: 'postgresql://postgres:postgres@localhost:5432/keyv_test' });
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvPostgres({ uri: 'postgresql://postgres:postgres@localhost:5432/keyv_test' })
keyvTestSuite(test, Keyv, store)

5
packages/keyv-redis/package.json

@ -32,7 +32,7 @@
],
"scripts": {
"posttest:docker": "docker-compose down --rmi local",
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava",
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava",
"test:docker": "docker-compose up --build --abort-on-container-exit --exit-code-from test"
},
"license": "MIT",
@ -40,8 +40,5 @@
"require": [
"requirable"
]
},
"xo": {
"extends": "xo-lukechilds"
}
}

78
packages/keyv-redis/src/index.js

@ -1,79 +1,79 @@
'use strict';
'use strict'
const EventEmitter = require('events');
const pEvent = require('p-event');
const Redis = require('ioredis');
const EventEmitter = require('events')
const pEvent = require('p-event')
const Redis = require('ioredis')
class KeyvRedis extends EventEmitter {
constructor(uri, options) {
super();
constructor (uri, options) {
super()
if (uri instanceof Redis) {
this.redis = uri;
this.redis = uri
} else {
options = Object.assign(
{},
typeof uri === 'string' ? { uri } : uri,
options
);
this.redis = new Redis(options.uri, options);
)
this.redis = new Redis(options.uri, options)
}
this.redis.on('error', error => this.emit('error', error));
this.redis.on('error', error => this.emit('error', error))
}
async get(key) {
const value = await this.redis.get(key);
return value === null ? undefined : value;
async get (key) {
const value = await this.redis.get(key)
return value === null ? undefined : value
}
async set(key, value, ttl) {
async set (key, value, ttl) {
if (typeof value === 'undefined') {
return undefined;
return undefined
}
return typeof ttl === 'number' ?
this.redis.set(key, value, 'PX', ttl) :
this.redis.set(key, value);
return typeof ttl === 'number'
? this.redis.set(key, value, 'PX', ttl)
: this.redis.set(key, value)
}
async delete(key) {
const result = await this.redis.unlink(key);
return result > 0;
async delete (key) {
const result = await this.redis.unlink(key)
return result > 0
}
async clear() {
const stream = this.redis.scanStream({ match: `${this.namespace}:*` });
async clear () {
const stream = this.redis.scanStream({ match: `${this.namespace}:*` })
const keys = [];
stream.on('data', matchedKeys => keys.push(...matchedKeys));
await pEvent(stream, 'end');
const keys = []
stream.on('data', matchedKeys => keys.push(...matchedKeys))
await pEvent(stream, 'end')
if (keys.length > 0) {
await this.redis.unlink(keys);
await this.redis.unlink(keys)
}
}
async * iterator() {
const scan = this.redis.scan.bind(this.redis);
const get = this.redis.mget.bind(this.redis);
async function * iterate(curs, pattern) {
const [cursor, keys] = await scan(curs, 'MATCH', pattern);
const values = await get(keys);
async * iterator () {
const scan = this.redis.scan.bind(this.redis)
const get = this.redis.mget.bind(this.redis)
async function * iterate (curs, pattern) {
const [cursor, keys] = await scan(curs, 'MATCH', pattern)
const values = await get(keys)
for (const i in keys) {
if (Object.prototype.hasOwnProperty.call(keys, i)) {
const key = keys[i];
const value = values[i];
yield [key, value];
const key = keys[i]
const value = values[i]
yield [key, value]
}
}
if (cursor !== '0') {
yield * iterate(cursor, pattern);
yield * iterate(cursor, pattern)
}
}
yield * iterate(0, `${this.namespace ? this.namespace + ':' : ''}*`);
yield * iterate(0, `${this.namespace ? this.namespace + ':' : ''}*`)
}
}
module.exports = KeyvRedis;
module.exports = KeyvRedis

38
packages/keyv-redis/test/test.js

@ -1,24 +1,24 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('@keyvhq/keyv');
const KeyvRedis = require('this');
const Redis = require('ioredis');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('@keyvhq/keyv')
const KeyvRedis = require('this')
const Redis = require('ioredis')
require('dotenv').config();
const { REDIS_HOST = 'localhost' } = process.env;
const redisURI = `redis://${REDIS_HOST}`;
require('dotenv').config()
const { REDIS_HOST = 'localhost' } = process.env
const redisURI = `redis://${REDIS_HOST}`
const store = () => new KeyvRedis(redisURI);
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvRedis(redisURI)
keyvTestSuite(test, Keyv, store)
test('reuse a redis instance', async t => {
const redis = new Redis(redisURI);
redis.foo = 'bar';
const keyv = new KeyvRedis(redis);
t.is(keyv.redis.foo, 'bar');
const redis = new Redis(redisURI)
redis.foo = 'bar'
const keyv = new KeyvRedis(redis)
t.is(keyv.redis.foo, 'bar')
await keyv.set('foo', 'bar');
const value = await redis.get('foo');
t.true(value === 'bar');
t.true(await keyv.get('foo') === value);
});
await keyv.set('foo', 'bar')
const value = await redis.get('foo')
t.true(value === 'bar')
t.true(await keyv.get('foo') === value)
})

5
packages/keyv-sql/package.json

@ -27,15 +27,12 @@
"src"
],
"scripts": {
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava"
},
"license": "MIT",
"ava": {
"require": [
"requirable"
]
},
"xo": {
"extends": "xo-lukechilds"
}
}

88
packages/keyv-sql/src/index.js

@ -1,96 +1,96 @@
'use strict';
'use strict'
const EventEmitter = require('events');
const EventEmitter = require('events')
class KeyvSql extends EventEmitter {
constructor(options) {
super();
this.ttlSupport = false;
constructor (options) {
super()
this.ttlSupport = false
this.options = Object.assign({
table: 'keyv',
keySize: 255,
iterationLimit: 10
}, options);
}, options)
const createTable = this.options.dialect === 'mysql' ? `CREATE TABLE IF NOT EXISTS \`${this.options.table}\` (\`key\` VARCHAR(${this.options.keySize}) PRIMARY KEY, \`value\` TEXT)` : `CREATE TABLE IF NOT EXISTS "${this.options.table}" ("key" VARCHAR(${this.options.keySize}) PRIMARY KEY, "value" TEXT)`;
const createTable = this.options.dialect === 'mysql' ? `CREATE TABLE IF NOT EXISTS \`${this.options.table}\` (\`key\` VARCHAR(${this.options.keySize}) PRIMARY KEY, \`value\` TEXT)` : `CREATE TABLE IF NOT EXISTS "${this.options.table}" ("key" VARCHAR(${this.options.keySize}) PRIMARY KEY, "value" TEXT)`
const connected = this.options.connect()
.then(query => query(createTable).then(() => query))
.catch(error => this.emit('error', error));
.catch(error => this.emit('error', error))
this.query = sqlString => connected
.then(query => query(sqlString));
.then(query => query(sqlString))
}
get(key) {
const select = this.options.dialect === 'mysql' ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`;
get (key) {
const select = this.options.dialect === 'mysql' ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`
return this.query(select)
.then(rows => {
const row = rows[0];
const row = rows[0]
if (row === undefined) {
return undefined;
return undefined
}
return row.value;
});
return row.value
})
}
set(key, value) {
set (key, value) {
if (this.options.dialect === 'mysql') {
value = value.replace(/\\/g, '\\\\');
value = value.replace(/\\/g, '\\\\')
}
const upsert = this.options.dialect === 'postgres' ?
`INSERT INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}') ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."value"` :
(this.options.dialect === 'mysql' ?
`REPLACE INTO \`${this.options.table}\` (\`key\`, \`value\`) VALUES ('${key}', '${value}')` :
`REPLACE INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}')`);
const upsert = this.options.dialect === 'postgres'
? `INSERT INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}') ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."value"`
: (this.options.dialect === 'mysql'
? `REPLACE INTO \`${this.options.table}\` (\`key\`, \`value\`) VALUES ('${key}', '${value}')`
: `REPLACE INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}')`)
return this.query(upsert);
return this.query(upsert)
}
delete(key) {
const select = this.options.dialect === 'mysql' ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`;
const del = this.options.dialect === 'mysql' ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`;
delete (key) {
const select = this.options.dialect === 'mysql' ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`
const del = this.options.dialect === 'mysql' ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`
return this.query(select)
.then(rows => {
const row = rows[0];
const row = rows[0]
if (row === undefined) {
return false;
return false
}
return this.query(del)
.then(() => true);
});
.then(() => true)
})
}
clear() {
const del = this.options.dialect === 'mysql' ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%')` : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%')`;
clear () {
const del = this.options.dialect === 'mysql' ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%')` : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%')`
return this.query(del)
.then(() => undefined);
.then(() => undefined)
}
async * iterator() {
const limit = Number.parseInt(this.options.iterationLimit, 10);
const selectChunk = this.options.dialect === 'mysql' ? `SELECT * FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%') LIMIT ${limit} OFFSET ` : `SELECT * FROM "${this.options.table}" WHERE ("${this.options.table}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%') LIMIT ${limit} OFFSET `;
async * iterator () {
const limit = Number.parseInt(this.options.iterationLimit, 10)
const selectChunk = this.options.dialect === 'mysql' ? `SELECT * FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%') LIMIT ${limit} OFFSET ` : `SELECT * FROM "${this.options.table}" WHERE ("${this.options.table}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%') LIMIT ${limit} OFFSET `
async function * iterate(offset, query) {
const entries = await query(selectChunk + offset);
async function * iterate (offset, query) {
const entries = await query(selectChunk + offset)
if (entries.length === 0) {
return;
return
}
for (const entry of entries) {
offset += 1;
yield [entry.key, entry.value];
offset += 1
yield [entry.key, entry.value]
}
if (offset !== '0') {
yield * iterate(offset, query);
yield * iterate(offset, query)
}
}
yield * iterate(0, this.query);
yield * iterate(0, this.query)
}
}
module.exports = KeyvSql;
module.exports = KeyvSql

32
packages/keyv-sql/test/test.js

@ -1,33 +1,33 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('@keyvhq/keyv');
const KeyvSql = require('this');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('@keyvhq/keyv')
const KeyvSql = require('this')
const sqlite3 = require('sqlite3');
const pify = require('pify');
const sqlite3 = require('sqlite3')
const pify = require('pify')
class TestSqlite extends KeyvSql {
constructor(options) {
constructor (options) {
options = Object.assign({
dialect: 'sqlite',
db: 'test/testdb.sqlite'
}, options);
}, options)
options.connect = () => new Promise((resolve, reject) => {
const db = new sqlite3.Database(options.db, error => {
if (error) {
reject(error);
reject(error)
} else {
db.configure('busyTimeout', 30000);
resolve(db);
db.configure('busyTimeout', 30000)
resolve(db)
}
});
})
.then(db => pify(db.all).bind(db));
})
.then(db => pify(db.all).bind(db))
super(options);
super(options)
}
}
const store = () => new TestSqlite();
keyvTestSuite(test, Keyv, store);
const store = () => new TestSqlite()
keyvTestSuite(test, Keyv, store)

5
packages/keyv-sqlite/package.json

@ -33,15 +33,12 @@
"src"
],
"scripts": {
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava"
},
"license": "MIT",
"ava": {
"require": [
"requirable"
]
},
"xo": {
"extends": "xo-lukechilds"
}
}

28
packages/keyv-sqlite/src/index.js

@ -1,34 +1,34 @@
'use strict';
'use strict'
const KeyvSql = require('@keyvhq/keyv-sql');
const sqlite3 = require('sqlite3');
const pify = require('pify');
const KeyvSql = require('@keyvhq/keyv-sql')
const sqlite3 = require('sqlite3')
const pify = require('pify')
class KeyvSqlite extends KeyvSql {
constructor(options) {
constructor (options) {
options = Object.assign({
dialect: 'sqlite',
uri: 'sqlite://:memory:'
}, options);
options.db = options.uri.replace(/^sqlite:\/\//, '');
}, options)
options.db = options.uri.replace(/^sqlite:\/\//, '')
options.connect = () => new Promise((resolve, reject) => {
const db = new sqlite3.Database(options.db, error => {
if (error) {
reject(error);
reject(error)
} else {
if (options.busyTimeout) {
db.configure('busyTimeout', options.busyTimeout);
db.configure('busyTimeout', options.busyTimeout)
}
resolve(db);
resolve(db)
}
});
})
.then(db => pify(db.all).bind(db));
})
.then(db => pify(db.all).bind(db))
super(options);
super(options)
}
}
module.exports = KeyvSqlite;
module.exports = KeyvSqlite

12
packages/keyv-sqlite/test/test.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('@keyvhq/keyv');
const KeyvSqlite = require('../src/index.js');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('@keyvhq/keyv')
const KeyvSqlite = require('../src/index.js')
const store = () => new KeyvSqlite({ uri: 'sqlite://test/testdb.sqlite', busyTimeout: 30000 });
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvSqlite({ uri: 'sqlite://test/testdb.sqlite', busyTimeout: 30000 })
keyvTestSuite(test, Keyv, store)

6
packages/keyv-test-suite/package.json

@ -34,10 +34,6 @@
"release:major": "npm test && npm version major -m \"chore: release\" && git push origin master --tags && npm publish --access public",
"release:minor": "npm test && npm version minor -m \"chore: release\" && git push origin master --tags && npm publish --access public",
"release:patch": "npm test && npm version patch -m \"chore: release\" && git push origin master --tags && npm publish --access public",
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava"
},
"license": "MIT",
"xo": {
"extends": "xo-lukechilds"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava"
}
}

164
packages/keyv-test-suite/src/api.js

@ -1,126 +1,126 @@
const tk = require('timekeeper');
const delay = require('delay');
const tk = require('timekeeper')
const delay = require('delay')
const keyvApiTests = (test, Keyv, store) => {
test.beforeEach(async () => {
const keyv = new Keyv({ store: store() });
await keyv.clear();
});
const keyv = new Keyv({ store: store() })
await keyv.clear()
})
test.serial('.set(key, value) returns a Promise', t => {
const keyv = new Keyv({ store: store() });
t.true(keyv.set('foo', 'bar') instanceof Promise);
});
const keyv = new Keyv({ store: store() })
t.true(keyv.set('foo', 'bar') instanceof Promise)
})
test.serial('.set(key, value) resolves to true', async t => {
const keyv = new Keyv({ store: store() });
t.is(await keyv.set('foo', 'bar'), true);
});
const keyv = new Keyv({ store: store() })
t.is(await keyv.set('foo', 'bar'), true)
})
test.serial('.set(key, value) sets a value', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar');
t.is(await keyv.get('foo'), 'bar');
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar')
t.is(await keyv.get('foo'), 'bar')
})
test.serial('.set(key, value, ttl) sets a value that expires', async t => {
const ttl = 1000;
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar', ttl);
t.is(await keyv.get('foo'), 'bar');
const ttl = 1000
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar', ttl)
t.is(await keyv.get('foo'), 'bar')
if (keyv.store.ttlSupport === true) {
await delay(ttl + 1);
await delay(ttl + 1)
} else {
tk.freeze(Date.now() + ttl + 1);
tk.freeze(Date.now() + ttl + 1)
}
t.is(await keyv.get('foo'), undefined);
tk.reset();
});
t.is(await keyv.get('foo'), undefined)
tk.reset()
})
test.serial('.get(key) returns a Promise', t => {
const keyv = new Keyv({ store: store() });
t.true(keyv.get('foo') instanceof Promise);
});
const keyv = new Keyv({ store: store() })
t.true(keyv.get('foo') instanceof Promise)
})
test.serial('.get(key) resolves to value', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar');
t.is(await keyv.get('foo'), 'bar');
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar')
t.is(await keyv.get('foo'), 'bar')
})
test.serial('.get(key) with nonexistent key resolves to undefined', async t => {
const keyv = new Keyv({ store: store() });
t.is(await keyv.get('foo'), undefined);
});
const keyv = new Keyv({ store: store() })
t.is(await keyv.get('foo'), undefined)
})
test.serial('.delete(key) returns a Promise', t => {
const keyv = new Keyv({ store: store() });
t.true(keyv.delete('foo') instanceof Promise);
});
const keyv = new Keyv({ store: store() })
t.true(keyv.delete('foo') instanceof Promise)
})
test.serial('.delete(key) resolves to true', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar');
t.is(await keyv.delete('foo'), true);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar')
t.is(await keyv.delete('foo'), true)
})
test.serial('.delete(key) with nonexistent key resolves to false', async t => {
const keyv = new Keyv({ store: store() });
t.is(await keyv.delete(), false);
t.is(await keyv.delete('foo'), false);
});
const keyv = new Keyv({ store: store() })
t.is(await keyv.delete(), false)
t.is(await keyv.delete('foo'), false)
})
test.serial('.delete(key) deletes a key', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar');
t.is(await keyv.delete('foo'), true);
t.is(await keyv.get('foo'), undefined);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar')
t.is(await keyv.delete('foo'), true)
t.is(await keyv.get('foo'), undefined)
})
test.serial('.clear() returns a Promise', async t => {
const keyv = new Keyv({ store: store() });
const returnValue = keyv.clear();
t.true(returnValue instanceof Promise);
await returnValue;
});
const keyv = new Keyv({ store: store() })
const returnValue = keyv.clear()
t.true(returnValue instanceof Promise)
await returnValue
})
test.serial('.clear() resolves to undefined', async t => {
const keyv = new Keyv({ store: store() });
t.is(await keyv.clear(), undefined);
await keyv.set('foo', 'bar');
t.is(await keyv.clear(), undefined);
});
const keyv = new Keyv({ store: store() })
t.is(await keyv.clear(), undefined)
await keyv.set('foo', 'bar')
t.is(await keyv.clear(), undefined)
})
test.serial('.clear() deletes all key/value pairs', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar');
await keyv.set('fizz', 'buzz');
await keyv.clear();
t.is(await keyv.get('foo'), undefined);
t.is(await keyv.get('fizz'), undefined);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar')
await keyv.set('fizz', 'buzz')
await keyv.clear()
t.is(await keyv.get('foo'), undefined)
t.is(await keyv.get('fizz'), undefined)
})
test.serial('.has(key) returns a Promise', t => {
const keyv = new Keyv({ store: store() });
t.true(keyv.has('foo') instanceof Promise);
});
const keyv = new Keyv({ store: store() })
t.true(keyv.has('foo') instanceof Promise)
})
test.serial('.has(key) resolves to true', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 'bar');
t.is(await keyv.has('foo'), true);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 'bar')
t.is(await keyv.has('foo'), true)
})
test.serial('.has(key) with nonexistent key resolves to false', async t => {
const keyv = new Keyv({ store: store() });
t.is(await keyv.has('foo'), false);
});
const keyv = new Keyv({ store: store() })
t.is(await keyv.has('foo'), false)
})
test.after.always(async () => {
const keyv = new Keyv({ store: store() });
await keyv.clear();
});
};
const keyv = new Keyv({ store: store() })
await keyv.clear()
})
}
module.exports = keyvApiTests;
module.exports = keyvApiTests

22
packages/keyv-test-suite/src/index.js

@ -1,20 +1,20 @@
const keyvApiTests = require('./api.js');
const keyvValueTests = require('./values.js');
const keyvNamespaceTests = require('./namespace.js');
const keyvIteratorTests = require('./iteration.js');
const keyvApiTests = require('./api.js')
const keyvValueTests = require('./values.js')
const keyvNamespaceTests = require('./namespace.js')
const keyvIteratorTests = require('./iteration.js')
const keyvTestSuite = (test, Keyv, store) => {
keyvIteratorTests(test, Keyv, store);
keyvApiTests(test, Keyv, store);
keyvValueTests(test, Keyv, store);
keyvNamespaceTests(test, Keyv, store);
};
keyvIteratorTests(test, Keyv, store)
keyvApiTests(test, Keyv, store)
keyvValueTests(test, Keyv, store)
keyvNamespaceTests(test, Keyv, store)
}
Object.assign(keyvTestSuite, {
keyvApiTests,
keyvValueTests,
keyvNamespaceTests,
keyvIteratorTests
});
})
module.exports = keyvTestSuite;
module.exports = keyvTestSuite

36
packages/keyv-test-suite/src/iteration.js

@ -1,30 +1,30 @@
const keyvIteratorTests = (test, Keyv, store) => {
test.beforeEach(async () => {
const keyv = new Keyv({ store: store() });
await keyv.clear();
});
const keyv = new Keyv({ store: store() })
await keyv.clear()
})
test.serial('.iterator() returns an asyncIterator', t => {
const keyv = new Keyv({ store: store() });
t.true(typeof keyv.iterator()[Symbol.asyncIterator] === 'function');
});
const keyv = new Keyv({ store: store() })
t.true(typeof keyv.iterator()[Symbol.asyncIterator] === 'function')
})
test.serial('iterator() iterates over all values', async t => {
const keyv = new Keyv({ store: store() });
const map = new Map(Array.from({ length: 5 }).fill(0).map((x, i) => [String(i), String(i + 10)]));
const toResolve = [];
const keyv = new Keyv({ store: store() })
const map = new Map(Array.from({ length: 5 }).fill(0).map((x, i) => [String(i), String(i + 10)]))
const toResolve = []
for (const [key, value] of map) {
toResolve.push(keyv.set(key, value));
toResolve.push(keyv.set(key, value))
}
await Promise.all(toResolve);
t.plan(map.size);
await Promise.all(toResolve)
t.plan(map.size)
for await (const [key, value] of keyv.iterator()) {
const doesKeyExist = map.has(key);
const isValueSame = map.get(key) === value;
t.true(doesKeyExist && isValueSame);
const doesKeyExist = map.has(key)
const isValueSame = map.get(key) === value
t.true(doesKeyExist && isValueSame)
}
});
};
})
}
module.exports = keyvIteratorTests;
module.exports = keyvIteratorTests

102
packages/keyv-test-suite/src/namespace.js

@ -1,64 +1,64 @@
const keyvNamepsaceTests = (test, Keyv, store) => {
test.beforeEach(async () => {
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' });
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' });
await keyv1.clear();
await keyv2.clear();
});
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' })
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' })
await keyv1.clear()
await keyv2.clear()
})
test.serial('namespaced set/get don\'t collide', async t => {
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' });
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' });
await keyv1.set('foo', 'keyv1');
await keyv2.set('foo', 'keyv2');
t.is(await keyv1.get('foo'), 'keyv1');
t.is(await keyv2.get('foo'), 'keyv2');
});
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' })
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' })
await keyv1.set('foo', 'keyv1')
await keyv2.set('foo', 'keyv2')
t.is(await keyv1.get('foo'), 'keyv1')
t.is(await keyv2.get('foo'), 'keyv2')
})
test.serial('namespaced delete only deletes from current namespace', async t => {
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' });
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' });
await keyv1.set('foo', 'keyv1');
await keyv2.set('foo', 'keyv2');
t.is(await keyv1.delete('foo'), true);
t.is(await keyv1.get('foo'), undefined);
t.is(await keyv2.get('foo'), 'keyv2');
});
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' })
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' })
await keyv1.set('foo', 'keyv1')
await keyv2.set('foo', 'keyv2')
t.is(await keyv1.delete('foo'), true)
t.is(await keyv1.get('foo'), undefined)
t.is(await keyv2.get('foo'), 'keyv2')
})
test.serial('namespaced clear only clears current namespace', async t => {
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' });
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' });
await keyv1.set('foo', 'keyv1');
await keyv1.set('bar', 'keyv1');
await keyv2.set('foo', 'keyv2');
await keyv2.set('bar', 'keyv2');
await keyv1.clear();
t.is(await keyv1.get('foo'), undefined);
t.is(await keyv1.get('bar'), undefined);
t.is(await keyv2.get('foo'), 'keyv2');
t.is(await keyv2.get('bar'), 'keyv2');
});
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' })
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' })
await keyv1.set('foo', 'keyv1')
await keyv1.set('bar', 'keyv1')
await keyv2.set('foo', 'keyv2')
await keyv2.set('bar', 'keyv2')
await keyv1.clear()
t.is(await keyv1.get('foo'), undefined)
t.is(await keyv1.get('bar'), undefined)
t.is(await keyv2.get('foo'), 'keyv2')
t.is(await keyv2.get('bar'), 'keyv2')
})
test.serial('no namespaced clear doesn\'t clears all stores', async t => {
const keyv1 = new Keyv({ store: store(), namespace: false });
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' });
await keyv1.set('foo', 'keyv1');
await keyv1.set('bar', 'keyv1');
await keyv2.set('foo', 'keyv2');
await keyv2.set('bar', 'keyv2');
await keyv1.clear();
t.is(await keyv1.get('foo'), 'keyv1');
t.is(await keyv1.get('bar'), 'keyv1');
t.is(await keyv2.get('foo'), 'keyv2');
t.is(await keyv2.get('bar'), 'keyv2');
});
const keyv1 = new Keyv({ store: store(), namespace: false })
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' })
await keyv1.set('foo', 'keyv1')
await keyv1.set('bar', 'keyv1')
await keyv2.set('foo', 'keyv2')
await keyv2.set('bar', 'keyv2')
await keyv1.clear()
t.is(await keyv1.get('foo'), 'keyv1')
t.is(await keyv1.get('bar'), 'keyv1')
t.is(await keyv2.get('foo'), 'keyv2')
t.is(await keyv2.get('bar'), 'keyv2')
})
test.after.always(async () => {
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' });
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' });
await keyv1.clear();
await keyv2.clear();
});
};
const keyv1 = new Keyv({ store: store(), namespace: 'keyv1' })
const keyv2 = new Keyv({ store: store(), namespace: 'keyv2' })
await keyv1.clear()
await keyv2.clear()
})
}
module.exports = keyvNamepsaceTests;
module.exports = keyvNamepsaceTests

88
packages/keyv-test-suite/src/values.js

@ -1,65 +1,65 @@
const keyvValueTests = (test, Keyv, store) => {
test.beforeEach(async () => {
const keyv = new Keyv({ store: store() });
await keyv.clear();
});
const keyv = new Keyv({ store: store() })
await keyv.clear()
})
test.serial('value can be false', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', false);
t.is(await keyv.get('foo'), false);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', false)
t.is(await keyv.get('foo'), false)
})
test.serial('value can be null', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', null);
t.is(await keyv.get('foo'), null);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', null)
t.is(await keyv.get('foo'), null)
})
test.serial('value can be undefined', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', undefined);
t.is(await keyv.get('foo'), undefined);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', undefined)
t.is(await keyv.get('foo'), undefined)
})
test.serial('value can be a number', async t => {
const keyv = new Keyv({ store: store() });
await keyv.set('foo', 0);
t.is(await keyv.get('foo'), 0);
});
const keyv = new Keyv({ store: store() })
await keyv.set('foo', 0)
t.is(await keyv.get('foo'), 0)
})
test.serial('value can be an object', async t => {
const keyv = new Keyv({ store: store() });
const value = { fizz: 'buzz' };
await keyv.set('foo', value);
t.deepEqual(await keyv.get('foo'), value);
});
const keyv = new Keyv({ store: store() })
const value = { fizz: 'buzz' }
await keyv.set('foo', value)
t.deepEqual(await keyv.get('foo'), value)
})
test.serial('value can be a buffer', async t => {
const keyv = new Keyv({ store: store() });
const buf = Buffer.from('bar');
await keyv.set('foo', buf);
t.true(buf.equals(await keyv.get('foo')));
});
const keyv = new Keyv({ store: store() })
const buf = Buffer.from('bar')
await keyv.set('foo', buf)
t.true(buf.equals(await keyv.get('foo')))
})
test.serial('value can be an object containing a buffer', async t => {
const keyv = new Keyv({ store: store() });
const value = { buff: Buffer.from('buzz') };
await keyv.set('foo', value);
t.deepEqual(await keyv.get('foo'), value);
});
const keyv = new Keyv({ store: store() })
const value = { buff: Buffer.from('buzz') }
await keyv.set('foo', value)
t.deepEqual(await keyv.get('foo'), value)
})
test.serial('value can contain quotes', async t => {
const keyv = new Keyv({ store: store() });
const value = '"';
await keyv.set('foo', value);
t.deepEqual(await keyv.get('foo'), value);
});
const keyv = new Keyv({ store: store() })
const value = '"'
await keyv.set('foo', value)
t.deepEqual(await keyv.get('foo'), value)
})
test.after.always(async () => {
const keyv = new Keyv({ store: store() });
await keyv.clear();
});
};
const keyv = new Keyv({ store: store() })
await keyv.clear()
})
}
module.exports = keyvValueTests;
module.exports = keyvValueTests

10
packages/keyv-test-suite/test/unit.js

@ -1,6 +1,6 @@
const test = require('ava');
const Keyv = require('@keyvhq/keyv');
const keyvTestSuite = require('this');
const test = require('ava')
const Keyv = require('@keyvhq/keyv')
const keyvTestSuite = require('this')
const store = () => new Map();
keyvTestSuite(test, Keyv, store);
const store = () => new Map()
keyvTestSuite(test, Keyv, store)

9
packages/keyv/package.json

@ -26,11 +26,8 @@
"src"
],
"scripts": {
"test": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava test/keyv.js",
"test:full": "xo && nyc --no-clean --temp-dir ../../.nyc_output ava --serial"
"test": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava test/keyv.js",
"test:full": "standard && nyc --no-clean --temp-dir ../../.nyc_output ava --serial"
},
"license": "MIT",
"xo": {
"extends": "xo-lukechilds"
}
"license": "MIT"
}

108
packages/keyv/src/index.js

@ -1,11 +1,11 @@
'use strict';
'use strict'
const EventEmitter = require('events');
const JSONB = require('json-buffer');
const EventEmitter = require('events')
const JSONB = require('json-buffer')
class Keyv extends EventEmitter {
constructor(options) {
super();
constructor (options) {
super()
this.options = Object.assign(
{
namespace: 'keyv',
@ -13,121 +13,121 @@ class Keyv extends EventEmitter {
deserialize: JSONB.parse
},
options
);
)
this.store = this.options.store || new Map();
this.store = this.options.store || new Map()
this.store.namespace = this.options.namespace;
this.store.namespace = this.options.namespace
if (typeof this.store.on === 'function') {
this.store.on('error', error => this.emit('error', error));
this.store.on('error', error => this.emit('error', error))
}
const generateIterator = iterator => async function * () {
for await (const [key, raw] of (typeof iterator === 'function' ? iterator() : iterator)) {
const data = (typeof raw === 'string') ? this.options.deserialize(raw) : raw;
const data = (typeof raw === 'string') ? this.options.deserialize(raw) : raw
if (!key.includes(this.options.namespace) || typeof data !== 'object') {
continue;
continue
}
if (typeof data.expires === 'number' && Date.now() > data.expires) {
this.delete(key);
continue;
this.delete(key)
continue
}
yield [this._getKeyUnprefix(key), data.value];
yield [this._getKeyUnprefix(key), data.value]
}
}
};
// Attach iterators
if (typeof this.store[Symbol.iterator] === 'function' && this.store instanceof Map) {
this.iterator = generateIterator(this.store);
this.iterator = generateIterator(this.store)
} else if (typeof this.store.iterator === 'function') {
this.iterator = generateIterator(this.store.iterator.bind(this.store));
this.iterator = generateIterator(this.store.iterator.bind(this.store))
} else {
this.iteratorSupport = false;
this.iteratorSupport = false
}
}
_getKeyPrefix(key) {
return this.options.namespace ? `${this.options.namespace}:${key}` : key;
_getKeyPrefix (key) {
return this.options.namespace ? `${this.options.namespace}:${key}` : key
}
_getKeyUnprefix(key) {
return this.options.namespace ? key.split(':').splice(1).join(':') : key;
_getKeyUnprefix (key) {
return this.options.namespace ? key.split(':').splice(1).join(':') : key
}
get(key, options) {
const keyPrefixed = this._getKeyPrefix(key);
const store = this.store;
get (key, options) {
const keyPrefixed = this._getKeyPrefix(key)
const store = this.store
return Promise.resolve()
.then(() => store.get(keyPrefixed))
.then(data => {
return (typeof data === 'string') ? this.options.deserialize(data) : data;
return (typeof data === 'string') ? this.options.deserialize(data) : data
})
.then(data => {
if (data === undefined) {
return undefined;
return undefined
}
if (typeof data.expires === 'number' && Date.now() > data.expires) {
this.delete(key);
return undefined;
this.delete(key)
return undefined
}
return (options && options.raw) ? data : data.value;
});
return (options && options.raw) ? data : data.value
})
}
has(key) {
const keyPrefixed = this._getKeyPrefix(key);
const store = this.store;
has (key) {
const keyPrefixed = this._getKeyPrefix(key)
const store = this.store
if (typeof store.has === 'function') {
return Promise.resolve()
.then(() => store.has(keyPrefixed));
.then(() => store.has(keyPrefixed))
}
return Promise.resolve()
.then(() => store.get(keyPrefixed))
.then(data => data !== undefined);
.then(data => data !== undefined)
}
set(key, value, ttl) {
const keyPrefixed = this._getKeyPrefix(key);
set (key, value, ttl) {
const keyPrefixed = this._getKeyPrefix(key)
if (typeof ttl === 'undefined') {
ttl = this.options.ttl;
ttl = this.options.ttl
}
if (ttl === 0) {
ttl = undefined;
ttl = undefined
}
const store = this.store;
const store = this.store
return Promise.resolve()
.then(() => {
const expires = (typeof ttl === 'number') ? (Date.now() + ttl) : null;
value = { value, expires };
return this.options.serialize(value);
const expires = (typeof ttl === 'number') ? (Date.now() + ttl) : null
value = { value, expires }
return this.options.serialize(value)
})
.then(value => store.set(keyPrefixed, value, ttl))
.then(() => true);
.then(() => true)
}
delete(key) {
const keyPrefixed = this._getKeyPrefix(key);
const store = this.store;
delete (key) {
const keyPrefixed = this._getKeyPrefix(key)
const store = this.store
return Promise.resolve()
.then(() => store.delete(keyPrefixed));
.then(() => store.delete(keyPrefixed))
}
clear() {
clear () {
if (!this.options.namespace) {
return Promise.resolve().then(() => undefined);
return Promise.resolve().then(() => undefined)
}
const store = this.store;
const store = this.store
return Promise.resolve()
.then(() => store.clear());
.then(() => store.clear())
}
}
module.exports = Keyv;
module.exports = Keyv

228
packages/keyv/test/keyv.js

@ -1,149 +1,149 @@
const test = require('ava');
const tk = require('timekeeper');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('this');
const test = require('ava')
const tk = require('timekeeper')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('this')
test.serial('Keyv is a class', t => {
t.is(typeof Keyv, 'function');
t.throws(() => Keyv()); // eslint-disable-line new-cap
t.notThrows(() => new Keyv());
});
t.is(typeof Keyv, 'function')
t.throws(() => Keyv()) // eslint-disable-line new-cap
t.notThrows(() => new Keyv())
})
test.serial('Keyv accepts storage adapters', async t => {
const store = new Map();
const keyv = new Keyv({ store });
t.is(store.size, 0);
await keyv.set('foo', 'bar');
t.is(await keyv.get('foo'), 'bar');
t.is(store.size, 1);
});
const store = new Map()
const keyv = new Keyv({ store })
t.is(store.size, 0)
await keyv.set('foo', 'bar')
t.is(await keyv.get('foo'), 'bar')
t.is(store.size, 1)
})
test.serial('Keyv passes tll info to stores', async t => {
t.plan(1);
const store = new Map();
const storeSet = store.set;
t.plan(1)
const store = new Map()
const storeSet = store.set
store.set = (key, value, ttl) => {
t.is(ttl, 100);
storeSet.call(store, key, value, ttl);
};
t.is(ttl, 100)
storeSet.call(store, key, value, ttl)
}
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar', 100);
});
const keyv = new Keyv({ store })
await keyv.set('foo', 'bar', 100)
})
test.serial('Keyv respects default tll option', async t => {
const store = new Map();
const keyv = new Keyv({ store, ttl: 100 });
await keyv.set('foo', 'bar');
t.is(await keyv.get('foo'), 'bar');
tk.freeze(Date.now() + 150);
t.is(await keyv.get('foo'), undefined);
t.is(store.size, 0);
tk.reset();
});
const store = new Map()
const keyv = new Keyv({ store, ttl: 100 })
await keyv.set('foo', 'bar')
t.is(await keyv.get('foo'), 'bar')
tk.freeze(Date.now() + 150)
t.is(await keyv.get('foo'), undefined)
t.is(store.size, 0)
tk.reset()
})
test.serial('.set(key, value, ttl) overwrites default tll option', async t => {
const startTime = Date.now();
tk.freeze(startTime);
const store = new Map();
const keyv = new Keyv({ store, ttl: 200 });
await keyv.set('foo', 'bar');
await keyv.set('fizz', 'buzz', 100);
await keyv.set('ping', 'pong', 300);
t.is(await keyv.get('foo'), 'bar');
t.is(await keyv.get('fizz'), 'buzz');
t.is(await keyv.get('ping'), 'pong');
tk.freeze(startTime + 150);
t.is(await keyv.get('foo'), 'bar');
t.is(await keyv.get('fizz'), undefined);
t.is(await keyv.get('ping'), 'pong');
tk.freeze(startTime + 250);
t.is(await keyv.get('foo'), undefined);
t.is(await keyv.get('ping'), 'pong');
tk.freeze(startTime + 350);
t.is(await keyv.get('ping'), undefined);
tk.reset();
});
const startTime = Date.now()
tk.freeze(startTime)
const store = new Map()
const keyv = new Keyv({ store, ttl: 200 })
await keyv.set('foo', 'bar')
await keyv.set('fizz', 'buzz', 100)
await keyv.set('ping', 'pong', 300)
t.is(await keyv.get('foo'), 'bar')
t.is(await keyv.get('fizz'), 'buzz')
t.is(await keyv.get('ping'), 'pong')
tk.freeze(startTime + 150)
t.is(await keyv.get('foo'), 'bar')
t.is(await keyv.get('fizz'), undefined)
t.is(await keyv.get('ping'), 'pong')
tk.freeze(startTime + 250)
t.is(await keyv.get('foo'), undefined)
t.is(await keyv.get('ping'), 'pong')
tk.freeze(startTime + 350)
t.is(await keyv.get('ping'), undefined)
tk.reset()
})
test.serial('.set(key, value, ttl) where ttl is "0" overwrites default tll option and sets key to never expire', async t => {
const startTime = Date.now();
tk.freeze(startTime);
const store = new Map();
const keyv = new Keyv({ store, ttl: 200 });
await keyv.set('foo', 'bar', 0);
t.is(await keyv.get('foo'), 'bar');
tk.freeze(startTime + 250);
t.is(await keyv.get('foo'), 'bar');
tk.reset();
});
const startTime = Date.now()
tk.freeze(startTime)
const store = new Map()
const keyv = new Keyv({ store, ttl: 200 })
await keyv.set('foo', 'bar', 0)
t.is(await keyv.get('foo'), 'bar')
tk.freeze(startTime + 250)
t.is(await keyv.get('foo'), 'bar')
tk.reset()
})
test.serial('.get(key, {raw: true}) returns the raw object instead of the value', async t => {
const store = new Map();
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar');
const value = await keyv.get('foo');
const rawObject = await keyv.get('foo', { raw: true });
t.is(value, 'bar');
t.is(rawObject.value, 'bar');
});
const store = new Map()
const keyv = new Keyv({ store })
await keyv.set('foo', 'bar')
const value = await keyv.get('foo')
const rawObject = await keyv.get('foo', { raw: true })
t.is(value, 'bar')
t.is(rawObject.value, 'bar')
})
test.serial('Keyv uses custom serializer when provided instead of json-buffer', async t => {
t.plan(3);
const store = new Map();
t.plan(3)
const store = new Map()
const serialize = data => {
t.pass();
return JSON.stringify(data);
};
t.pass()
return JSON.stringify(data)
}
const deserialize = data => {
t.pass();
return JSON.parse(data);
};
t.pass()
return JSON.parse(data)
}
const keyv = new Keyv({ store, serialize, deserialize });
await keyv.set('foo', 'bar');
t.is(await keyv.get('foo'), 'bar');
});
const keyv = new Keyv({ store, serialize, deserialize })
await keyv.set('foo', 'bar')
t.is(await keyv.get('foo'), 'bar')
})
test.serial('Keyv supports async serializer/deserializer', async t => {
t.plan(3);
const store = new Map();
t.plan(3)
const store = new Map()
const serialize = async data => {
t.pass();
return JSON.stringify(data);
};
t.pass()
return JSON.stringify(data)
}
const deserialize = async data => {
t.pass();
return JSON.parse(data);
};
t.pass()
return JSON.parse(data)
}
const keyv = new Keyv({ store, serialize, deserialize });
await keyv.set('foo', 'bar');
t.is(await keyv.get('foo'), 'bar');
});
const keyv = new Keyv({ store, serialize, deserialize })
await keyv.set('foo', 'bar')
t.is(await keyv.get('foo'), 'bar')
})
test.serial('Keyv uses a default namespace', async t => {
const store = new Map();
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar');
t.is([...store.keys()][0], 'keyv:foo');
});
const store = new Map()
const keyv = new Keyv({ store })
await keyv.set('foo', 'bar')
t.is([...store.keys()][0], 'keyv:foo')
})
test.serial('Default namespace can be overridden', async t => {
const store = new Map();
const keyv = new Keyv({ store, namespace: 'magic' });
await keyv.set('foo', 'bar');
t.is([...store.keys()][0], 'magic:foo');
});
const store = new Map()
const keyv = new Keyv({ store, namespace: 'magic' })
await keyv.set('foo', 'bar')
t.is([...store.keys()][0], 'magic:foo')
})
test.serial('An empty namespace stores the key as-is', async t => {
const store = new Map();
const keyv = new Keyv({ store, namespace: '' });
await keyv.set(42, 'foo');
t.is([...store.keys()][0], 42);
});
const store = () => new Map();
keyvTestSuite(test, Keyv, store);
const store = new Map()
const keyv = new Keyv({ store, namespace: '' })
await keyv.set(42, 'foo')
t.is([...store.keys()][0], 42)
})
const store = () => new Map()
keyvTestSuite(test, Keyv, store)

12
packages/keyv/test/storage-adapters/mongodb.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('this');
const KeyvMongo = require('@keyvhq/keyv-mongo');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('this')
const KeyvMongo = require('@keyvhq/keyv-mongo')
const store = () => new KeyvMongo('mongodb://127.0.0.1:27017');
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvMongo('mongodb://127.0.0.1:27017')
keyvTestSuite(test, Keyv, store)

12
packages/keyv/test/storage-adapters/mysql.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('this');
const KeyvMysql = require('@keyvhq/keyv-mysql');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('this')
const KeyvMysql = require('@keyvhq/keyv-mysql')
const store = () => new KeyvMysql('mysql://mysql@localhost/keyv_test');
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvMysql('mysql://mysql@localhost/keyv_test')
keyvTestSuite(test, Keyv, store)

12
packages/keyv/test/storage-adapters/postgresql.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('this');
const KeyvPostgres = require('postgres');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('this')
const KeyvPostgres = require('postgres')
const store = () => new KeyvPostgres('postgresql://postgres@localhost:5432/keyv_test');
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvPostgres('postgresql://postgres@localhost:5432/keyv_test')
keyvTestSuite(test, Keyv, store)

12
packages/keyv/test/storage-adapters/redis.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('this');
const KeyvRedis = require('redis');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('this')
const KeyvRedis = require('redis')
const store = () => new KeyvRedis('redis://localhost');
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvRedis('redis://localhost')
keyvTestSuite(test, Keyv, store)

12
packages/keyv/test/storage-adapters/sqlite.js

@ -1,7 +1,7 @@
const test = require('ava');
const keyvTestSuite = require('@keyvhq/keyv-test-suite');
const Keyv = require('this');
const KeyvSqlite = require('sqlite');
const test = require('ava')
const keyvTestSuite = require('@keyvhq/keyv-test-suite')
const Keyv = require('this')
const KeyvSqlite = require('sqlite')
const store = () => new KeyvSqlite('sqlite://test/testdb.sqlite');
keyvTestSuite(test, Keyv, store);
const store = () => new KeyvSqlite('sqlite://test/testdb.sqlite')
keyvTestSuite(test, Keyv, store)

Loading…
Cancel
Save