@cero-base/core 0.2.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +180 -136
- package/package.json +123 -28
- package/src/blobs/index.js +297 -0
- package/src/database/CLAUDE.md +3 -0
- package/src/database/bootstrap.js +76 -0
- package/src/database/dispatch.js +156 -0
- package/src/database/index.js +572 -0
- package/src/identity/CLAUDE.md +3 -0
- package/src/identity/index.js +232 -0
- package/src/index.js +20 -1
- package/src/lib/CLAUDE.md +3 -0
- package/src/lib/constants.js +24 -4
- package/src/lib/errors.js +150 -0
- package/src/lib/schema.js +58 -440
- package/src/lib/spec/index.js +353 -0
- package/src/lib/spec/schema.json +284 -0
- package/src/lib/utils.js +54 -49
- package/src/network/discovery.js +80 -0
- package/src/network/index.js +231 -0
- package/src/pairing/index.js +482 -0
- package/src/pairing/invite.js +199 -0
- package/src/rpc/client.js +45 -0
- package/src/rpc/index.js +141 -0
- package/src/rpc/server.js +45 -0
- package/src/storage/index.js +261 -0
- package/types/blobs/index.d.ts +169 -0
- package/types/database/bootstrap.d.ts +17 -0
- package/types/database/dispatch.d.ts +8 -0
- package/types/database/index.d.ts +329 -0
- package/types/identity/index.d.ts +160 -0
- package/types/index.d.ts +11 -0
- package/types/lib/constants.d.ts +13 -0
- package/types/lib/errors.d.ts +110 -0
- package/types/lib/schema.d.ts +53 -0
- package/types/lib/spec/index.d.ts +95 -0
- package/types/lib/utils.d.ts +39 -0
- package/types/network/discovery.d.ts +44 -0
- package/types/network/index.d.ts +115 -0
- package/types/pairing/index.d.ts +194 -0
- package/types/pairing/invite.d.ts +157 -0
- package/types/rpc/client.d.ts +18 -0
- package/types/rpc/index.d.ts +67 -0
- package/types/rpc/server.d.ts +18 -0
- package/types/storage/index.d.ts +163 -0
- package/src/lib/base.js +0 -84
- package/src/lib/batch.js +0 -98
- package/src/lib/builder.js +0 -24
- package/src/lib/collection.js +0 -252
- package/src/lib/crypto.js +0 -6
- package/src/lib/index.js +0 -6
- package/src/lib/room.js +0 -145
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { SINGLE, COLLECTION, ACTION, COUNTERS } from '../lib/constants.js'
|
|
2
|
+
import { toId, toKey } from '../lib/utils.js'
|
|
3
|
+
import { Identity } from '../identity/index.js'
|
|
4
|
+
|
|
5
|
+
// Built-in collections wired into every cero spec.
|
|
6
|
+
export const BUILTINS = [
|
|
7
|
+
{ name: 'members', verb: 'member' },
|
|
8
|
+
{ name: 'devices', verb: 'device' },
|
|
9
|
+
{ name: 'invites', verb: 'invite' },
|
|
10
|
+
{ name: 'handles', verb: 'handle' }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
export function makeDispatcher(spec, ns, routes) {
|
|
14
|
+
const dispatcher = new spec.dispatch.Router()
|
|
15
|
+
|
|
16
|
+
const countersCol = `@${ns}/${COUNTERS}`
|
|
17
|
+
const member = (view, id) => view.get(`@${ns}/members`, { id })
|
|
18
|
+
const device = (view, id) => view.get(`@${ns}/devices`, { id })
|
|
19
|
+
|
|
20
|
+
async function insert(view, name, col, op) {
|
|
21
|
+
if (name !== COUNTERS) {
|
|
22
|
+
const existing = op.id != null ? await view.get(col, op) : null
|
|
23
|
+
if (existing && existing.index != null) {
|
|
24
|
+
op.index = existing.index
|
|
25
|
+
} else {
|
|
26
|
+
const counter = (await view.get(countersCol, { name })) ?? { name, value: 0 }
|
|
27
|
+
op.index = counter.value + 1
|
|
28
|
+
await view.insert(countersCol, { name, value: op.index })
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
await view.insert(col, op)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const upsert = (name, col, kind) => async (op, ctx) => {
|
|
35
|
+
const d = ctx.key && (await device(ctx.view, toId(ctx.key)))
|
|
36
|
+
op.memberId = d?.memberId || null
|
|
37
|
+
if (kind === COLLECTION) await insert(ctx.view, name, col, op)
|
|
38
|
+
else await ctx.view.insert(col, op)
|
|
39
|
+
}
|
|
40
|
+
const remove = (col) => async (op, ctx) => ctx.view.delete(col, op)
|
|
41
|
+
const add = (verb, fn) => dispatcher.add(`@${ns}/${verb}`, fn)
|
|
42
|
+
|
|
43
|
+
add('add-writer', async (op, ctx) => {
|
|
44
|
+
if (!Identity.verify(op.master, op.writer, op.sig)) return
|
|
45
|
+
await ctx.host.addWriter(op.writer, { isIndexer: op.isIndexer !== false })
|
|
46
|
+
const ts = Date.now()
|
|
47
|
+
await insert(ctx.view, 'devices', `@${ns}/devices`, {
|
|
48
|
+
id: toId(op.writer),
|
|
49
|
+
memberId: toId(op.master),
|
|
50
|
+
name: op.name || null,
|
|
51
|
+
isMobile: op.isMobile === true,
|
|
52
|
+
createdAt: ts,
|
|
53
|
+
updatedAt: ts
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
add('del-writer', async (op, ctx) => {
|
|
58
|
+
if (!Identity.verify(op.master, op.writer, op.sig)) return
|
|
59
|
+
await ctx.host.removeWriter(op.writer)
|
|
60
|
+
await ctx.view.delete(`@${ns}/devices`, { id: toId(op.writer) })
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
add('claim-writer', async (op, ctx) => {
|
|
64
|
+
if (!Identity.verify(op.identity, op.writer, op.sig)) return
|
|
65
|
+
const memberId = toId(op.identity)
|
|
66
|
+
if (!(await member(ctx.view, memberId))) return
|
|
67
|
+
await ctx.host.addWriter(op.writer, { isIndexer: true })
|
|
68
|
+
const ts = Date.now()
|
|
69
|
+
await insert(ctx.view, 'devices', `@${ns}/devices`, {
|
|
70
|
+
id: toId(op.writer),
|
|
71
|
+
memberId,
|
|
72
|
+
name: op.name || null,
|
|
73
|
+
isMobile: op.isMobile === true,
|
|
74
|
+
createdAt: ts,
|
|
75
|
+
updatedAt: ts
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
add('add-member', async (op, ctx) => {
|
|
80
|
+
await insert(ctx.view, 'members', `@${ns}/members`, op)
|
|
81
|
+
const deviceId = toId(op.key)
|
|
82
|
+
const existingDevice = await device(ctx.view, deviceId)
|
|
83
|
+
const ts = Date.now()
|
|
84
|
+
await insert(ctx.view, 'devices', `@${ns}/devices`, {
|
|
85
|
+
id: deviceId,
|
|
86
|
+
memberId: op.id,
|
|
87
|
+
name: existingDevice?.name ?? null,
|
|
88
|
+
isMobile: existingDevice?.isMobile ?? false,
|
|
89
|
+
createdAt: existingDevice?.createdAt ?? ts,
|
|
90
|
+
updatedAt: ts
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
add('set-member', async (op, ctx) => {
|
|
95
|
+
const existing = await member(ctx.view, op.id)
|
|
96
|
+
if (!existing) return
|
|
97
|
+
await insert(ctx.view, 'members', `@${ns}/members`, { ...existing, ...op })
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
add('del-member', async (op, ctx) => {
|
|
101
|
+
const existing = await member(ctx.view, op.id)
|
|
102
|
+
if (!existing) return
|
|
103
|
+
if (existing.key) await ctx.host.removeWriter(existing.key)
|
|
104
|
+
await ctx.view.delete(`@${ns}/members`, op)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
add('del-device', async (op, ctx) => {
|
|
108
|
+
const existing = await device(ctx.view, op.id)
|
|
109
|
+
if (!existing) return
|
|
110
|
+
await ctx.host.removeWriter(toKey(existing.id))
|
|
111
|
+
await ctx.view.delete(`@${ns}/devices`, op)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
for (const b of BUILTINS) {
|
|
115
|
+
const col = `@${ns}/${b.name}`
|
|
116
|
+
if (b.verb === 'member') continue
|
|
117
|
+
if (b.verb === 'device') {
|
|
118
|
+
add(`add-${b.verb}`, async (op, ctx) => {
|
|
119
|
+
await insert(ctx.view, b.name, col, { ...op, memberId: op.memberId ?? null })
|
|
120
|
+
})
|
|
121
|
+
add(`set-${b.verb}`, async (op, ctx) => {
|
|
122
|
+
const existing = await device(ctx.view, op.id)
|
|
123
|
+
await insert(ctx.view, b.name, col, { ...op, memberId: existing?.memberId ?? null })
|
|
124
|
+
})
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
add(`add-${b.verb}`, upsert(b.name, col, COLLECTION))
|
|
128
|
+
add(`set-${b.verb}`, upsert(b.name, col, COLLECTION))
|
|
129
|
+
add(`del-${b.verb}`, remove(col))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const [name, info] of Object.entries(spec.meta?.refs || {})) {
|
|
133
|
+
if (info.builtin) continue
|
|
134
|
+
if (info.kind === 'handle') continue
|
|
135
|
+
if (info.kind === ACTION) {
|
|
136
|
+
add(name, routes[name] || (async () => {}))
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
const col = `@${ns}/${name}`
|
|
140
|
+
const kind = info.kind === SINGLE ? SINGLE : COLLECTION
|
|
141
|
+
add(`set-${name}`, upsert(name, col, kind))
|
|
142
|
+
if (info.kind === SINGLE) continue
|
|
143
|
+
add(`add-${name}`, upsert(name, col, COLLECTION))
|
|
144
|
+
add(`del-${name}`, remove(col))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
dispatcher,
|
|
149
|
+
async apply(nodes, view, host) {
|
|
150
|
+
for (const node of nodes) {
|
|
151
|
+
await dispatcher.dispatch(node.value, { view, host, key: node.key })
|
|
152
|
+
}
|
|
153
|
+
await view.flush()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|