├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── code-of-conduct.md ├── package.json ├── rollup.config.ts ├── src ├── @notabug │ └── gun-lmdb.ts ├── client.ts ├── gun.ts └── receiver.ts ├── test └── gun-lmdb.test.ts ├── tools ├── gh-pages-publish.ts └── semantic-release-prepare.ts ├── tsconfig.json ├── tslint.json ├── types └── node-lmdb.d.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | #root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 100 10 | indent_size = 2 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | .DS_Store 5 | *.log 6 | .vscode 7 | .idea 8 | dist 9 | compiled 10 | .awcache 11 | .rpt2_cache 12 | docs 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - ~/.npm 5 | notifications: 6 | email: false 7 | node_js: 8 | - '10' 9 | - '11' 10 | - '8' 11 | - '6' 12 | script: 13 | - npm run test:prod && npm run build 14 | after_success: 15 | - npm run travis-deploy-once "npm run report-coverage" 16 | - if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run travis-deploy-once "npm run deploy-docs"; fi 17 | - if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run travis-deploy-once "npm run semantic-release"; fi 18 | branches: 19 | except: 20 | - /^v\d+\.\d+\.\d+$/ 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 go1dfish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gun-lmdb 2 | 3 | LMDB adapter for gunDB based on node-lmdb 4 | 5 | ## Installation 6 | 7 | npm install @notabug/gun-lmdb 8 | 9 | ## Usage 10 | 11 | require("@notabug/gun-lmdb").attachToGun(Gun, { 12 | path: "path/to/an/existing/folder, 13 | mapSize: 1024**4 // Maximum size of database in bytes 14 | }); 15 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Free Speech and Consideration 4 | 5 | Everyone has the right to communicate opinions and ideas without fear of censorship. Consideration shall be given to all constructive speech that is based in fact and reason. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@notabug/gun-lmdb", 3 | "version": "0.2.3", 4 | "description": "LMDB adapter for gun and gun-receiver", 5 | "main": "dist/@notabug/gun-lmdb.umd.js", 6 | "module": "dist/@notabug/gun-lmdb.es5.js", 7 | "typings": "dist/types/gun-lmdb.d.ts", 8 | "files": ["dist"], 9 | "author": "go1dfish ", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/notabugio/gun-lmdb.git" 13 | }, 14 | "keywords": ["lmdb", "gundb"], 15 | "license": "MIT", 16 | "engines": { 17 | "node": ">=10.0.0 && <11.0.0" 18 | }, 19 | "scripts": { 20 | "lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'", 21 | "prebuild": "rimraf dist", 22 | "build": "tsc --module commonjs && rollup -c rollup.config.ts && typedoc --out docs --target es6 --theme minimal --mode file src", 23 | "start": "rollup -c rollup.config.ts -w", 24 | "test": "jest --coverage", 25 | "test:watch": "jest --coverage --watch", 26 | "test:prod": "npm run lint && npm run test -- --no-cache", 27 | "deploy-docs": "ts-node tools/gh-pages-publish", 28 | "report-coverage": "cat ./coverage/lcov.info | coveralls", 29 | "commit": "git-cz", 30 | "semantic-release": "semantic-release", 31 | "semantic-release-prepare": "ts-node tools/semantic-release-prepare", 32 | "precommit": "lint-staged", 33 | "travis-deploy-once": "travis-deploy-once" 34 | }, 35 | "lint-staged": { 36 | "{src,test}/**/*.ts": ["prettier --write", "git add"] 37 | }, 38 | "config": { 39 | "commitizen": { 40 | "path": "node_modules/cz-conventional-changelog" 41 | } 42 | }, 43 | "jest": { 44 | "transform": { 45 | ".(ts|tsx)": "ts-jest" 46 | }, 47 | "testEnvironment": "node", 48 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 49 | "moduleFileExtensions": ["ts", "tsx", "js"], 50 | "coveragePathIgnorePatterns": ["/node_modules/", "/test/"], 51 | "coverageThreshold": { 52 | "global": { 53 | "branches": 90, 54 | "functions": 95, 55 | "lines": 95, 56 | "statements": 95 57 | } 58 | }, 59 | "collectCoverageFrom": ["src/*.{js,ts}"] 60 | }, 61 | "prettier": { 62 | "semi": false, 63 | "singleQuote": true 64 | }, 65 | "commitlint": { 66 | "extends": ["@commitlint/config-conventional"] 67 | }, 68 | "devDependencies": { 69 | "@commitlint/cli": "^7.1.2", 70 | "@commitlint/config-conventional": "^7.1.2", 71 | "@types/jest": "^23.3.2", 72 | "@types/node": "^10.11.0", 73 | "colors": "^1.3.2", 74 | "commitizen": "^3.0.0", 75 | "coveralls": "^3.0.2", 76 | "cross-env": "^5.2.0", 77 | "cz-conventional-changelog": "^2.1.0", 78 | "husky": "^1.0.1", 79 | "jest": "^23.6.0", 80 | "jest-config": "^23.6.0", 81 | "lint-staged": "^8.0.0", 82 | "lodash.camelcase": "^4.3.0", 83 | "prettier": "^1.14.3", 84 | "prompt": "^1.0.0", 85 | "replace-in-file": "^3.4.2", 86 | "rimraf": "^2.6.2", 87 | "rollup": "^0.67.0", 88 | "rollup-plugin-commonjs": "^9.1.8", 89 | "rollup-plugin-json": "^3.1.0", 90 | "rollup-plugin-node-resolve": "^3.4.0", 91 | "rollup-plugin-sourcemaps": "^0.4.2", 92 | "rollup-plugin-typescript2": "^0.18.0", 93 | "semantic-release": "^15.9.16", 94 | "shelljs": "^0.8.3", 95 | "travis-deploy-once": "^5.0.9", 96 | "ts-jest": "^23.10.2", 97 | "ts-node": "^7.0.1", 98 | "tslint": "^5.11.0", 99 | "tslint-config-prettier": "^1.15.0", 100 | "tslint-config-standard": "^8.0.1", 101 | "typedoc": "^0.12.0", 102 | "typescript": "^3.0.3" 103 | }, 104 | "dependencies": { 105 | "node-lmdb": "^0.6.2" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | import sourceMaps from 'rollup-plugin-sourcemaps' 4 | import camelCase from 'lodash.camelcase' 5 | import typescript from 'rollup-plugin-typescript2' 6 | import json from 'rollup-plugin-json' 7 | 8 | const pkg = require('./package.json') 9 | 10 | const libraryName = '@notabug/gun-lmdb' 11 | 12 | export default { 13 | input: `src/${libraryName}.ts`, 14 | output: [ 15 | { file: pkg.main, name: camelCase(libraryName), format: 'umd', sourcemap: true }, 16 | { file: pkg.module, format: 'es', sourcemap: true } 17 | ], 18 | // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash') 19 | external: ['node-lmdb'], 20 | watch: { 21 | include: 'src/**' 22 | }, 23 | plugins: [ 24 | // Allow json resolution 25 | json(), 26 | // Compile TypeScript files 27 | typescript({ useTsconfigDeclarationDir: true }), 28 | // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs) 29 | commonjs(), 30 | // Allow node_modules resolution, so you can use 'external' to control 31 | // which external modules to include in the bundle 32 | // https://github.com/rollup/rollup-plugin-node-resolve#usage 33 | resolve(), 34 | 35 | // Resolve source maps to the original source 36 | sourceMaps() 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/@notabug/gun-lmdb.ts: -------------------------------------------------------------------------------- 1 | import * as receiverFns from '../receiver' 2 | export { attachToGun } from '../gun' 3 | export { createClient } from "../client"; 4 | 5 | export const receiver = receiverFns 6 | -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- 1 | import lmdb from 'node-lmdb' 2 | 3 | export interface GunNode { 4 | _: { 5 | '#': string 6 | '>': { 7 | [key: string]: number 8 | } 9 | [key: string]: any 10 | } 11 | [key: string]: any 12 | } 13 | 14 | export interface GunPut { 15 | [soul: string]: GunNode 16 | } 17 | 18 | const DEFAULT_CONFIG = { 19 | path: 'lmdb' 20 | } 21 | 22 | export class GunLmdbClient { 23 | Gun: any 24 | env: any 25 | dbi: any 26 | 27 | constructor(Gun: any, lmdbConfig = DEFAULT_CONFIG) { 28 | this.Gun = Gun 29 | this.env = new lmdb.Env() 30 | this.env.open(lmdbConfig) 31 | this.dbi = this.env.openDbi({ 32 | name: 'gun-nodes', 33 | create: true 34 | }) 35 | } 36 | 37 | async get(soul: string) { 38 | if (!soul) return null 39 | const txn = this.env.beginTxn() 40 | try { 41 | const data = this.deserialize(txn.getStringUnsafe(this.dbi, soul)) 42 | txn.commit() 43 | return data 44 | } catch (e) { 45 | txn.abort() 46 | throw e 47 | } 48 | } 49 | 50 | async getRaw(soul: string) { 51 | if (!soul) return null 52 | const txn = this.env.beginTxn() 53 | try { 54 | const data = txn.getString(this.dbi, soul) 55 | txn.commit() 56 | return data || '' 57 | } catch (e) { 58 | txn.abort() 59 | throw e 60 | } 61 | } 62 | 63 | async read(soul: string) { 64 | const data = await this.get(soul) 65 | if (!data) return 66 | 67 | if (!this.Gun.SEA || soul.indexOf('~') === -1) return data 68 | 69 | for (let key in data) { 70 | if (key === '_') continue 71 | this.Gun.SEA.verify( 72 | this.Gun.SEA.opt.pack(data[key], key, data, soul), 73 | false, 74 | (res: GunNode) => (data[key] = this.Gun.SEA.opt.unpack(res, key, data)) 75 | ) 76 | } 77 | 78 | return data 79 | } 80 | 81 | serialize(node: GunNode) { 82 | return JSON.stringify(node) 83 | } 84 | 85 | deserialize(data: string) { 86 | return JSON.parse(data) 87 | } 88 | 89 | async writeNode(soul: string, nodeData: GunNode) { 90 | if (!soul) return 91 | const txn = this.env.beginTxn() 92 | const nodeDataMeta = (nodeData && nodeData['_']) || {} 93 | const nodeDataState = nodeDataMeta['>'] || {} 94 | 95 | try { 96 | const existingData = txn.getStringUnsafe(this.dbi, soul) 97 | const node = this.deserialize(existingData) || {} 98 | const meta = (node['_'] = node['_'] || { '#': soul, '>': {} }) 99 | const state = (meta['>'] = meta['>'] || {}) 100 | 101 | for (let key in nodeData) { 102 | if (key === '_' || !(key in nodeDataState)) continue 103 | node[key] = nodeData[key] 104 | state[key] = nodeDataState[key] 105 | } 106 | 107 | txn.putString(this.dbi, soul, this.serialize(node)) 108 | txn.commit() 109 | } catch (e) { 110 | txn.abort() 111 | throw e 112 | } 113 | } 114 | 115 | async write(put: GunPut) { 116 | if (!put) return 117 | for (let soul in put) await this.writeNode(soul, put[soul]) 118 | } 119 | 120 | close() { 121 | this.dbi.close() 122 | this.env.close() 123 | } 124 | } 125 | 126 | export function createClient(Gun: any, options: any) { 127 | return new GunLmdbClient(Gun, options) 128 | } 129 | -------------------------------------------------------------------------------- /src/gun.ts: -------------------------------------------------------------------------------- 1 | import { GunLmdbClient, GunPut } from './client' 2 | 3 | interface GunGet { 4 | '#': string 5 | get: { 6 | '#': string 7 | } 8 | } 9 | 10 | export const attachToGun = (Gun: any, options?: any) => 11 | Gun.on('create', function(this: any, db: any) { 12 | const lmdb = (Gun.lmdb = db.lmdb = new GunLmdbClient(Gun, options)) 13 | 14 | db.on('get', async function(this: any, request: GunGet) { 15 | this.to.next(request) 16 | if (!request) return 17 | const dedupId = request['#'] 18 | const get = request.get 19 | const soul = get['#'] 20 | 21 | try { 22 | const result = await lmdb.get(soul) 23 | db.on('in', { 24 | '@': dedupId, 25 | put: result ? { [soul]: result } : null, 26 | err: null 27 | }) 28 | } catch (err) { 29 | console.error('error', err.stack || err) 30 | db.on('in', { 31 | '@': dedupId, 32 | put: null, 33 | err 34 | }) 35 | } 36 | }) 37 | 38 | db.on('put', async function(this: any, request: GunPut) { 39 | if (!request) return this.to.next(request) 40 | const dedupId = request['#'] 41 | 42 | try { 43 | await lmdb.write(request.put) 44 | db.on('in', { 45 | '@': dedupId, 46 | ok: true, 47 | err: null 48 | }) 49 | } catch (err) { 50 | db.on('in', { 51 | '@': dedupId, 52 | ok: false, 53 | err: err 54 | }) 55 | } 56 | 57 | this.to.next(request) 58 | }) 59 | 60 | this.to.next(db) 61 | }) 62 | -------------------------------------------------------------------------------- /src/receiver.ts: -------------------------------------------------------------------------------- 1 | import { GunLmdbClient, GunNode } from './client' 2 | 3 | export const respondToGets = ( 4 | Gun: any, 5 | { disableRelay = true, skipValidation = true } = {}, 6 | lmdbOpts = undefined 7 | ) => (db: any) => { 8 | const lmdb = (Gun.lmdb = db.lmdb = new GunLmdbClient(Gun, lmdbOpts)) 9 | 10 | db.onIn(async function gunLmdbRespondToGets(msg: any) { 11 | const { from, json, fromCluster } = msg 12 | const get = json && json.get 13 | const soul = get && get['#'] 14 | const dedupId = (json && json['#']) || '' 15 | 16 | if (!soul || fromCluster) return msg 17 | 18 | try { 19 | // const result = await lmdb.get(soul) 20 | const rawResult = await lmdb.getRaw(soul) 21 | let put = 'null' 22 | if (rawResult) { 23 | put = ['{', `${JSON.stringify(soul)}: ${rawResult}`, '}'].join('') 24 | } 25 | const raw: string = [ 26 | '{', 27 | `"#": ${JSON.stringify(from.msgId())},`, 28 | `"@": ${JSON.stringify(dedupId)},`, 29 | `"put": ${put}`, 30 | '}' 31 | ].join('') 32 | /* 33 | const json = { 34 | '#': from.msgId(), 35 | '@': dedupId, 36 | put: result ? { [soul]: result } : null 37 | } 38 | */ 39 | 40 | from.send({ 41 | raw, 42 | // json, 43 | ignoreLeeching: true, 44 | skipValidation: !rawResult || skipValidation 45 | }) 46 | 47 | return disableRelay && rawResult ? { ...msg, noRelay: true } : msg 48 | } catch (err) { 49 | console.error('get err', err.stack || err) 50 | const json = { 51 | '#': from.msgId(), 52 | '@': dedupId, 53 | err: `${err}` 54 | } 55 | 56 | from.send({ json, ignoreLeeching: true, skipValidation }) 57 | return msg 58 | } 59 | }) 60 | 61 | return db 62 | } 63 | 64 | export const acceptWrites = (Gun: any, { disableRelay = false } = {}, lmdbOpts = undefined) => ( 65 | db: any 66 | ) => { 67 | const lmdb = (Gun.lmdb = db.lmdb = new GunLmdbClient(Gun, lmdbOpts)) 68 | 69 | db.onIn(async function gunLmdbAcceptWrites(msg: any) { 70 | if (msg.fromCluster || !msg.json.put) return msg 71 | const diff: GunNode = await db.getDiff(msg.json.put) 72 | const souls = diff && Object.keys(diff) 73 | 74 | if (!souls || !souls.length) { 75 | return disableRelay ? { ...msg, noRelay: true } : msg 76 | } 77 | 78 | try { 79 | await lmdb.write(diff) 80 | const json = { '@': msg.json['#'], ok: true, err: null } 81 | 82 | msg.from && 83 | msg.from.send && 84 | msg.from.send({ 85 | json, 86 | noRelay: true, 87 | ignoreLeeching: true, 88 | skipValidation: true 89 | }) 90 | return msg 91 | } catch (err) { 92 | console.error('error writing data', err) 93 | const json = { '@': msg.json['#'], ok: false, err: `${err}` } 94 | 95 | msg.from && 96 | msg.from.send && 97 | msg.from.send({ 98 | json, 99 | noRelay: disableRelay, 100 | ignoreLeeching: true, 101 | skipValidation: true 102 | }) 103 | 104 | return msg 105 | } 106 | }) 107 | 108 | return db 109 | } 110 | -------------------------------------------------------------------------------- /test/gun-lmdb.test.ts: -------------------------------------------------------------------------------- 1 | import DummyClass from "../src/gun-lmdb" 2 | 3 | /** 4 | * Dummy test 5 | */ 6 | describe("Dummy test", () => { 7 | it("works if true is truthy", () => { 8 | expect(true).toBeTruthy() 9 | }) 10 | 11 | it("DummyClass is instantiable", () => { 12 | expect(new DummyClass()).toBeInstanceOf(DummyClass) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /tools/gh-pages-publish.ts: -------------------------------------------------------------------------------- 1 | const { cd, exec, echo, touch } = require("shelljs") 2 | const { readFileSync } = require("fs") 3 | const url = require("url") 4 | 5 | let repoUrl 6 | let pkg = JSON.parse(readFileSync("package.json") as any) 7 | if (typeof pkg.repository === "object") { 8 | if (!pkg.repository.hasOwnProperty("url")) { 9 | throw new Error("URL does not exist in repository section") 10 | } 11 | repoUrl = pkg.repository.url 12 | } else { 13 | repoUrl = pkg.repository 14 | } 15 | 16 | let parsedUrl = url.parse(repoUrl) 17 | let repository = (parsedUrl.host || "") + (parsedUrl.path || "") 18 | let ghToken = process.env.GH_TOKEN 19 | 20 | echo("Deploying docs!!!") 21 | cd("docs") 22 | touch(".nojekyll") 23 | exec("git init") 24 | exec("git add .") 25 | exec('git config user.name "go1dfish"') 26 | exec('git config user.email "me@go1dfish.me"') 27 | exec('git commit -m "docs(docs): update gh-pages"') 28 | exec( 29 | `git push --force --quiet "https://${ghToken}@${repository}" master:gh-pages` 30 | ) 31 | echo("Docs deployed!!") 32 | -------------------------------------------------------------------------------- /tools/semantic-release-prepare.ts: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const { fork } = require("child_process") 3 | const colors = require("colors") 4 | 5 | const { readFileSync, writeFileSync } = require("fs") 6 | const pkg = JSON.parse( 7 | readFileSync(path.resolve(__dirname, "..", "package.json")) 8 | ) 9 | 10 | pkg.scripts.prepush = "npm run test:prod && npm run build" 11 | pkg.scripts.commitmsg = "commitlint -E HUSKY_GIT_PARAMS" 12 | 13 | writeFileSync( 14 | path.resolve(__dirname, "..", "package.json"), 15 | JSON.stringify(pkg, null, 2) 16 | ) 17 | 18 | // Call husky to set up the hooks 19 | fork(path.resolve(__dirname, "..", "node_modules", "husky", "lib", "installer", 'bin'), ['install']) 20 | 21 | console.log() 22 | console.log(colors.green("Done!!")) 23 | console.log() 24 | 25 | if (pkg.repository.url.trim()) { 26 | console.log(colors.cyan("Now run:")) 27 | console.log(colors.cyan(" npm install -g semantic-release-cli")) 28 | console.log(colors.cyan(" semantic-release-cli setup")) 29 | console.log() 30 | console.log( 31 | colors.cyan('Important! Answer NO to "Generate travis.yml" question') 32 | ) 33 | console.log() 34 | console.log( 35 | colors.gray( 36 | 'Note: Make sure "repository.url" in your package.json is correct before' 37 | ) 38 | ) 39 | } else { 40 | console.log( 41 | colors.red( 42 | 'First you need to set the "repository.url" property in package.json' 43 | ) 44 | ) 45 | console.log(colors.cyan("Then run:")) 46 | console.log(colors.cyan(" npm install -g semantic-release-cli")) 47 | console.log(colors.cyan(" semantic-release-cli setup")) 48 | console.log() 49 | console.log( 50 | colors.cyan('Important! Answer NO to "Generate travis.yml" question') 51 | ) 52 | } 53 | 54 | console.log() 55 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { "*": ["types/*"] }, 5 | "moduleResolution": "node", 6 | "target": "es5", 7 | "module": "es2015", 8 | "lib": ["es2015", "es2016", "es2017", "dom"], 9 | "strict": true, 10 | "sourceMap": true, 11 | "declaration": true, 12 | "allowSyntheticDefaultImports": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true, 15 | "declarationDir": "dist/types", 16 | "outDir": "dist/lib", 17 | "typeRoots": ["node_modules/@types"] 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-standard", 4 | "tslint-config-prettier" 5 | ] 6 | } -------------------------------------------------------------------------------- /types/node-lmdb.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'node-lmdb' 2 | --------------------------------------------------------------------------------