@iobroker/db-objects-file
Advanced tools
Comparing version 0.0.14 to 1.0.0
module.exports = { | ||
Client: require('@iobroker/db-objects-redis').Client, | ||
Server: require('./lib/objects/objectsInMemServerRedis.js'), | ||
Server: require('./lib/objects/objectsInMemServerClass.js'), | ||
getDefaultObjectsPort: _host => { | ||
@@ -5,0 +5,0 @@ return 9001; |
@@ -21,2 +21,3 @@ /** | ||
const utils = require('@iobroker/db-objects-redis').objectsUtils; | ||
const tools = require('@iobroker/db-base').tools; | ||
@@ -59,8 +60,2 @@ const RedisHandler = require('@iobroker/db-base').redisHandler; | ||
this.server = { | ||
app: null, | ||
server: null, | ||
io: null, | ||
settings: this.settings | ||
}; | ||
this.serverConnections = {}; | ||
@@ -79,12 +74,14 @@ this.namespaceObjects = (this.settings.redisNamespace || (settings.connection && settings.connection.redisNamespace) || 'cfg') + '.'; | ||
this._initRedisServer(this.settings.connection, this.server); | ||
this._initRedisServer(this.settings.connection).then(() => { | ||
this.log.debug(this.namespace + ' ' + (settings.secure ? 'Secure ' : '') + ' Redis inMem-objects listening on port ' + (settings.port || 9001)); | ||
if (this.settings.connected) { | ||
setImmediate(() => this.settings.connected(this)); | ||
} | ||
if (typeof this.settings.connected === 'function') { | ||
setImmediate(() => this.settings.connected()); | ||
} | ||
}).catch(e => { | ||
this.log.error(this.namespace + ' Cannot start inMem-objects on port ' + (settings.port || 9001) + ': ' + e.message); | ||
process.exit(24); // todo: replace it with exitcode | ||
}); | ||
} | ||
addPreserveSettings(_settings) { | ||
// when Redis is used this is done by objectsInRedis already | ||
} | ||
/** | ||
@@ -99,4 +96,4 @@ * Separate Namespace from ID and return both | ||
let ns = this.namespaceObjects; | ||
let id; | ||
let name; | ||
let id = null; | ||
let name = ''; | ||
let isMeta; | ||
@@ -134,3 +131,3 @@ if (Array.isArray(idWithNamespace)) { | ||
id = fileIdDetails[1]; | ||
name = fileIdDetails[2]|| ''; | ||
name = fileIdDetails[2] || ''; | ||
isMeta = undefined; | ||
@@ -274,4 +271,3 @@ } else { | ||
if (!this.knownScripts[data[0]]) { | ||
handler.sendError(responseId, new Error('Unknown Script ' + data[0])); | ||
return; | ||
return void handler.sendError(responseId, new Error('Unknown Script ' + data[0])); | ||
} | ||
@@ -291,19 +287,14 @@ if (this.knownScripts[data[0]].design) { | ||
} | ||
this.getObjectView(scriptDesign, scriptSearch, { | ||
startkey: data[3], | ||
endkey: data[4], | ||
include_docs: true | ||
}, (err, objs) => { | ||
if (err) { | ||
handler && handler.sendError(responseId, new Error('getObjectView Error for ' + scriptDesign + '/' + scriptSearch + ': ' + err)); | ||
return; | ||
} | ||
const res = []; | ||
objs.rows.forEach(obj => | ||
res.push(JSON.stringify(this.dataset[obj.value._id || obj.id]))); | ||
handler && handler.sendArray(responseId, res); | ||
}); | ||
return; | ||
let objs; | ||
try { | ||
objs = this._getObjectView(scriptDesign, scriptSearch, { | ||
startkey: data[3], | ||
endkey: data[4], | ||
include_docs: true | ||
}); | ||
} catch (err) { | ||
return void handler.sendError(responseId, new Error('_getObjectView Error for ' + scriptDesign + '/' + scriptSearch + ': ' + err)); | ||
} | ||
const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id])); | ||
handler.sendArray(responseId, res); | ||
} | ||
@@ -315,21 +306,13 @@ } else if (this.knownScripts[data[0]].func && data.length > 4) { | ||
} | ||
this._applyView(scriptFunc, { | ||
const objs = this._applyView(scriptFunc, { | ||
startkey: data[3], | ||
endkey: data[4], | ||
include_docs: true | ||
}, (err, objs) => { | ||
if (err) { | ||
handler && handler.sendError(responseId, new Error('getObjectView Error ' + err)); | ||
return; | ||
} | ||
const res = []; | ||
}); | ||
const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id])); | ||
objs.rows.forEach(obj => | ||
res.push(JSON.stringify(this.dataset[obj.value._id || obj.id]))); | ||
handler && handler.sendArray(responseId, res); | ||
}); | ||
return; | ||
return void handler.sendArray(responseId, res); | ||
} else { | ||
handler.sendError(responseId, new Error('Unknown LUA script eval call ' + JSON.stringify(data))); | ||
} | ||
handler.sendError(responseId, new Error('Unknown LUA script eval call ' + JSON.stringify(data))); | ||
}); | ||
@@ -342,4 +325,3 @@ | ||
if (namespace === this.namespaceObj) { // a "set" always comes afterwards, so do not publish | ||
handler.sendInteger(responseId, 0); | ||
return; // do not publish for now | ||
return void handler.sendInteger(responseId, 0); // do not publish for now | ||
} | ||
@@ -353,4 +335,3 @@ const publishCount = this.publishAll(namespace.substr(0, namespace.length - 1), id, JSON.parse(data[1])); | ||
if (!data || !data.length) { | ||
handler.sendArray(responseId, []); | ||
return; | ||
return void handler.sendArray(responseId, []); | ||
} | ||
@@ -370,12 +351,10 @@ const {namespace, isMeta} = this._normalizeId(data[0]); | ||
}); | ||
this.getObjects(keys, (err, result) => { | ||
if (err || !result) { | ||
handler && handler.sendError(responseId, new Error('ERROR getObjects: ' + err)); // TODO | ||
return; | ||
} | ||
for (let i = 0; i < result.length; i++) { | ||
result[i] = result[i] ? JSON.stringify(result[i]) : null; | ||
} | ||
handler && handler.sendArray(responseId, result); | ||
}); | ||
let result; | ||
try { | ||
result = this._getObjects(keys); | ||
} catch (err) { | ||
return void handler.sendError(responseId, new Error('ERROR _getObjects: ' + err.message)); | ||
} | ||
result = result.map(el => el ? JSON.stringify(el) : null); | ||
handler.sendArray(responseId, result); | ||
} else if (namespace === this.namespaceFile) { | ||
@@ -392,3 +371,3 @@ // Handle request for Meta data for files | ||
} | ||
this.loadFileSettings(id); | ||
this._loadFileSettings(id); | ||
if (!this.fileOptions[id] || !this.fileOptions[id][name]) { | ||
@@ -398,3 +377,3 @@ response.push(null); | ||
} | ||
const obj = this.clone(this.fileOptions[id][name]); | ||
const obj = this._clone(this.fileOptions[id][name]); | ||
try { | ||
@@ -427,9 +406,8 @@ // @ts-ignore | ||
if (namespace === this.namespaceObj) { | ||
this.getObject(id, (err, result) => { | ||
if (err || !result) { | ||
handler && handler.sendNull(responseId); | ||
} else { | ||
handler && handler.sendBulk(responseId, JSON.stringify(result)); | ||
} | ||
}); | ||
const result = this._getObject(id); | ||
if (!result) { | ||
handler.sendNull(responseId); | ||
} else { | ||
handler.sendBulk(responseId, JSON.stringify(result)); | ||
} | ||
} else if (namespace === this.namespaceFile) { | ||
@@ -442,7 +420,6 @@ // Handle request for Meta data for files | ||
} catch (err) { | ||
handler.sendNull(responseId); | ||
return; | ||
return void handler.sendNull(responseId); | ||
} | ||
if (stats.isDirectory()) { | ||
handler.sendBulk(responseId, JSON.stringify({ | ||
return void handler.sendBulk(responseId, JSON.stringify({ | ||
file: name, | ||
@@ -452,11 +429,9 @@ stats: {}, | ||
})); | ||
return; | ||
} | ||
this.loadFileSettings(id); | ||
this._loadFileSettings(id); | ||
if (!this.fileOptions[id] || !this.fileOptions[id][name]) { | ||
handler.sendNull(responseId); | ||
return; | ||
return void handler.sendNull(responseId); | ||
} | ||
let obj = this.clone(this.fileOptions[id][name]); | ||
let obj = this._clone(this.fileOptions[id][name]); | ||
if (typeof obj !== 'object') { | ||
@@ -476,14 +451,18 @@ obj = { | ||
// Handle request for File data | ||
this.readFile(id, name, (err, data, _mimeType) => { | ||
if (err || data === undefined || data === null) { | ||
handler && handler.sendNull(responseId); | ||
} else { | ||
if (!Buffer.isBuffer(data) && typeof data === 'object') { | ||
// if its an invalid object, stringify it and log warning | ||
data = JSON.stringify(data); | ||
this.log.warn(`${namespaceLog} Data of "${id}/${name}" has invalid structure at file data request: ${data}`); | ||
} | ||
handler && handler.sendBufBulk(responseId, Buffer.from(data)); | ||
} | ||
}); | ||
let data; | ||
try { | ||
data = this._readFile(id, name); | ||
} catch (err) { | ||
return void handler.sendNull(responseId); | ||
} | ||
if (data.fileContent === undefined || data.fileContent === null) { | ||
return void handler.sendNull(responseId); | ||
} | ||
let fileData = data.fileContent; | ||
if (!Buffer.isBuffer(fileData) && tools.isObject(fileData)) { | ||
// if its an invalid object, stringify it and log warning | ||
fileData = JSON.stringify(fileData); | ||
this.log.warn(`${namespaceLog} Data of "${id}/${name}" has invalid structure at file data request: ${fileData}`); | ||
} | ||
handler.sendBufBulk(responseId, Buffer.from(fileData)); | ||
} | ||
@@ -502,12 +481,7 @@ } else { | ||
const obj = JSON.parse(data[1].toString('utf-8')); | ||
this._setObjectDirect(id, obj, (err, id) => { | ||
if (err || !id) { | ||
handler && handler.sendError(responseId, new Error(`ERROR setObject id=${id}: ${err}`)); | ||
} else { | ||
handler && handler.sendString(responseId, 'OK'); | ||
} | ||
}); | ||
this._setObjectDirect(id, obj); | ||
} catch (err) { | ||
handler.sendError(responseId, new Error(`ERROR setObject id=${id}: ${err}`)); | ||
return void handler.sendError(responseId, new Error(`ERROR setObject id=${id}: ${err}`)); | ||
} | ||
handler.sendString(responseId, 'OK'); | ||
} else if (namespace === this.namespaceFile) { | ||
@@ -517,3 +491,3 @@ // Handle request to set meta data, we ignore it because | ||
if (isMeta) { | ||
this.loadFileSettings(id); | ||
this._loadFileSettings(id); | ||
@@ -529,4 +503,3 @@ try { | ||
} catch (err) { | ||
handler.sendError(responseId, new Error(`ERROR writeFile-Meta id=${id}: ${err.message}`)); | ||
return; | ||
return void handler.sendError(responseId, new Error(`ERROR writeFile-Meta id=${id}: ${err.message}`)); | ||
} | ||
@@ -536,9 +509,8 @@ handler.sendString(responseId, 'OK'); | ||
// Handle request to write the file | ||
this.writeFile(id, name, data[1], err => { | ||
if (err) { | ||
handler && handler.sendError(responseId, new Error(`ERROR writeFile id=${id}: ${err.message}`)); | ||
} else { | ||
handler && handler.sendString(responseId, 'OK'); | ||
} | ||
}); | ||
try { | ||
this._writeFile(id, name, data[1]); | ||
} catch (err) { | ||
return void handler.sendError(responseId, new Error(`ERROR writeFile id=${id}: ${err.message}`)); | ||
} | ||
handler.sendString(responseId, 'OK'); | ||
} | ||
@@ -557,3 +529,3 @@ } else { | ||
if (oldDetails.id !== newDetails.id) { | ||
return handler.sendError(responseId, new Error('ERROR renameObject: id needs to stay the same')); | ||
return void handler.sendError(responseId, new Error('ERROR renameObject: id needs to stay the same')); | ||
} | ||
@@ -566,9 +538,8 @@ | ||
// Handle request for File data | ||
this.rename(oldDetails.id, oldDetails.name, newDetails.name, err => { | ||
if (err) { | ||
handler && handler.sendNull(responseId); | ||
} else { | ||
handler && handler.sendString(responseId, 'OK'); | ||
} | ||
}); | ||
try { | ||
this._rename(oldDetails.id, oldDetails.name, newDetails.name); | ||
} catch (err) { | ||
return void handler.sendNull(responseId); | ||
} | ||
handler.sendString(responseId, 'OK'); | ||
} | ||
@@ -585,9 +556,8 @@ } else { | ||
if (namespace === this.namespaceObj) { | ||
this.delObject(id, err => { | ||
if (err) { | ||
handler && handler.sendError(responseId, new Error('ERROR delObject ' + id + ': ' + err)); | ||
} else { | ||
handler && handler.sendInteger(responseId, 1); | ||
} | ||
}); | ||
try { | ||
this._delObject(id); | ||
} catch (err) { | ||
return void handler.sendError(responseId, err); | ||
} | ||
handler.sendInteger(responseId, 1); | ||
} else if (namespace === this.namespaceFile) { | ||
@@ -600,9 +570,8 @@ // Handle request to delete meta data, we ignore it because | ||
// Handle request to remove the file | ||
this.unlink(id, name, err => { | ||
if (err) { | ||
handler && handler.sendError(responseId, new Error(`ERROR unlink id=${id}: ${err}`)); | ||
} else { | ||
handler && handler.sendString(responseId, 'OK'); | ||
} | ||
}); | ||
try { | ||
this._unlink(id, name); | ||
} catch (err) { | ||
return void handler.sendError(responseId, err); | ||
} | ||
handler.sendString(responseId, 'OK'); | ||
} | ||
@@ -616,3 +585,3 @@ } else { | ||
if (!data || !data.length) { | ||
handler.sendInteger(responseId, 0); | ||
return void handler.sendInteger(responseId, 0); | ||
} | ||
@@ -624,11 +593,17 @@ | ||
if (namespace === this.namespaceObj) { | ||
let exists; | ||
try { | ||
const exists = await this.objectExists(id); | ||
handler.sendInteger(responseId, +exists); | ||
exists = this._objectExists(id); | ||
} catch (e) { | ||
return handler.sendError(responseId, e); | ||
return void handler.sendError(responseId, e); | ||
} | ||
handler.sendInteger(responseId, exists ? 1 : 0); | ||
} else if (namespace === this.namespaceFile) { | ||
// @ts-ignore | ||
handler.sendInteger(responseId, +await this.fileExists(id, name)); | ||
let exists; | ||
try { | ||
exists = this._fileExists(id, name); | ||
} catch (e) { | ||
return void handler.sendError(responseId, e); | ||
} | ||
handler.sendInteger(responseId, exists ? 1 : 0); | ||
} else { | ||
@@ -639,6 +614,6 @@ handler.sendError(responseId, new Error(`EXISTS-UNSUPPORTED for namespace ${namespace}`)); | ||
// handle Redis "SCAN" request for state namespace | ||
// handle Redis "SCAN" request for objects namespace | ||
handler.on('scan', (data, responseId) => { | ||
if (!data || data.length < 3) { | ||
return handler.sendArray(responseId, ['0', []]); | ||
return void handler.sendArray(responseId, ['0', []]); | ||
} | ||
@@ -652,3 +627,3 @@ | ||
if (!data || !data.length) { | ||
return handler.sendArray(responseId, []); | ||
return void handler.sendArray(responseId, []); | ||
} | ||
@@ -664,4 +639,4 @@ | ||
if (namespace === this.namespaceObj) { | ||
this.subscribeConfigForClient(handler, id, () => | ||
handler && handler.sendArray(responseId, ['psubscribe', data[0], 1])); | ||
this._subscribeConfigForClient(handler, id); | ||
handler.sendArray(responseId, ['psubscribe', data[0], 1]); | ||
} else { | ||
@@ -677,4 +652,4 @@ handler.sendError(responseId, new Error('PSUBSCRIBE-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))); | ||
if (namespace === this.namespaceObj) { | ||
this.unsubscribeConfigForClient(handler, id, () => | ||
handler && handler.sendArray(responseId, ['punsubscribe', data[0], 1])); | ||
this._unsubscribeConfigForClient(handler, id); | ||
handler.sendArray(responseId, ['punsubscribe', data[0], 1]); | ||
} else { | ||
@@ -732,6 +707,6 @@ handler.sendError(responseId, new Error('PUNSUBSCRIBE-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))); | ||
*/ | ||
destroy() { | ||
super.destroy(); | ||
async destroy() { | ||
await super.destroy(); | ||
if (this.server.server) { | ||
if (this.server) { | ||
Object.keys(this.serverConnections).forEach(s => { | ||
@@ -742,7 +717,13 @@ this.serverConnections[s].close(); | ||
try { | ||
this.server.server.close(); | ||
} catch (e) { | ||
console.log(e.message); | ||
} | ||
return /** @type {Promise<void>} */ (new Promise(resolve => { | ||
if (!this.server) { | ||
return void resolve(); | ||
} | ||
try { | ||
this.server.close(() => resolve()); | ||
} catch (e) { | ||
console.log(e.message); | ||
resolve(); | ||
} | ||
})); | ||
} | ||
@@ -763,24 +744,17 @@ } | ||
if (namespace === this.namespaceObj) { | ||
this.getKeys(id, (err, result) => { | ||
if (err || !result) { | ||
return handler && handler.sendError(responseId, new Error(`ERROR getKeys: ${err}`)); | ||
} | ||
for (let i = 0; i < result.length; i++) { | ||
result[i] = this.namespaceObj + result[i]; | ||
} | ||
// if scan, we send the cursor as first argument | ||
handler && handler.sendArray(responseId, isScan ? ['0', result] : result); | ||
}); | ||
} else if (namespace === this.namespaceFile) { | ||
let response = []; | ||
if (namespace === this.namespaceObj || namespace === this.namespaceObjects) { | ||
response = this._getKeys(id).map(val => this.namespaceObj + val); | ||
// if scan, we send the cursor as first argument | ||
if (namespace !== this.namespaceObjects) { // When it was not the full DB namespace send out response | ||
return void handler.sendArray(responseId, isScan ? ['0', response] : response); | ||
} | ||
} | ||
if (namespace === this.namespaceFile || namespace === this.namespaceObjects) { | ||
// Handle request to get meta data keys | ||
if (isMeta === undefined) { | ||
this.readDir(id, name, (err, res) => { | ||
if (err && err.toString().endsWith(utils.ERRORS.ERROR_NOT_FOUND)) { | ||
res = []; | ||
err = null; | ||
} else if (err) { | ||
handler && handler.sendError(responseId, new Error(`ERROR readDir id=${id}: ${err.message}`)); | ||
return; | ||
} else if (!res || !res.length) { | ||
let res; | ||
try { | ||
res = this._readDir(id, name); | ||
if (!res || !res.length) { | ||
res = [{ | ||
@@ -794,20 +768,29 @@ file: '_data.json', | ||
} | ||
const response = []; | ||
const baseName = name + ((!name.length || name.endsWith('/')) ? '' : '/'); | ||
res.forEach(arr => { | ||
let entryId = id; | ||
if (arr.isDir) { | ||
if (entryId === '' || entryId === '*') { | ||
entryId = arr.file; | ||
arr.file = '_data.json'; // We return a "virtual file" to mark the directory as existing | ||
} else { | ||
arr.file += '/_data.json'; // We return a "virtual file" to mark the directory as existing | ||
} | ||
} catch (err) { | ||
if (!err.message.endsWith(utils.ERRORS.ERROR_NOT_FOUND)) { | ||
return void handler.sendError(responseId, new Error(`ERROR readDir id=${id}: ${err.message}`)); | ||
} | ||
res = []; | ||
} | ||
let baseName = name; | ||
if (!baseName.endsWith('/')) { | ||
baseName += '/'; | ||
} | ||
res.forEach(arr => { | ||
let entryId = id; | ||
if (arr.isDir) { | ||
if (entryId === '' || entryId === '*') { | ||
entryId = arr.file; | ||
arr.file = '_data.json'; // We return a "virtual file" to mark the directory as existing | ||
} else { | ||
arr.file += '/_data.json'; // We return a "virtual file" to mark the directory as existing | ||
} | ||
// We need to simulate the Meta data here, so return both | ||
response.push(this.getFileId(entryId, baseName + arr.file, true)); | ||
response.push(this.getFileId(entryId, baseName + arr.file, false)); | ||
}); | ||
handler && handler.sendArray(responseId, isScan ? ['0', response] : response); | ||
} | ||
// We need to simulate the Meta data here, so return both | ||
response.push(this.getFileId(entryId, baseName + arr.file, true)); | ||
response.push(this.getFileId(entryId, baseName + arr.file, false)); | ||
}); | ||
handler.sendArray(responseId, isScan ? ['0', response] : response); // send out file or full db response | ||
} else { // such a request should never happen | ||
handler.sendArray(responseId, isScan ? ['0', []] : []); // send out file or full db response | ||
} | ||
@@ -848,22 +831,25 @@ } else { | ||
* @param settings Settings object | ||
* @param server Network server to use | ||
* @private | ||
* @return {Promise<void>} | ||
*/ | ||
_initRedisServer(settings, server) { | ||
try { | ||
_initRedisServer(settings) { | ||
return new Promise((resolve, reject) => { | ||
if (settings.secure) { | ||
throw Error('Secure Redis unsupported'); | ||
} else { | ||
this.server.server = net.createServer(); | ||
reject(new Error('Secure Redis unsupported for File-DB')); | ||
} | ||
this.server.server.on('error', err => this.log.info(this.namespace + ' ' + (settings.secure ? 'Secure ' : '') + ' Error inMem-objects listening on port ' + (settings.port || 9001)) + ': ' + err); | ||
server.server.listen(settings.port || 9001, (settings.host && settings.host !== 'localhost') ? settings.host : ((settings.host === 'localhost') ? '127.0.0.1' : undefined)); | ||
} catch (e) { | ||
this.log.error(this.namespace + ' Cannot start inMem-objects on port ' + (settings.port || 9001) + ': ' + e.message); | ||
process.exit(24); | ||
} | ||
try { | ||
this.server = net.createServer(); | ||
this.server.on('error', err => | ||
this.log.info(`${this.namespace} ${settings.secure ? 'Secure ' : ''} Error inMem-objects listening on port ${settings.port || 9001}: ${err}`)); | ||
this.server.on('connection', socket => this._initSocket(socket)); | ||
this.server.server.on('connection', socket => this._initSocket(socket)); | ||
this.log.debug(this.namespace + ' ' + (settings.secure ? 'Secure ' : '') + ' Redis inMem-objects listening on port ' + (settings.port || 9001)); | ||
this.server.listen( | ||
settings.port || 9001, | ||
settings.host === 'localhost' ? '127.0.0.1' : settings.host ? settings.host : undefined, | ||
() => resolve() | ||
); | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}); | ||
} | ||
@@ -870,0 +856,0 @@ } |
{ | ||
"name": "@iobroker/db-objects-file", | ||
"version": "0.0.14", | ||
"version": "1.0.0", | ||
"engines": { | ||
@@ -8,4 +8,4 @@ "node": ">=10.0.0" | ||
"dependencies": { | ||
"@iobroker/db-base": "^0.0.14", | ||
"@iobroker/db-objects-redis": "^0.0.14", | ||
"@iobroker/db-base": "^1.0.0", | ||
"@iobroker/db-objects-redis": "^1.0.0", | ||
"deep-clone": "^3.0.3", | ||
@@ -36,3 +36,3 @@ "fs-extra": "^9.0.1", | ||
}, | ||
"gitHead": "6f1253ede80b73ec1ffbc24d1afd19f2552bbdc4" | ||
"gitHead": "eaedf07726250c1fb3bf3cbfa9778e6cddd5c963" | ||
} |
Sorry, the diff of this file is too big to display
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
7
1
81939
1570
+ Added@iobroker/db-base@1.2.4(transitive)
+ Added@iobroker/db-objects-redis@1.2.7(transitive)
- Removed@iobroker/db-base@0.0.14(transitive)
- Removed@iobroker/db-objects-redis@0.0.14(transitive)
Updated@iobroker/db-base@^1.0.0