A temporal fact database for TypeScript.
Store facts. Query relationships. Derive state. Keep the complete history.
Bitemporal facts, Datalog, and typed, content-addressed configuration—built on Effect.
import { Effect } from "effect";
import { Triples } from "@bjacobso/triplex";
export const openGradingTasks = Effect.gen(function* () {
const triples = yield* Triples;
return yield* triples.query({
find: ["?student", "?quiz"],
where: [
["?submission", ":submission/student", "?student"],
["?submission", ":submission/quiz", "?quiz"],
["not", ["?submission", ":submission/grade", "?grade"]],
],
});
});import { Effect } from "effect";
import { EntityId, KvTriples, Triples, ref } from "@bjacobso/triplex";
const student = EntityId.make("student:ada");
const quiz = EntityId.make("quiz:logic-1");
const submission = EntityId.make("submission:ada:logic-1");
export const submitQuiz = Effect.gen(function* () {
const triples = yield* Triples;
return yield* triples.transact(
[
{
op: "assert",
entityId: submission,
entityType: "Submission",
attribute: ":submission/student",
value: ref(student),
},
{
op: "assert",
entityId: submission,
entityType: "Submission",
attribute: ":submission/quiz",
value: ref(quiz),
},
],
{
actor: "teacher:grace",
commandId: "submit:ada:logic-1",
configSnapshot: "config:learning-2026.1",
},
);
}).pipe(Effect.provide(KvTriples.layer));import { Effect } from "effect";
import { Attribute, ConfigStore, EntityType } from "@bjacobso/triplex/config";
const StudentName = Attribute.text(":student/name");
const Student = EntityType.make("Student", {
attributes: {
name: Attribute.use(StudentName, {
required: true,
unique: true,
}),
},
});
export const publishSchema = Effect.gen(function* () {
const config = yield* ConfigStore.ConfigStore;
return yield* config.commit({
label: "learning-2026.1",
objects: yield* Student.nodes,
ref: "live",
});
});