Skip to content

Latest commit

 

History

History
186 lines (145 loc) · 8.76 KB

File metadata and controls

186 lines (145 loc) · 8.76 KB

API Reference

new SencilloDB(config)

Every field is optional.

Option Type Default Meaning
file string "./sencillo.json" Single file mode. Parent folders are created if needed.
folder string Folder mode: one file per collection, loaded on demand.
sharding boolean false Folder mode only: one file per index bucket.
compression boolean false gzip the persisted files.
aof boolean false Append writes to a log instead of rewriting the store.
appendfsync "always" | "everysec" | "no" "everysec" How aggressively the log is flushed to disk.
maxCacheSize number 0 Collections/shards kept in memory. 0 means no limit.
clone boolean true Return deep copies instead of live references.
lock boolean | { staleMs, timeoutMs, retryMs } false Advisory cross process lock file.
loadHook () => Promise<string> Single file mode: load the store from somewhere else.
saveHook (json: string) => Promise<void> Single file mode: save the store somewhere else.
debug boolean false Also print internal warnings to stderr.

Rejected combinations throw a ValidationError: sharding without folder, file together with folder, and hooks together with folder.

Database methods

db.transaction(callback)

Runs callback(tx) and commits when it returns. Resolves to the callback's return value. If the callback throws, every change is discarded and the error is re-thrown.

db.compact()

Folds the append only log back into the store and deletes the log. Also purges expired documents. Safe to call in any mode.

db.close()

Writes anything still pending and drops the in-memory cache. The instance stays usable — the next transaction reloads from disk.

db.export()

Loads every collection (and every shard) and resolves to a plain object copy of the whole store.

db.import(data, options?)

Replaces the store with data. With { merge: true }, existing collections are kept and merged rather than removed. The result is written straight through and the log is reset.

db.snapshot(path)

Writes export() to path. Gzipped when the path ends in .gz.

db.migrate(migrations)

Runs any migration whose version has not been recorded yet, in ascending version order, each inside its own transaction. Applied versions are stored in the __migrations collection. Resolves to the list of versions applied by this call.

await db.migrate([
  { version: 1, name: "seed", up: async (tx) => { await tx.create({ collection: "users", data: { name: "root" } }); } },
  { version: 2, name: "add-role", up: async (tx) => { await tx.updateMany({ collection: "users", filter: {}, $set: { role: "member" } }); } },
]);

A migration that throws is not recorded, so it runs again next time.

SencilloDB extends EventEmitter; see Events.

Transaction methods

All are asynchronous and take a single instructions object.

Method Returns Notes
tx.create the created document Needs data.
tx.createMany array of created documents data must be an array. index may be a function.
tx.update the updated document Needs _id or filter, and data or an update operator.
tx.updateMany array of updated documents Applies the same patch to every match.
tx.destroy the removed document Needs _id or filter.
tx.destroyMany array of removed documents Removes every match.
tx.find the first match or undefined
tx.findMany array of matches Honours sort, skip, limit, populate.
tx.count number Same matching rules as findMany.
tx.dropCollection void Deletes the collection and its files.
tx.dropIndex void Deletes one index bucket, its documents and (in sharded mode) its file.
tx.rewriteCollection void Rebuilds the collection: renumbers ids, re-sorts, re-buckets. Secondary indexes and TTL rules are preserved.
tx.ensureIndex void { collection, field, unique? }. Builds the index over existing documents.
tx.dropSecondaryIndex void { collection, field }.
tx.ensureTTL void { collection, field, seconds }.

tx.update in detail

// full replacement (fields not present are dropped)
await tx.update({ collection: "users", _id: 1, data: { name: "Alice", age: 31 } });

// partial patch
await tx.update({ collection: "users", _id: 1, $set: { age: 31 } });
await tx.update({ collection: "users", _id: 1, $inc: { visits: 1 } });
await tx.update({ collection: "users", _id: 1, $unset: ["temporary"] });

// by filter, creating the document when nothing matches
await tx.update({
  collection: "settings",
  filter: { key: "theme" },
  $set: { value: "dark" },
  upsert: true,
});

$set and $unset accept dot paths ("profile.theme"). Update operators are applied on top of data when both are given.

The instructions object

Field Used by Meaning
collection all Collection name. Defaults to "default".
index all Index bucket. A string, a (document) => string function (create/createMany/update), or { current, new } to move a document. For reads it restricts the search to one bucket.
data create, createMany, update The document (or array of documents).
_id update, destroy Target document id.
filter reads, update, destroy, *Many Query object — see Querying.
callback reads (document) => boolean, combined with filter via AND.
sort findMany, rewriteCollection Array.sort comparator. Defaults to ascending _id.
limit / skip findMany Pagination, applied after sorting.
populate find, findMany [{ field, collection, targetField? }]. targetField defaults to _id.
$set / $inc / $unset update, updateMany Partial update operators.
upsert update Create the document when nothing matches.

quickTx(db)

Wraps a single operation in its own transaction:

import { quickTx } from "sencillodb";

const qtx = quickTx(db);
const user = await qtx("create", { collection: "users", data: { name: "Alice" } });

createResourceManager(config)

A schema-validating, hook-aware wrapper around one collection. See Schemas and Resource Managers.

const User = createResourceManager({
  db,
  collection: "users",
  index: (user) => user.country,
  schema: { name: String, age: { type: Number, required: false, default: 0 } },
  hooks: { beforeCreate: (data) => ({ ...data, createdAt: new Date().toISOString() }) },
});

await User.create({ data: { name: "Alice" } });
await User.findMany({ filter: { age: { $gte: 18 } } });

Returned object: schema, validate(document), errors(document), withDefaults(document), execute(operation, instructions) and shorthands create, createMany, update, updateMany, destroy, destroyMany, find, findMany, count.

Errors

All extend SencilloDBError, which extends Error.

Error Thrown when
ValidationError Bad instructions, bad config, or a schema violation.
CollectionNotFoundError The collection does not exist.
IndexNotFoundError The index bucket does not exist.
DocumentNotFoundError No document matched the _id or filter.
UniqueConstraintError A write would duplicate a value on a unique index.
CorruptDataError A store file exists but could not be read.
LockError The advisory lock could not be acquired before the timeout.
DatabaseNotLoadedError An operation was used outside a transaction before any load.

Events

db.on("create", ({ collection, index, _id, doc }) => { /* … */ });
Event Payload Fires
create, update, destroy { collection, index, _id, doc } After the transaction commits
dropCollection, dropIndex, rewriteCollection { collection, index? } After commit
ensureIndex, dropSecondaryIndex, ensureTTL { collection } After commit
expire { collection, index, _id, doc } During compact()
commit array of the events just emitted After commit
rollback { error } After a failed transaction
warning { message, detail } On a recoverable problem, e.g. an unreadable log line