@cero-base/cero 0.4.0
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 +287 -0
- package/package.json +79 -0
- package/src/CLAUDE.md +3 -0
- package/src/builder.js +276 -0
- package/src/handle/CLAUDE.md +3 -0
- package/src/handle/index.js +600 -0
- package/src/index.js +170 -0
- package/src/lib/CLAUDE.md +3 -0
- package/src/lib/builtins.js +410 -0
- package/src/lib/operators.js +128 -0
- package/src/lib/peek.js +36 -0
- package/src/lib/spec.js +6 -0
- package/src/lib/utils.js +41 -0
- package/src/local/index.js +49 -0
- package/src/rpc/CLAUDE.md +3 -0
- package/src/rpc/client.js +337 -0
- package/src/rpc/index.js +8 -0
- package/src/rpc/server.js +290 -0
- package/types/builder.d.ts +32 -0
- package/types/handle/index.d.ts +353 -0
- package/types/index.d.ts +102 -0
- package/types/lib/builtins.d.ts +141 -0
- package/types/lib/operators.d.ts +29 -0
- package/types/lib/peek.d.ts +10 -0
- package/types/lib/spec.d.ts +1 -0
- package/types/lib/utils.d.ts +44 -0
- package/types/local/index.d.ts +33 -0
- package/types/rpc/client.d.ts +125 -0
- package/types/rpc/index.d.ts +2 -0
- package/types/rpc/server.d.ts +154 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import HypercoreStorage from 'hypercore-storage'
|
|
2
|
+
import Corestore from 'corestore'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
|
|
5
|
+
import { Identity } from '@cero-base/core/identity'
|
|
6
|
+
import { Network } from '@cero-base/core/network'
|
|
7
|
+
import { CeroError } from '@cero-base/core/errors'
|
|
8
|
+
|
|
9
|
+
import { Handle, Ref } from './handle/index.js'
|
|
10
|
+
import { Local } from './local/index.js'
|
|
11
|
+
import { put, set, get, del, count, watch, call, open } from './lib/operators.js'
|
|
12
|
+
import { peek } from './lib/peek.js'
|
|
13
|
+
import { t, schema } from './lib/spec.js'
|
|
14
|
+
|
|
15
|
+
export { Handle, Ref, Local }
|
|
16
|
+
export { put, set, get, del, count, watch, call, open } from './lib/operators.js'
|
|
17
|
+
export { peek } from './lib/peek.js'
|
|
18
|
+
export { t, schema } from './lib/spec.js'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {object} CeroOpts
|
|
22
|
+
* @property {Identity} [identity] Pre-resolved identity. If absent, derived from `seed`/`phrase` or generated.
|
|
23
|
+
* @property {Uint8Array} [seed] 16- or 32-byte seed entropy.
|
|
24
|
+
* @property {string} [phrase] BIP-39 mnemonic — alternative to `seed`.
|
|
25
|
+
* @property {12 | 24} [words] Mnemonic length when generating a fresh identity.
|
|
26
|
+
* @property {string | null} [name] Friendly device name persisted on the identity claim.
|
|
27
|
+
* @property {boolean} [isMobile] Marks this device as mobile.
|
|
28
|
+
* @property {Array<{ host: string, port: number }>} [bootstrap] Custom DHT bootstrap nodes.
|
|
29
|
+
* @property {Uint8Array} [key] Pre-existing database key (skip bootstrap).
|
|
30
|
+
* @property {Uint8Array} [encryptionKey] Pre-existing encryption key.
|
|
31
|
+
* @property {Record<string, Function>} [routes] Custom RPC routes for the database dispatcher.
|
|
32
|
+
* @property {(err: any) => void} [onerror] Background-task error handler.
|
|
33
|
+
* @property {boolean} [recovery] Recovery flow — wipe local state and re-claim a writer slot.
|
|
34
|
+
* @property {number} [recoveryTimeout] Max wait for writer capability during recovery.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Open (or create) a cero handle at `dir`. Sets up storage, network and
|
|
39
|
+
* identity, then returns a ready root `Handle` with all schema refs
|
|
40
|
+
* attached as properties.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} dir Data directory.
|
|
43
|
+
* @param {any} spec Built spec — output of `cero/build`.
|
|
44
|
+
* @param {CeroOpts} [opts]
|
|
45
|
+
* @returns {Promise<Handle>}
|
|
46
|
+
*/
|
|
47
|
+
export async function cero(dir, spec, opts = {}) {
|
|
48
|
+
if (typeof dir !== 'string' || !dir) throw CeroError.INVALID('dir must be a non-empty string')
|
|
49
|
+
if (!spec) throw CeroError.REQUIRED('spec')
|
|
50
|
+
|
|
51
|
+
const storage = new HypercoreStorage(`${dir}/main`)
|
|
52
|
+
await storage.ready()
|
|
53
|
+
const store = new Corestore(storage, { manifestVersion: 2 })
|
|
54
|
+
await store.ready()
|
|
55
|
+
|
|
56
|
+
let local = null
|
|
57
|
+
if (spec.local && spec.meta?.local) {
|
|
58
|
+
local = new Local(null, spec, { store })
|
|
59
|
+
await local.ready()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const identity = await resolveIdentity(opts, local)
|
|
63
|
+
const writer = local ? (await local.store.get('keypair')).data : null
|
|
64
|
+
|
|
65
|
+
const network = new Network({ bootstrap: opts.bootstrap })
|
|
66
|
+
await network.ready()
|
|
67
|
+
const discovery = network.join(identity.topic)
|
|
68
|
+
await Promise.race([discovery.flush(), new Promise((r) => setTimeout(r, 500))])
|
|
69
|
+
|
|
70
|
+
const me = new Handle({
|
|
71
|
+
storage,
|
|
72
|
+
store,
|
|
73
|
+
local,
|
|
74
|
+
discovery,
|
|
75
|
+
identity,
|
|
76
|
+
network,
|
|
77
|
+
spec,
|
|
78
|
+
opts,
|
|
79
|
+
dir,
|
|
80
|
+
routes: opts.routes,
|
|
81
|
+
key: opts.key,
|
|
82
|
+
encryptionKey: opts.encryptionKey,
|
|
83
|
+
keyPair: writer ? { publicKey: writer.publicKey, secretKey: writer.secretKey } : undefined,
|
|
84
|
+
pair: false
|
|
85
|
+
})
|
|
86
|
+
await me.ready()
|
|
87
|
+
|
|
88
|
+
if (!writer && me.store.length === 0 && (!opts.key || opts.recovery)) {
|
|
89
|
+
const result = await me.bootstrap({
|
|
90
|
+
name: opts.name || null,
|
|
91
|
+
isMobile: opts.isMobile === true,
|
|
92
|
+
recovering: opts.recovery === true
|
|
93
|
+
})
|
|
94
|
+
if (local && result?.writer) {
|
|
95
|
+
await local.store.set('keypair', {
|
|
96
|
+
publicKey: result.writer.publicKey,
|
|
97
|
+
secretKey: result.writer.secretKey
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
if (!opts.recovery) {
|
|
101
|
+
const ts = Date.now()
|
|
102
|
+
await me.store.call('add-member', {
|
|
103
|
+
id: identity.id,
|
|
104
|
+
key: me.store.writerKey,
|
|
105
|
+
role: 'owner',
|
|
106
|
+
name: opts.name || null,
|
|
107
|
+
createdAt: ts,
|
|
108
|
+
updatedAt: ts
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (opts.recovery)
|
|
113
|
+
await me.recover({
|
|
114
|
+
timeout: opts.recoveryTimeout,
|
|
115
|
+
name: opts.name || null,
|
|
116
|
+
isMobile: opts.isMobile === true
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
return me
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Restore a cero instance from a mnemonic phrase. Closes the running
|
|
124
|
+
* instance, wipes the on-disk `main/` tree and re-opens with `recovery: true`
|
|
125
|
+
* so the writer slot is re-claimed.
|
|
126
|
+
*
|
|
127
|
+
* @param {Handle} me Existing root handle to restore.
|
|
128
|
+
* @param {string} phrase BIP-39 mnemonic phrase.
|
|
129
|
+
* @returns {Promise<Handle>} Freshly restored root handle.
|
|
130
|
+
*/
|
|
131
|
+
export async function restore(me, phrase) {
|
|
132
|
+
if (!me?._dir) throw CeroError.INVALID('me must be a cero instance')
|
|
133
|
+
if (!phrase || typeof phrase !== 'string') throw CeroError.INVALID('phrase must be a string')
|
|
134
|
+
|
|
135
|
+
const { _dir: dir, spec, _opts: opts } = me
|
|
136
|
+
const { name, bootstrap, isMobile, onerror, routes, recoveryTimeout } = opts
|
|
137
|
+
|
|
138
|
+
await me.close()
|
|
139
|
+
await fs.promises.rm(`${dir}/main`, { recursive: true, force: true })
|
|
140
|
+
|
|
141
|
+
return cero(dir, spec, {
|
|
142
|
+
name,
|
|
143
|
+
bootstrap,
|
|
144
|
+
isMobile,
|
|
145
|
+
onerror,
|
|
146
|
+
routes,
|
|
147
|
+
recoveryTimeout,
|
|
148
|
+
phrase,
|
|
149
|
+
recovery: true
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Object.assign(cero, { put, set, get, del, count, watch, call, open, peek, restore, t, schema })
|
|
154
|
+
|
|
155
|
+
async function resolveIdentity(opts, local) {
|
|
156
|
+
if (opts.identity) return opts.identity
|
|
157
|
+
|
|
158
|
+
const provided = opts.seed || (opts.phrase && Identity.toSeed(opts.phrase))
|
|
159
|
+
if (provided) {
|
|
160
|
+
if (local) await local.store.set('master', { seed: provided })
|
|
161
|
+
return Identity.fromSeed(provided)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const stored = local && (await local.store.get('master')).data?.seed
|
|
165
|
+
if (stored) return Identity.fromSeed(stored)
|
|
166
|
+
|
|
167
|
+
const fresh = await Identity.generate({ words: opts.words })
|
|
168
|
+
if (local) await local.store.set('master', { seed: fresh.seed })
|
|
169
|
+
return fresh
|
|
170
|
+
}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builtin type/collection/dispatch/rpc definitions used by the builder.
|
|
3
|
+
* Every cero schema includes these; user-defined refs are added on top.
|
|
4
|
+
*
|
|
5
|
+
* @typedef {{ name: string, type: string, verb?: string, kind?: string }} BuiltinRef
|
|
6
|
+
* @typedef {{ name: string, type: string, required: boolean }} FieldDesc
|
|
7
|
+
* @typedef {{ name: string, compact: boolean, fields: FieldDesc[] }} TypeDesc
|
|
8
|
+
* @typedef {{ name: string, schema: string, key: string[] }} CollectionDesc
|
|
9
|
+
* @typedef {{ name: string, requestType: string }} DispatchDesc
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* name: string,
|
|
12
|
+
* request: { name: string },
|
|
13
|
+
* response: { name: string, stream?: boolean }
|
|
14
|
+
* }} CommandDesc
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Refs that exist on every main (Handle) schema.
|
|
19
|
+
*
|
|
20
|
+
* @type {BuiltinRef[]}
|
|
21
|
+
*/
|
|
22
|
+
export const BUILTINS = [
|
|
23
|
+
{ name: 'members', type: 'member', verb: 'member' },
|
|
24
|
+
{ name: 'devices', type: 'device', verb: 'device' },
|
|
25
|
+
{ name: 'invites', type: 'invite', verb: 'invite' },
|
|
26
|
+
{ name: 'handles', type: 'handle', verb: 'handle' }
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Refs that exist on every local schema.
|
|
31
|
+
*
|
|
32
|
+
* @type {BuiltinRef[]}
|
|
33
|
+
*/
|
|
34
|
+
export const LOCAL_BUILTINS = [
|
|
35
|
+
{ name: 'master', type: 'master', kind: 'single' },
|
|
36
|
+
{ name: 'keypair', type: 'keypair', kind: 'single' },
|
|
37
|
+
{ name: 'handle-keypairs', type: 'handle-keypair', kind: 'collection' }
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
const HYPERDB_TYPE = {
|
|
41
|
+
string: 'string',
|
|
42
|
+
uint: 'uint',
|
|
43
|
+
int: 'int',
|
|
44
|
+
bool: 'bool',
|
|
45
|
+
bytes: 'buffer',
|
|
46
|
+
json: 'json',
|
|
47
|
+
fixed32: 'fixed32',
|
|
48
|
+
fixed64: 'fixed64'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Map a schema-DSL primitive name to its HyperDB type name.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} prim
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
export function hyperdbType(prim) {
|
|
58
|
+
return HYPERDB_TYPE[prim] || 'string'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Type descriptors registered into every main schema.
|
|
63
|
+
*
|
|
64
|
+
* @returns {TypeDesc[]}
|
|
65
|
+
*/
|
|
66
|
+
export function builtinTypes() {
|
|
67
|
+
return [
|
|
68
|
+
{ name: 'del-by-id', compact: false, fields: [{ name: 'id', type: 'string', required: true }] },
|
|
69
|
+
{
|
|
70
|
+
name: 'writer',
|
|
71
|
+
compact: false,
|
|
72
|
+
fields: [
|
|
73
|
+
{ name: 'master', type: 'buffer', required: true },
|
|
74
|
+
{ name: 'writer', type: 'buffer', required: true },
|
|
75
|
+
{ name: 'sig', type: 'buffer', required: true },
|
|
76
|
+
{ name: 'name', type: 'string', required: false },
|
|
77
|
+
{ name: 'isMobile', type: 'bool', required: false },
|
|
78
|
+
{ name: 'isIndexer', type: 'bool', required: false }
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'counter',
|
|
83
|
+
compact: false,
|
|
84
|
+
fields: [
|
|
85
|
+
{ name: 'name', type: 'string', required: true },
|
|
86
|
+
{ name: 'value', type: 'uint', required: true }
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'member',
|
|
91
|
+
compact: false,
|
|
92
|
+
fields: [
|
|
93
|
+
{ name: 'id', type: 'string', required: true },
|
|
94
|
+
{ name: 'key', type: 'buffer', required: true },
|
|
95
|
+
{ name: 'role', type: 'string', required: true },
|
|
96
|
+
{ name: 'name', type: 'string', required: false },
|
|
97
|
+
{ name: 'createdAt', type: 'int', required: false },
|
|
98
|
+
{ name: 'updatedAt', type: 'int', required: false },
|
|
99
|
+
{ name: 'sig', type: 'buffer', required: false },
|
|
100
|
+
{ name: 'index', type: 'uint', required: false }
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'device',
|
|
105
|
+
compact: false,
|
|
106
|
+
fields: [
|
|
107
|
+
{ name: 'id', type: 'string', required: true },
|
|
108
|
+
{ name: 'memberId', type: 'string', required: false },
|
|
109
|
+
{ name: 'name', type: 'string', required: false },
|
|
110
|
+
{ name: 'isMobile', type: 'bool', required: false },
|
|
111
|
+
{ name: 'createdAt', type: 'int', required: false },
|
|
112
|
+
{ name: 'updatedAt', type: 'int', required: false },
|
|
113
|
+
{ name: 'index', type: 'uint', required: false }
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'invite',
|
|
118
|
+
compact: false,
|
|
119
|
+
fields: [
|
|
120
|
+
{ name: 'id', type: 'string', required: true },
|
|
121
|
+
{ name: 'invite', type: 'buffer', required: true },
|
|
122
|
+
{ name: 'publicKey', type: 'buffer', required: true },
|
|
123
|
+
{ name: 'data', type: 'buffer', required: false },
|
|
124
|
+
{ name: 'sig', type: 'buffer', required: false },
|
|
125
|
+
{ name: 'role', type: 'string', required: true },
|
|
126
|
+
{ name: 'expires', type: 'int', required: false },
|
|
127
|
+
{ name: 'createdAt', type: 'int', required: false },
|
|
128
|
+
{ name: 'index', type: 'uint', required: false }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'handle',
|
|
133
|
+
compact: false,
|
|
134
|
+
fields: [
|
|
135
|
+
{ name: 'id', type: 'string', required: true },
|
|
136
|
+
{ name: 'type', type: 'string', required: true },
|
|
137
|
+
{ name: 'key', type: 'buffer', required: true },
|
|
138
|
+
{ name: 'encryptionKey', type: 'buffer', required: false },
|
|
139
|
+
{ name: 'name', type: 'string', required: false },
|
|
140
|
+
{ name: 'createdAt', type: 'int', required: false },
|
|
141
|
+
{ name: 'updatedAt', type: 'int', required: false },
|
|
142
|
+
{ name: 'index', type: 'uint', required: false }
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'claim',
|
|
147
|
+
compact: false,
|
|
148
|
+
fields: [
|
|
149
|
+
{ name: 'identity', type: 'buffer', required: true },
|
|
150
|
+
{ name: 'writer', type: 'buffer', required: true },
|
|
151
|
+
{ name: 'sig', type: 'buffer', required: true },
|
|
152
|
+
{ name: 'name', type: 'string', required: false },
|
|
153
|
+
{ name: 'isMobile', type: 'bool', required: false }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Collection name for the internal counters table. */
|
|
160
|
+
export const COUNTERS = 'counters'
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Collection descriptors for every main schema, namespaced under `ns`.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} ns
|
|
166
|
+
* @returns {CollectionDesc[]}
|
|
167
|
+
*/
|
|
168
|
+
export function builtinCollections(ns) {
|
|
169
|
+
const out = BUILTINS.map((b) => ({ name: b.name, schema: `@${ns}/${b.type}`, key: ['id'] }))
|
|
170
|
+
out.push({ name: COUNTERS, schema: `@${ns}/counter`, key: ['name'] })
|
|
171
|
+
return out
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Dispatch (mutation) descriptors for every main schema, namespaced under `ns`.
|
|
176
|
+
*
|
|
177
|
+
* @param {string} ns
|
|
178
|
+
* @returns {DispatchDesc[]}
|
|
179
|
+
*/
|
|
180
|
+
export function builtinDispatches(ns) {
|
|
181
|
+
const at = (n) => `@${ns}/${n}`
|
|
182
|
+
const out = [
|
|
183
|
+
{ name: 'add-writer', requestType: at('writer') },
|
|
184
|
+
{ name: 'del-writer', requestType: at('writer') },
|
|
185
|
+
{ name: 'claim-writer', requestType: at('claim') }
|
|
186
|
+
]
|
|
187
|
+
for (const b of BUILTINS) {
|
|
188
|
+
out.push({ name: `add-${b.verb}`, requestType: at(b.type) })
|
|
189
|
+
out.push({ name: `set-${b.verb}`, requestType: at(b.type) })
|
|
190
|
+
out.push({ name: `del-${b.verb}`, requestType: at('del-by-id') })
|
|
191
|
+
}
|
|
192
|
+
return out
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Type descriptors registered into every local schema.
|
|
197
|
+
*
|
|
198
|
+
* @returns {TypeDesc[]}
|
|
199
|
+
*/
|
|
200
|
+
export function localBuiltinTypes() {
|
|
201
|
+
return [
|
|
202
|
+
{ name: 'master', compact: false, fields: [{ name: 'seed', type: 'buffer', required: true }] },
|
|
203
|
+
{
|
|
204
|
+
name: 'keypair',
|
|
205
|
+
compact: false,
|
|
206
|
+
fields: [
|
|
207
|
+
{ name: 'publicKey', type: 'buffer', required: true },
|
|
208
|
+
{ name: 'secretKey', type: 'buffer', required: true }
|
|
209
|
+
]
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'handle-keypair',
|
|
213
|
+
compact: false,
|
|
214
|
+
fields: [
|
|
215
|
+
{ name: 'id', type: 'string', required: true },
|
|
216
|
+
{ name: 'publicKey', type: 'buffer', required: true },
|
|
217
|
+
{ name: 'secretKey', type: 'buffer', required: true },
|
|
218
|
+
{ name: 'encryptionKey', type: 'buffer', required: false }
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Collection descriptors for every local schema, namespaced under `ns`.
|
|
226
|
+
*
|
|
227
|
+
* @param {string} ns
|
|
228
|
+
* @returns {CollectionDesc[]}
|
|
229
|
+
*/
|
|
230
|
+
export function localBuiltinCollections(ns) {
|
|
231
|
+
return [
|
|
232
|
+
{ name: 'master', schema: `@${ns}/master`, key: [] },
|
|
233
|
+
{ name: 'keypair', schema: `@${ns}/keypair`, key: [] },
|
|
234
|
+
{ name: 'handle-keypairs', schema: `@${ns}/handle-keypair`, key: ['id'] }
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Request/response type descriptors for the RPC facade.
|
|
240
|
+
*
|
|
241
|
+
* @returns {TypeDesc[]}
|
|
242
|
+
*/
|
|
243
|
+
export function rpcTypes() {
|
|
244
|
+
return [
|
|
245
|
+
{ name: 'req-empty', compact: false, fields: [{ name: 'ok', type: 'bool', required: false }] },
|
|
246
|
+
{
|
|
247
|
+
name: 'req-restore',
|
|
248
|
+
compact: false,
|
|
249
|
+
fields: [{ name: 'phrase', type: 'string', required: true }]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'req-row',
|
|
253
|
+
compact: false,
|
|
254
|
+
fields: [
|
|
255
|
+
{ name: 'handle', type: 'string', required: true },
|
|
256
|
+
{ name: 'ref', type: 'string', required: true },
|
|
257
|
+
{ name: 'data', type: 'buffer', required: true }
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'req-id',
|
|
262
|
+
compact: false,
|
|
263
|
+
fields: [
|
|
264
|
+
{ name: 'handle', type: 'string', required: true },
|
|
265
|
+
{ name: 'ref', type: 'string', required: true },
|
|
266
|
+
{ name: 'id', type: 'string', required: true }
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'req-query',
|
|
271
|
+
compact: false,
|
|
272
|
+
fields: [
|
|
273
|
+
{ name: 'handle', type: 'string', required: true },
|
|
274
|
+
{ name: 'ref', type: 'string', required: true },
|
|
275
|
+
{ name: 'query', type: 'buffer', required: false }
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'req-call',
|
|
280
|
+
compact: false,
|
|
281
|
+
fields: [
|
|
282
|
+
{ name: 'handle', type: 'string', required: true },
|
|
283
|
+
{ name: 'op', type: 'string', required: true },
|
|
284
|
+
{ name: 'data', type: 'buffer', required: false }
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: 'req-invite',
|
|
289
|
+
compact: false,
|
|
290
|
+
fields: [
|
|
291
|
+
{ name: 'handle', type: 'string', required: true },
|
|
292
|
+
{ name: 'role', type: 'string', required: false }
|
|
293
|
+
]
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'req-revoke',
|
|
297
|
+
compact: false,
|
|
298
|
+
fields: [
|
|
299
|
+
{ name: 'handle', type: 'string', required: true },
|
|
300
|
+
{ name: 'invite', type: 'string', required: true }
|
|
301
|
+
]
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: 'req-join',
|
|
305
|
+
compact: false,
|
|
306
|
+
fields: [
|
|
307
|
+
{ name: 'parent', type: 'string', required: true },
|
|
308
|
+
{ name: 'ref', type: 'string', required: true },
|
|
309
|
+
{ name: 'invite', type: 'string', required: true }
|
|
310
|
+
]
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'req-open',
|
|
314
|
+
compact: false,
|
|
315
|
+
fields: [
|
|
316
|
+
{ name: 'parent', type: 'string', required: true },
|
|
317
|
+
{ name: 'row', type: 'string', required: true }
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: 'req-handle',
|
|
322
|
+
compact: false,
|
|
323
|
+
fields: [{ name: 'handle', type: 'string', required: true }]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'res-data',
|
|
327
|
+
compact: false,
|
|
328
|
+
fields: [{ name: 'data', type: 'buffer', required: false }]
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: 'res-rows',
|
|
332
|
+
compact: false,
|
|
333
|
+
fields: [
|
|
334
|
+
{ name: 'data', type: 'buffer', required: true },
|
|
335
|
+
{ name: 'total', type: 'int', required: true },
|
|
336
|
+
{ name: 'size', type: 'int', required: true }
|
|
337
|
+
]
|
|
338
|
+
},
|
|
339
|
+
{ name: 'res-count', compact: false, fields: [{ name: 'count', type: 'int', required: true }] },
|
|
340
|
+
{
|
|
341
|
+
name: 'res-invite',
|
|
342
|
+
compact: false,
|
|
343
|
+
fields: [{ name: 'invite', type: 'string', required: true }]
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: 'res-handle',
|
|
347
|
+
compact: false,
|
|
348
|
+
fields: [
|
|
349
|
+
{ name: 'id', type: 'string', required: true },
|
|
350
|
+
{ name: 'type', type: 'string', required: true },
|
|
351
|
+
{ name: 'name', type: 'string', required: false }
|
|
352
|
+
]
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: 'res-identity',
|
|
356
|
+
compact: false,
|
|
357
|
+
fields: [
|
|
358
|
+
{ name: 'id', type: 'string', required: true },
|
|
359
|
+
{ name: 'deviceId', type: 'string', required: false },
|
|
360
|
+
{ name: 'phrase', type: 'string', required: false }
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
{ name: 'res-ok', compact: false, fields: [{ name: 'ok', type: 'bool', required: false }] }
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* RPC command (verb) descriptors for the cero server, namespaced under `ns`.
|
|
369
|
+
*
|
|
370
|
+
* @param {string} ns
|
|
371
|
+
* @returns {CommandDesc[]}
|
|
372
|
+
*/
|
|
373
|
+
export function rpcCommands(ns) {
|
|
374
|
+
const at = (n) => `@${ns}/${n}`
|
|
375
|
+
return [
|
|
376
|
+
{
|
|
377
|
+
name: 'init',
|
|
378
|
+
request: { name: at('req-empty') },
|
|
379
|
+
response: { name: at('res-identity') }
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: 'restore',
|
|
383
|
+
request: { name: at('req-restore') },
|
|
384
|
+
response: { name: at('res-identity') }
|
|
385
|
+
},
|
|
386
|
+
{ name: 'add-row', request: { name: at('req-row') }, response: { name: at('res-data') } },
|
|
387
|
+
{ name: 'add-handle', request: { name: at('req-row') }, response: { name: at('res-handle') } },
|
|
388
|
+
{ name: 'set', request: { name: at('req-row') }, response: { name: at('res-data') } },
|
|
389
|
+
{ name: 'get', request: { name: at('req-query') }, response: { name: at('res-rows') } },
|
|
390
|
+
{ name: 'get-one', request: { name: at('req-id') }, response: { name: at('res-data') } },
|
|
391
|
+
{ name: 'del', request: { name: at('req-id') }, response: { name: at('res-ok') } },
|
|
392
|
+
{ name: 'count', request: { name: at('req-query') }, response: { name: at('res-count') } },
|
|
393
|
+
{
|
|
394
|
+
name: 'watch',
|
|
395
|
+
request: { name: at('req-query') },
|
|
396
|
+
response: { name: at('res-rows'), stream: true }
|
|
397
|
+
},
|
|
398
|
+
{ name: 'call', request: { name: at('req-call') }, response: { name: at('res-data') } },
|
|
399
|
+
{ name: 'invite', request: { name: at('req-invite') }, response: { name: at('res-invite') } },
|
|
400
|
+
{ name: 'revoke', request: { name: at('req-revoke') }, response: { name: at('res-ok') } },
|
|
401
|
+
{ name: 'join', request: { name: at('req-join') }, response: { name: at('res-handle') } },
|
|
402
|
+
{
|
|
403
|
+
name: 'open-handle',
|
|
404
|
+
request: { name: at('req-open') },
|
|
405
|
+
response: { name: at('res-handle') }
|
|
406
|
+
},
|
|
407
|
+
{ name: 'close-handle', request: { name: at('req-handle') }, response: { name: at('res-ok') } },
|
|
408
|
+
{ name: 'leave', request: { name: at('req-handle') }, response: { name: at('res-ok') } }
|
|
409
|
+
]
|
|
410
|
+
}
|