astriveone-db
v1.2.0
Published
Universal Database Engine For Everything. Install once. Use everywhere.
Maintainers
Readme
astriveone-db
Universal Database Engine. One package, installed once.
npm install astriveone-dbQuick start
import { AstriveOne } from "astriveone-db";
const db = new AstriveOne(); // in-memory, zero config
await db.connect();
await db.set("users", { id: "001", username: "bayu" });
const user = await db.get("users", "001");
// { id: "001", username: "bayu" }
await db.close();Drivers — what actually works where
Each driver has its own platform requirements. Choose the right one for your runtime.
memory — all platforms ✅
const db = new AstriveOne({ driver: "memory" });| Platform | Works | |---|---| | Node.js ≥ 18 | ✅ | | Bun | ✅ | | Deno | ✅ | | Browser | ✅ | | Cloudflare Workers | ✅ | | Vercel Edge | ✅ | | React Native | ✅ |
Data lives only in process memory. Zero persistence — use remote or pair with a server for shared/persistent access.
json — Node / Bun / Deno only ⚠️
const db = new AstriveOne({ driver: "json", path: "./data.json" });| Platform | Works |
|---|---|
| Node.js ≥ 18 | ✅ |
| Bun | ✅ |
| Deno (with --allow-write) | ✅ |
| Browser | ❌ No filesystem |
| Cloudflare Workers | ❌ No filesystem |
| Vercel Edge | ❌ No filesystem |
| AWS Lambda /tmp | ⚠️ Ephemeral only — data lost on cold start |
| React Native | ❌ No Node fs module |
Calling db.connect() on a platform without a filesystem throws immediately with a clear error message rather than failing silently later.
Writes are crash-safe: data is written to a tmp file and atomically renamed, so a power loss mid-write cannot corrupt the database.
sqlite — Node 22+ / Bun ⚠️
const db = new AstriveOne({ driver: "sqlite", path: "./app.db" });
// or in-memory:
const db = new AstriveOne({ driver: "sqlite", path: ":memory:" });| Platform | Works |
|---|---|
| Node.js ≥ 22 | ✅ Uses built-in node:sqlite — no extra packages |
| Bun | ✅ |
| Node.js < 22 | ❌ node:sqlite not available |
| Browser / Edge / RN | ❌ No SQLite runtime |
Uses Node 22's built-in node:sqlite module — zero extra npm dependencies. Native SQL transactions are fully supported (not a snapshot/rollback workaround):
await db.transaction(async (tx) => {
await tx.update("accounts", "a1", { balance: 50 });
await tx.update("accounts", "a2", { balance: 150 });
// if anything throws → full ROLLBACK, no partial writes
});
// Direct SQL for complex queries:
const storage = new SQLiteStorage("./app.db");
const rows = storage.exec("SELECT COUNT(*) as c FROM __documents WHERE collection = ?", ["users"]);remote — all platforms ✅
const db = new AstriveOne({
mode: "remote",
url: "https://db.example.com",
apiKey: process.env.ASTRIVEONE_KEY,
});An HTTP client over the AstriveOne-DB REST+WebSocket wire protocol. Works anywhere fetch is available (all modern JS runtimes). Start a server with await db.start() on your Node/Bun backend and point this at it from any other language or platform.
Planned — not yet implemented 🚧
| Driver | Status |
|---|---|
| postgres | 🚧 Planned |
| mysql | 🚧 Planned |
| mongo | 🚧 Planned |
| redis | 🚧 Planned |
Calling connect() with any of these drivers throws a clear error that says exactly what is and isn't implemented. There is no driver that pretends to work but fails silently.
CRUD
// Insert — auto-generates id if omitted
await db.set("posts", { id: "p1", title: "Hello", views: 0 });
await db.set("posts", { title: "Auto ID" }); // id generated
// Read — returns null (not undefined, not an error) if missing
const post = await db.get("posts", "p1");
// Find with operators
const results = await db.find("posts", {
where: {
views: { $gte: 100 }, // range
category: { $in: ["tech", "design"] }, // in list
title: { $contains: "guide" }, // string match
draft: { $ne: true }, // not equal
published: true, // exact match
},
sort: { views: -1 }, // -1 desc, 1 asc
limit: 20,
offset: 0,
});
// Partial update — keeps fields not in patch
await db.update("posts", "p1", { views: 1 });
// Delete — returns false if document didn't exist
const deleted = await db.delete("posts", "p1");Transactions
await db.transaction(async (tx) => {
const sender = await tx.get("accounts", "alice");
if (sender.balance < 100) throw new Error("insufficient funds");
await tx.update("accounts", "alice", { balance: sender.balance - 100 });
await tx.update("accounts", "bob", { balance: bob.balance + 100 });
// if anything throws, all writes are rolled back
});Memory/JSON drivers use snapshot-and-rollback. SQLite driver uses native SQL BEGIN/COMMIT/ROLLBACK — guaranteed atomic by the database engine.
Realtime
// Watch a collection — fires on insert, update, delete
const stop = db.watch("messages", (event) => {
console.log(event.type); // "insert" | "update" | "delete"
console.log(event.id); // document id
console.log(event.doc); // new/updated document
console.log(event.before); // previous state (update/delete)
});
// Watch everything
const stopAll = db.watchAll((event) => {
console.log(`${event.collection}/${event.id}: ${event.type}`);
});
// Custom pub/sub
db.subscribe("notifications", (payload) => console.log(payload));
db.emit("notifications", { text: "New message" });
// Stop watching
stop();The realtime bus is a zero-dependency in-process event system (plain Map<string, Set<fn>>). No node:events, no EventEmitter — runs identically on Node, Bun, Deno, browsers, Cloudflare Workers, and React Native.
Authentication
// Register and login
await db.auth.register("bayu", "password123", ["admin"]);
const session = await db.auth.login("bayu", "password123");
console.log(session.token); // secure random token
// Verify and check roles
const verified = await db.auth.verify(session.token);
const isAdmin = db.auth.hasRole(verified, "admin");
// Logout (invalidates the token server-side)
await db.auth.logout(session.token);
// API keys for service-to-service auth
const key = await db.auth.createApiKey("my-bot", ["service"]);
await db.auth.verifyApiKey(key);
await db.auth.revokeApiKey(key);External identity (OAuth integration)
This package does not perform OAuth flows (redirects, code exchange, provider API calls). That is your responsibility. Once you have verified an external identity, call:
// After YOU have verified the Google/GitHub/etc token and obtained the user's external id:
const session = await db.auth.createSessionFromExternalIdentity(
"google",
verifiedGoogleUserId, // from Google's verified token payload
{ email: "[email protected]", name: "Bayu" }
);If the user does not exist yet, a local record is created automatically. If they do, a new session is issued for the existing record.
Encryption at rest
// Requires an explicit, non-empty secret. encrypt: true alone is rejected.
const db = new AstriveOne({
driver: "json",
path: "./data.json",
encrypt: { secret: process.env.DB_SECRET }, // must be explicit
});Uses AES-256-GCM. The file on disk is completely unreadable without the secret. There is no hardcoded fallback key — passing an empty or whitespace-only secret throws immediately at connect().
Validation
db.defineSchema("users", {
username: { type: "string", required: true, min: 3, max: 32 },
email: { type: "string", required: true, pattern: "^[^@]+@[^@]+$" },
age: { type: "number", min: 0, max: 120 },
active: { type: "boolean" },
});
// This throws before touching storage:
await db.set("users", { username: "ab" });
// AstriveOne-DB: validation failed for "users": "username" min length is 3Built-in cache
await db.useCache({ ttl: 30_000, maxEntries: 1000 });
// Reads are cached — identical object reference on cache hit:
const a = await db.get("users", "u1");
const b = await db.get("users", "u1"); // same reference, from cache
// Cache is invalidated automatically on any write to the same collection
await db.update("users", "u1", { name: "Updated" });
const c = await db.get("users", "u1"); // fresh from storageSecurity
// Rate limiting (token bucket per collection)
db.enableRateLimit(100, 60_000); // 100 ops per minute
// Audit log — every mutation recorded
const db = new AstriveOne({ audit: true });
// Backup / restore
await db.backup("./backups/2026-06-25.json");
await db.restore("./backups/2026-06-25.json");Studio (visual admin dashboard)
const studio = await db.start({ port: 4848 });
// Prints:
// ╔══════════════════════════════════════════════╗
// ║ AstriveOne-DB v1.0.0 ║
// ╠══════════════════════════════════════════════╣
// ║ Status: ONLINE ║
// ║ REST: http://localhost:4848/api/db ║
// ║ Studio: http://localhost:4848/studio ║
// ╠══════════════════════════════════════════════╣
// ║ Access Token: ║
// ║ astro_root_xxxxxxxxxxxxxxxxxxxxxxxxxxxx ║
// ╚══════════════════════════════════════════════╝
// Tokens for sharing access:
const viewToken = await db.createToken("viewer", "read-only client", { expire: "24h" });
const editToken = await db.createToken("editor", "CI bot");
const list = await db.listTokens();
await db.revokeToken(list[0].id);
const rotated = await db.rotateToken(oldToken);Examples
Discord bot
import { Client, GatewayIntentBits } from "discord.js";
import { AstriveOne } from "astriveone-db";
const db = new AstriveOne({ driver: "json", path: "./bot.json" });
await db.connect();
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.on("interactionCreate", async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "save") {
await db.set("notes", { id: crypto.randomUUID(), userId: interaction.user.id, text: interaction.options.getString("text") });
await interaction.reply({ content: "Saved ✓", ephemeral: true });
}
});
client.login(process.env.DISCORD_TOKEN);WhatsApp bot
import { Client } from "whatsapp-web.js";
import { AstriveOne } from "astriveone-db";
const db = new AstriveOne({ driver: "json", path: "./wa.json" });
await db.connect();
const client = new Client();
client.on("message", async (msg) => {
if (msg.body.startsWith("!save ")) {
await db.set("notes", { id: crypto.randomUUID(), from: msg.from, text: msg.body.slice(6) });
msg.reply("Saved ✓");
}
});
client.initialize();License
MIT © althericoffcv — follow on GitHub
