cero-base
v1.1.1
Published
P2P database with collection-based CRUD, RPC proxy, and React hooks
Maintainers
Readme
cero-base
Collection-based CRUD layer on top of cero. Define your schema, get put/get/del/sub across three scopes with built-in pagination.
Install
npm install cero-baseSchema
import { t } from 'cero-base'
export const schema = t.schema(
{
drafts: { fields: { id: t.string, content: t.string }, key: ['id'], scope: 'local' },
settings: { fields: { key: t.string, value: t.json }, key: ['key'] },
messages: {
fields: { id: t.string, text: t.string, memberId: t.string },
key: ['id'],
scope: 'shared',
timestamps: true
}
},
{ namespace: 'chat' }
)| Scope | Replication |
| ------------------- | --------------------- |
| local | Device-only |
| private (default) | Across paired devices |
| shared | Between room members |
Build & Use
import { build } from 'cero-base'
await build('./spec', schema)import { Cero } from 'cero-base'
const db = new Cero('./data', schema, spec)
await db.ready()
const drafts = db.collection('drafts')
await drafts.put({ content: 'WIP' })
const room = await db.rooms.open()
const messages = room.collection('messages')
await messages.put({ text: 'Hello!', memberId: db.id })Collections
await col.put({ text: 'Hello' }) // upsert (auto-generates id)
await col.get() // all
await col.get({ done: false }) // filter
await col.del({ id: 'abc' }) // delete
col.sub().on('data', console.log) // subscribePagination
Every non-local collection gets a monotonic index on insert.
await col.get({ gte: 50, lte: 80 }) // range query
await col.get({ gte: 0 }, { reverse: true, limit: 30 })
await col.total() // count (no full scan)Rooms
const room = await db.rooms.open() // create
const room = await db.rooms.open(roomId) // open
const room = await db.rooms.open(invite) // join
room.collection('messages')
room.id
room.members
room.invites
room.profileBatch
const batch = db.batch()
batch.put('settings', { key: 'a', value: '1' })
batch.put('settings', { key: 'b', value: '2' })
await batch.flush()Device Pairing
const invite = await db.devices.invite() // Device A
await db2.join(invite) // Device BRecovery
Restore an identity on a new device using the seed phrase. Requires an existing device online.
const seed = db.seed() // save this securely
await db2.join(seed) // recover on new deviceReact
import {
Cero,
Room,
useCero,
useRoom,
useProfile,
useRooms,
useCollection,
useMembers
} from 'cero-base/react'
function App() {
return (
<Cero db={db}>
<Room id={roomId}>
<Chat />
</Room>
</Cero>
)
}
function Chat() {
const { data, put, sub } = useCollection('messages')
return (
<div>
{data.map((m) => (
<p key={m.id}>{m.text}</p>
))}
</div>
)
}| Hook | Returns |
| --------------------- | ------------------------------------------- |
| useCero() | db |
| useRoom() | room |
| useProfile() | { data, busy, error, set } |
| useRooms() | { data, busy, error } |
| useCollection(name) | { data, busy, error, put, del, get, sub } |
| useMembers() | { data, busy, error } |
RPC Proxy
For process isolation (Pear/Electron). Worker runs cero, renderer uses the same API over IPC.
// Worker
import { serve } from 'cero-base/rpc'
serve(Chats, Bare.IPC, storage)
// Renderer
import { Cero } from 'cero-base/rpc/proxy'
const db = new Cero(ipc, schema, spec)
await db.ready()Types
| Helper | Type |
| ---------------------- | ------------ |
| t.string | String |
| t.uint | Unsigned int |
| t.int | Signed int |
| t.float | Float |
| t.bool | Boolean |
| t.buffer | Buffer |
| t.json | JSON |
| t.optional(t.string) | Optional |
| t.array(t.string) | Array |
| t.ref('tasks') | Reference |
License
Apache-2.0
