@nan0web/db
v1.2.1
Published
Every data becomes a database. Agnostic document database and data manipulation utilities.
Maintainers
Readme
@nan0web/db
|Package name|Status|Documentation|Test coverage|Features|Npm version|
|---|---|---|---|---|---|
|@nan0web/db |🟢 98.8% |🧪 English 🏴Українською 🇺🇦 |🟢 93.7% |✅ d.ts 📜 system.md 🕹️ playground |1.2.0 |
Agnostic document database and data manipulation utilities. Designed to be flexible, minimal and powerful — the tool that supports any data format and nested hierarchy with reference resolution, inheritance and global variables.
Inspired by zero-is-not-a-number rule of nan0web:
Every data becomes a database.
Based on real use-cases, supports:
- object flattening/unflattening
- deep merging with reference handling
- async directory listing (for fs & fetch layers)
- stream-based progress during traversal
See how it works in playground.
Installation
How to install with npm?
npm install @nan0web/dbHow to install with pnpm?
pnpm add @nan0web/dbHow to install with yarn?
yarn add @nan0web/dbQuick Start
How to load JSON document?
import DB from "@nan0web/db"
const db = new DB()
const doc = await db.loadDocumentAs(".json", "doc", { key: "value" })
console.info(doc) // ← { key: "value" }Example: Using get() with default fallback
How to get or return default?
import DB from "@nan0web/db"
const db = new DB()
const result = await db.get("missing-file.json", { defaultValue: {} })
console.info(result) // ← {}Example: Loading known document
How to get specific document?
import DB from "@nan0web/db"
const db = new DB({ data: new Map([["file.txt", "text"]]) })
const result = await db.get("file.txt")
console.info(result) // ← "text"Usage with Real Context
Resolving references and global vars
How to use document reference system?
import DB from "@nan0web/db"
const db = new DB({
data: new Map([
["_/index.json", { global: "value" }],
["data.json", { "$ref": "_/index.json", key: "val" }]
])
})
await db.connect()
const res = await db.fetch("data.json")
console.info(res) // ← { global: "value", key: "val" }Playground
CLI sandbox for safe experiments:
git clone https://github.com/nan0web/db.git
cd db
npm install
npm run playAPI Reference
The heart of the package includes core tools to manage hierarchical data structures.
db.get(uri, GetOpts)
Loads/returns document content from its URI.
Parameters
uri(string) – Document URI.GetOpts.defaultValue(any) – fallback if doc not found
Returns
- (any) – Document content or default value.
How to get document value?
import DB from "@nan0web/db"
const db = new DB({ data: new Map([["x.file", "hello"]]) })
const result = await db.get("x.file")
console.info(result) // ← "hello"db.fetch(uri, FetchOptions)
Like get, plus advanced features: refs, vars, inherit rules processing.
Supports extension lookup, e.g. find .json even when omitted.
How to load extended data?
import DB from "@nan0web/db"
const db = new DB({ predefined: [["file.json", { value: "loaded" }]] })
await db.connect()
const result = await db.fetch("file")
console.info(result) // ← { value: "loaded" }db.set(uri, data)
Sets document content and marks metadata updates.
How to save new content?
import DB from "@nan0web/db"
const db = new DB()
const res = await db.set("file.text", "save me!")
console.info(res) // ← "save me!"
console.info(db.data.get("file.text")) // ← "save me!"Data.flatten(data)
Flattens nested object into paths as keys.
How to flatten object?
import { Data } from "@nan0web/db"
const flat = Data.flatten({ x: { a: [1, 2, { b: 3 }] } })
console.info(flat) // ← { 'x/a/[0]': 1, 'x/a/[1]': 2, 'x/a/[2]/b': 3 }Data.unflatten(data)
Reconstructs nested structure from flat keys.
How to unflatten data?
import { Data } from "@nan0web/db"
const nested = Data.unflatten({
"x/y/z": 7,
"arr/[0]/title": "first",
"arr/[1]/title": "second"
})
console.info(nested) // ← { x: { y: { z: 7 } }, arr: [ { title: 'first' }, { title: 'second' } ] }Data.merge(a, b)
Deep merges two objects, handling array conflicts by replacing.
How to merge deeply?
import { Data } from "@nan0web/db"
const a = { x: { one: 1 }, arr: [0] }
const b = { y: "two", x: { two: 2 }, arr: [1] }
const merged = Data.merge(a, b)
console.info(merged) // ← { x: { one: 1, two: 2 }, y: 'two', arr: [ 1 ] }Path Utilities
@nan0web/db/path provides URI/path resolution functions for cross-platform use.
Supports normalization, basename/dirname extraction, and absolute/relative resolution.
Import Path Utilities
How to import path utilities?
import { normalize, basename, dirname, absolute, resolveSync } from '@nan0web/db/path'
console.info(normalize("a/b/../c")) // ← a/c
console.info(basename("path/to/file.txt")) // ← file.txt
console.info(dirname("path/to/file.txt")) // ← path/to/
console.info(absolute("/base", "root", "file")) // ← /base/root/file
console.info(resolveSync("/base", ".", "file.txt")) // ← file.txtnormalize(...segments)
Normalizes path segments, handling ../, ./, and duplicate slashes.
How to normalize path segments?
import { normalize } from '@nan0web/db/path'
console.info(normalize("a/b/../c")) // ← a/c
console.info(normalize("a//b///c")) // ← a/b/c
console.info(normalize("dir/sub/")) // ← dir/sub/basename(uri, [suffix])
Extracts basename, optionally removing suffix or extension.
How to extract basename?
import { basename } from '@nan0web/db/path'
console.info(basename("/dir/file.txt")) // ← file.txt
console.info(basename("/dir/file.txt", ".txt")) // ← file
console.info(basename("/dir/file.txt", true)) // ← file (remove ext)
console.info(basename("/dir/")) // ← dir/dirname(uri)
Extracts parent directory path.
How to extract dirname?
import { dirname } from '@nan0web/db/path'
console.info(dirname("/a/b/file")) // ← /a/b/
console.info(dirname("/a/b/")) // ← /a/
console.info(dirname("/file")) // ← /
console.info(dirname("file.txt")) // ← .extname(uri)
Extracts file extension with dot (lowercase).
How to extract extension?
import { extname } from '@nan0web/db/path'
console.info(extname("file.TXT")) // ← .txt
console.info(extname("archive.tar.gz")) // ← .gz
console.info(extname("noext")) // ← ''
console.info(extname("/dir/")) // ← ''resolveSync(cwd, root, ...segments)
Resolves segments relative to cwd/root (synchronous).
How to resolve path synchronously?
import { resolveSync } from '@nan0web/db/path'
console.info(resolveSync("/base", ".", "a/b/../c")) // ← a/crelative(from, to)
Computes relative path from from to to.
How to compute relative path?
import { relative } from '@nan0web/db/path'
console.info(relative("/a/b", "/a/c")) // ← c
console.info(relative("/root/dir", "/root/")) // ← dirabsolute(cwd, root, ...segments)
Builds absolute path/URL from cwd, root, and segments.
How to build absolute path?
import { absolute } from '@nan0web/db/path'
console.info(absolute("/base", "root", "file")) // ← /base/root/file
console.info(absolute("https://ex.com", "api", "v1")) // ← https://ex.com/api/v1isRemote(uri) & isAbsolute(uri)
Checks if URI is remote or absolute.
How to check URI type?
import { isRemote, isAbsolute } from '@nan0web/db/path'
console.info(isRemote("https://ex.com")) // ← true
console.info(isAbsolute("/abs/path")) // ← true
console.info(isAbsolute("./rel")) // ← falseJava•Script types & Autocomplete
Package is fully typed with jsdoc and d.ts.
How many d.ts files should cover the source?
Drivers & Extensions
Drivers extend DB with storage backends. Extend DBDriverProtocol for custom logic.
Basic Driver Extension
How to extend DBDriverProtocol?
import { DBDriverProtocol } from '@nan0web/db'
class MyDriver extends DBDriverProtocol {
async read(uri) {
// Custom read logic
return { data: 'from custom storage' }
}
}
const driver = new MyDriver()
console.log(await driver.read("/path")) // ← { data: 'from custom storage' }Using Driver in DB
How to attach driver to DB?
import { DB, DBDriverProtocol } from '@nan0web/db'
class SimpleDriver extends DBDriverProtocol {
async read(uri) { return `Read: ${uri}` }
async write(uri, data) { return true }
}
class ExtendedDB extends DB {
constructor() {
super({ driver: new SimpleDriver() })
this.loadDocument = async (uri) => await this.driver.read(uri)
this.saveDocument = async (uri, data) => await this.driver.write(uri, data)
}
}
const db = new ExtendedDB()
await db.connect()
console.info(await db.get('/test')) // ← Read: testAuthentication & Authorization
Use AuthContext for role-based access in DB operations.
Basic AuthContext Usage
How to create AuthContext?
import { AuthContext } from '@nan0web/db'
const ctx = new AuthContext({ role: 'user', roles: ['user', 'guest'] })
console.info(ctx.hasRole('user')) // ← true
console.info(ctx.role) // ← userAuthContext with DB Access
How to use AuthContext in DB?
import { DB, AuthContext } from '@nan0web/db'
const db = new DB()
const ctx = new AuthContext({ role: 'admin' })
await db.set('secure/file.txt', 'secret', ctx)
console.info(await db.get('secure/file.txt', {}, ctx)) // ← secretHandling Access Failures
How to handle auth failures?
import { AuthContext } from '@nan0web/db'
const ctx = new AuthContext()
ctx.fail(new Error('Access denied'))
console.info(ctx.fails) // ← [Error: Access denied]
console.info(ctx.hasRole('admin')) // ← falseContributing
How to participate? – see CONTRIBUTING.md
License
ISC LICENSE – see full text
