@aurios/jason
Version:
A simple, lightweight, and embeddable JSON document database built on Bun.
108 lines (95 loc) • 3.52 kB
text/typescript
import { access, mkdir, readdir } from "node:fs/promises";
import path from "node:path";
import Collection from "../data/collection.js";
import type {
CollectionOptions,
Document,
JasonDBOptions,
} from "../types/index.js";
export default class JasonDB<T> {
#basePath: string;
#collections = new Map<keyof T, Collection<T, keyof T>>();
/**
* Creates a new JasonDB instance with the given configuration.
* @param options - Either a string representing the database name (will be created in current working directory),
* or a JasonDBOptions configuration object
* @example
* // Simple usage
* const db = new JasonDB('my-database');
*
* // Advanced usage
* const db = new JasonDB({
* basename: 'my-database',
* path: './custom-location'
* });
*/
constructor(options: string | JasonDBOptions) {
if (typeof options === "string") {
const cwd = path.resolve(".");
this.#basePath = path.join(cwd, options);
} else {
const cwd = path.resolve(options.path);
this.#basePath = path.join(cwd, options.basename);
}
this.#ensureDataDirExists();
}
async #ensureDataDirExists() {
try {
await access(this.#basePath);
} catch {
await mkdir(this.#basePath, { recursive: true });
}
}
/**
* Retrieves or creates a collection in the database.
*
* If a collection with the given name does not exist, it initializes a new collection
* with the provided options and stores it. If the collection already exists, it returns the existing one.
*
* @param name - The name of the collection.
* @param options - Optional settings for the collection.
* @param options.initialData - An array of initial data to populate the collection.
* @param options.schema - A validation function for the collection's documents.
* @param options.concurrencyStrategy - The concurrency strategy to use for the collection.
* @param options.cacheTimeout - The cache timeout in milliseconds.
* @param options.generateMetadata - Whether to generate metadata for the collection.
* @returns The collection instance associated with the given name.
*/
collection<K extends keyof T>(
name: K,
options: CollectionOptions<Document<T, K>> = {}
): Collection<T, K> {
const existingCollection = this.#collections.get(name);
if (existingCollection) {
return existingCollection as Collection<T, K>;
}
const newCollection = new Collection<T, K>(this.#basePath, name, options);
this.#collections.set(name, newCollection);
return newCollection;
}
/**
* Lists all collections in the database.
*
* Reads the base directory and returns the names of all subdirectories,
* which represent the collections.
*
* @returns A promise that resolves to an array of collection names.
* If an error occurs, it resolves to an empty array.
*/
async listCollections(): Promise<(keyof T)[]> {
try {
if (this.#collections.size > 0)
return Array.from(this.#collections.keys());
const entries = await readdir(this.#basePath, { withFileTypes: true });
return entries
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("_"))
.map((entry) => entry.name) as (keyof T)[];
} catch (error) {
console.error("Failed to list collections", {
path: this.#basePath,
error: error instanceof Error ? error.message : "Unknown error",
});
return [];
}
}
}