cracksdb
v1.1.0
Published
A minimalist permanent state management package.
Downloads
13
Maintainers
Readme
cracksdb
A minimalist permanent state management npm package.
StateFile Architecture is the concept behind cracksdb. In case you're willing to know how exatly it works, contributes to it, or use all it's features properly. You may want to read this article.
Usage
You can install cracksdb in your node project with the command:
npm i cracksdb
cracksdb can be used either by javascript or typescript. However, typescript is highly recommended for best practice; with javascript it can be easly misused. For instance, stateFile.extendUnitType(obj: Type)
method can be passed object values that are not compatible with Type
, and that could wind up with unexpected results or errors.
Using cracksdb is very simple, we shall first import StateManager and FileManager to initialize or minial db variable.
import { StateManager, FileManager } from 'cracksdb'
Then we construct a StateManager instance by passing to the constructor the root dir value and a FileManager instance.
FileManager constructor could be passed an object of File object values, however, it's not necessary. An empty object will do the job.
const db = new StateManager("./db", new FileManager({}));
StateManager automatically scans the root dir and loads the application state for you. However, in case it's just empty so far. You may define and declare your application substates with the method db.add(substate_name)
.
const users = db.add("users") // returns a StateFile object
The structure of data units stored in users substate may be defined either by using users.extendUnitType(obj: Type)
method or by using generic db.add<DataUnit>(...)
at the first place. However, as obvious, the generic method only specifies the unit type for typescript autocomplete. Therefore, it's highly recommended to use extendUnitType method (right after creating the StateFile with db.add(...)) to ensure data integrity, and more importantly to enable updating data units as it will be illustrated next.
Type is a map from strings to the set of values: "string", "number", "boolean", and Type itself.
const users = db.add<{name: string}>("users")
users.extendUnitType({
age: "number"
})
To update your data units you can use StateFile.update(...)
method which takes two arguments: first the index of the data unit, and second a builder function. A builder function simply takes the data unit, at the specified index, as a parameter and returns a new data unit object with the updated fields. A worth mentioning subtlety, in StateFile update mechanism, is that update methods can only update fields that are declared by StateFile.extendUnitType
method. As shown in the following example:
const users = db.add<{name: string}>("users")
users.extendUnitType({
age: "number"
})
users.add({
name: "Joe",
age: 30
})
users.update(0, (prev) => ({ age: prev.age + 1 })) // yields { name: "Joe", age: 31 }
users.update(0, (prev) => ({ name: "John", age: 25 })) // throws an error as name is not declared in the unittype
You can add (unupdatable) fields that are not declared in the unittype. However, this practice is not recommended; as the undeclared fields will get removed automatically if extendUnitType is invoked again. In fact, extendUnitType could be computationally very expensive; it trims and restructs all data units in all state files.
To retrieve StateFiles from db
, remove, or delete them. You may write:
const users = db.get("users")
users.add({
name: "Jack",
age: 20
})
console.log(user.get(0)) // {name: "jack", age: 20}
console.log(user.getWhere(obj => (obj.age > 15))) // {name: "jack", age: 20}
console.log(user.removeWhere(obj => (obj.age > 15))) // [true]
db.remove("users") // only removed virtually (from RAM).
db.delete("users", "mypassword") // removes users virtually and physically.
StateManager can delete a StateFile only if it's been assigned a passkey at first place. passkeys can be assigned to StateFiles by passing them in the second parameter of db.add(substate_name, passkey)
method.
const mysf = db.add("mysf", "password")
db.delete("mysf", ""); // logs a warning
db.delete("mysf", "password"); // deletes mysf files
With cracksdb version 1.1.0 or later, you can declare array fields just like declaring objects in the StateFile.extendUnitType
method, but with the length attribute defined as "number".
const users = db.add("users")
users.extendUnitType({
username: "string",
posts: {
length: "number",
title: "string",
content: "string"
}
})
users.add({
username: "John",
posts: [{
title: "My First Post",
content: "Lorem..."
}]
})
users.extendUnitType({ posts: { views: "number" } })
console.log(users.get(0)) // { username: "John", posts: [{title: "My First Post", content: "Lorem...", view: 0}] }
That's pretty much it. Now you are ready to go with crackdb.
In case you want to know more about cracksdb and how it works, you may read this article. Or you can just play around with it by using the below interface as a reference to its methods.
interface StateFile<DataUnit> {
len(): number;
passkey(): string;
setLimit(limit: number): void;
getSimul(): boolean;
setSimul(simul: boolean): void;
addMetaAttr(attr: string, value: string): boolean;
rmvMetaAttr(attr: string): boolean;
getUnitType(): Type;
extendUnitType(extension: Type): void;
get(index: number): DataUnit;
getWhere(cond: Condition<DataUnit>): DataUnit;
getList(from: number, to: number): Array<DataUnit>;
getListWhere(cond: Condition<DataUnit>): Array<DataUnit>;
getIndexOf(cond: Condition<DataUnit>): number[];
add(obj: DataUnit): void;
update(index: number, builder: (prev: DataUnit) => DataUnit): void;
updateWhere(cond: Condition<DataUnit>, builder: (prev: DataUnit) => DataUnit): boolean[];
remove(index: number): void;
removeWhere(cond: Condition<DataUnit>): boolean[];
loadAll(): void;
loadOne(): boolean;
unloadOne(): boolean;
seal(newCrackData?: Array<DataUnit>): void;
split(): void;
save(): void;
}