@cero-base/cero 0.5.2 → 0.6.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/package.json +4 -4
- package/src/build/builtins.js +137 -0
- package/src/{builder.js → build/index.js} +35 -34
- package/src/build/schemas.js +162 -0
- package/src/handle/index.js +11 -15
- package/src/index.js +3 -7
- package/src/lib/constants.js +20 -0
- package/types/build/builtins.d.ts +88 -0
- package/types/build/schemas.d.ts +153 -0
- package/types/handle/index.d.ts +1 -5
- package/types/lib/constants.d.ts +14 -0
- package/src/lib/builtins.js +0 -413
- package/types/lib/builtins.d.ts +0 -141
- /package/types/{builder.d.ts → build/index.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cero-base/cero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "The ideal p2p API — everything is a handle, handles contain refs, refs contain rows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"default": "./src/index.js"
|
|
21
21
|
},
|
|
22
22
|
"./build": {
|
|
23
|
-
"types": "./types/
|
|
24
|
-
"default": "./src/
|
|
23
|
+
"types": "./types/build/index.d.ts",
|
|
24
|
+
"default": "./src/build/index.js"
|
|
25
25
|
},
|
|
26
26
|
"./server": {
|
|
27
27
|
"types": "./types/rpc/server.d.ts",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"test": "ls test/*.test.js | xargs -P2 -n1 brittle-node"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@cero-base/core": "^0.
|
|
70
|
+
"@cero-base/core": "^0.6.0",
|
|
71
71
|
"b4a": "^1.8.1",
|
|
72
72
|
"bare-crypto": "^1.13.7",
|
|
73
73
|
"bare-fs": "^4.7.1",
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Build wiring for the builtin schemas (schemas.js): turns the grouped field
|
|
2
|
+
// maps into the type / collection / dispatch / command descriptors the builder
|
|
3
|
+
// registers. Every cero schema includes these; app refs are added on top.
|
|
4
|
+
import { CeroError } from '@cero-base/core/errors'
|
|
5
|
+
import { COUNTERS, DB_TYPE } from '../lib/constants.js'
|
|
6
|
+
import * as schemas from './schemas.js'
|
|
7
|
+
|
|
8
|
+
// Builtin collections by scope — ref name → { type, kind? }. kind defaults to
|
|
9
|
+
// 'collection' (id-keyed); 'single' has no key. Dispatches are derived for
|
|
10
|
+
// main; counters is internal (a collection with no add/set/del).
|
|
11
|
+
export const refs = {
|
|
12
|
+
main: {
|
|
13
|
+
members: { type: 'member' },
|
|
14
|
+
devices: { type: 'device' },
|
|
15
|
+
invites: { type: 'invite' },
|
|
16
|
+
handles: { type: 'handle' }
|
|
17
|
+
},
|
|
18
|
+
local: {
|
|
19
|
+
master: { type: 'master', kind: 'single' },
|
|
20
|
+
keypair: { type: 'keypair', kind: 'single' },
|
|
21
|
+
'handle-keypairs': { type: 'handle-keypair' }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const hyperdbType = (prim) => DB_TYPE[prim] || 'string'
|
|
26
|
+
|
|
27
|
+
const at = (ns, n) => `@${ns}/${n}`
|
|
28
|
+
const keyOf = (def) => (def.kind === 'single' ? [] : ['id'])
|
|
29
|
+
|
|
30
|
+
const fields = (map) =>
|
|
31
|
+
Object.entries(map).map(([name, m]) => ({
|
|
32
|
+
name,
|
|
33
|
+
type: hyperdbType(m.prim),
|
|
34
|
+
required: m.required === true
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
// Merge app `t.extend` fields into a builtin's base fields — base can't be redeclared.
|
|
38
|
+
const merge = (type, base, extra) => {
|
|
39
|
+
if (!extra) return base
|
|
40
|
+
for (const k in extra)
|
|
41
|
+
if (k in base)
|
|
42
|
+
throw CeroError.INVALID(`'${k}' is a base field of '${type}' and cannot be redeclared`)
|
|
43
|
+
return { ...base, ...extra }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const descriptors = (group, extend = {}) =>
|
|
47
|
+
Object.entries(group).map(([name, base]) => ({
|
|
48
|
+
name,
|
|
49
|
+
compact: false,
|
|
50
|
+
fields: fields(merge(name, base, extend[name]))
|
|
51
|
+
}))
|
|
52
|
+
|
|
53
|
+
// meta.refs entries for a scope's builtins.
|
|
54
|
+
export const builtinRefs = (ns, scope) =>
|
|
55
|
+
Object.fromEntries(
|
|
56
|
+
Object.entries(refs[scope]).map(([name, def]) => [
|
|
57
|
+
name,
|
|
58
|
+
{
|
|
59
|
+
kind: def.kind || 'collection',
|
|
60
|
+
path: [name],
|
|
61
|
+
builtin: true,
|
|
62
|
+
...(scope === 'main' && { verb: def.type }),
|
|
63
|
+
schema: at(ns, def.type)
|
|
64
|
+
}
|
|
65
|
+
])
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// hyperschema type descriptors. scope is 'main' | 'local' | 'rpc'; `extend`
|
|
69
|
+
// merges app `t.extend` fields into the matching type.
|
|
70
|
+
export const builtinTypes = (scope, extend) => descriptors(schemas[scope], extend)
|
|
71
|
+
export const rpcTypes = () => descriptors(schemas.rpc)
|
|
72
|
+
|
|
73
|
+
// hyperdb collection descriptors for a scope.
|
|
74
|
+
export const builtinCollections = (ns, scope) => {
|
|
75
|
+
const out = Object.entries(refs[scope]).map(([name, def]) => ({
|
|
76
|
+
name,
|
|
77
|
+
schema: at(ns, def.type),
|
|
78
|
+
key: keyOf(def)
|
|
79
|
+
}))
|
|
80
|
+
if (scope === 'main') out.push({ name: COUNTERS, schema: at(ns, 'counter'), key: ['name'] })
|
|
81
|
+
return out
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// hyperdispatch descriptors (main scope only).
|
|
85
|
+
export const builtinDispatches = (ns) => [
|
|
86
|
+
{ name: 'add-writer', requestType: at(ns, 'writer') },
|
|
87
|
+
{ name: 'del-writer', requestType: at(ns, 'writer') },
|
|
88
|
+
{ name: 'claim-writer', requestType: at(ns, 'claim') },
|
|
89
|
+
...Object.values(refs.main).flatMap(({ type }) => [
|
|
90
|
+
{ name: `add-${type}`, requestType: at(ns, type) },
|
|
91
|
+
{ name: `set-${type}`, requestType: at(ns, type) },
|
|
92
|
+
{ name: `del-${type}`, requestType: at(ns, 'del-by-id') }
|
|
93
|
+
])
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
export const rpcCommands = (ns) => {
|
|
97
|
+
const ref = (n) => at(ns, n)
|
|
98
|
+
return [
|
|
99
|
+
{ name: 'init', request: { name: ref('req-empty') }, response: { name: ref('res-identity') } },
|
|
100
|
+
{
|
|
101
|
+
name: 'restore',
|
|
102
|
+
request: { name: ref('req-restore') },
|
|
103
|
+
response: { name: ref('res-identity') }
|
|
104
|
+
},
|
|
105
|
+
{ name: 'add-row', request: { name: ref('req-row') }, response: { name: ref('res-data') } },
|
|
106
|
+
{
|
|
107
|
+
name: 'add-handle',
|
|
108
|
+
request: { name: ref('req-row') },
|
|
109
|
+
response: { name: ref('res-handle') }
|
|
110
|
+
},
|
|
111
|
+
{ name: 'set', request: { name: ref('req-row') }, response: { name: ref('res-data') } },
|
|
112
|
+
{ name: 'get', request: { name: ref('req-query') }, response: { name: ref('res-rows') } },
|
|
113
|
+
{ name: 'get-one', request: { name: ref('req-id') }, response: { name: ref('res-data') } },
|
|
114
|
+
{ name: 'del', request: { name: ref('req-id') }, response: { name: ref('res-ok') } },
|
|
115
|
+
{ name: 'count', request: { name: ref('req-query') }, response: { name: ref('res-count') } },
|
|
116
|
+
{
|
|
117
|
+
name: 'watch',
|
|
118
|
+
request: { name: ref('req-query') },
|
|
119
|
+
response: { name: ref('res-rows'), stream: true }
|
|
120
|
+
},
|
|
121
|
+
{ name: 'call', request: { name: ref('req-call') }, response: { name: ref('res-data') } },
|
|
122
|
+
{ name: 'invite', request: { name: ref('req-invite') }, response: { name: ref('res-invite') } },
|
|
123
|
+
{ name: 'revoke', request: { name: ref('req-revoke') }, response: { name: ref('res-ok') } },
|
|
124
|
+
{ name: 'join', request: { name: ref('req-join') }, response: { name: ref('res-handle') } },
|
|
125
|
+
{
|
|
126
|
+
name: 'open-handle',
|
|
127
|
+
request: { name: ref('req-open') },
|
|
128
|
+
response: { name: ref('res-handle') }
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'close-handle',
|
|
132
|
+
request: { name: ref('req-handle') },
|
|
133
|
+
response: { name: ref('res-ok') }
|
|
134
|
+
},
|
|
135
|
+
{ name: 'leave', request: { name: ref('req-handle') }, response: { name: ref('res-ok') } }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
@@ -8,20 +8,17 @@ import HRPCBuilder from 'hrpc'
|
|
|
8
8
|
|
|
9
9
|
import { CeroError } from '@cero-base/core/errors'
|
|
10
10
|
|
|
11
|
+
import { NS } from '../lib/constants.js'
|
|
11
12
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
hyperdbType,
|
|
13
|
+
refs,
|
|
14
|
+
builtinRefs,
|
|
15
15
|
builtinTypes,
|
|
16
16
|
builtinCollections,
|
|
17
17
|
builtinDispatches,
|
|
18
|
-
localBuiltinTypes,
|
|
19
|
-
localBuiltinCollections,
|
|
20
18
|
rpcTypes,
|
|
21
|
-
rpcCommands
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const NS = 'cero'
|
|
19
|
+
rpcCommands,
|
|
20
|
+
hyperdbType
|
|
21
|
+
} from './builtins.js'
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
24
|
* @typedef {import('@cero-base/core/schema').Schema} Schema
|
|
@@ -45,22 +42,34 @@ const NS = 'cero'
|
|
|
45
42
|
* @returns {Promise<void>}
|
|
46
43
|
*/
|
|
47
44
|
export async function build(specDir, schema, { ns = NS } = {}) {
|
|
48
|
-
const
|
|
49
|
-
if (!
|
|
45
|
+
const raw = /** @type {SchemaDefs & { local?: SchemaDefs }} */ (schema?.defs || schema)
|
|
46
|
+
if (!raw || typeof raw !== 'object') throw CeroError.REQUIRED('schema')
|
|
47
|
+
|
|
48
|
+
// Pull out `t.extend(...)` entries (keyed by builtin ref name) and map them
|
|
49
|
+
// to the builtin's type name, to merge into builtin types across every scope.
|
|
50
|
+
const extend = {}
|
|
51
|
+
const defs = {}
|
|
52
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
53
|
+
if (v && v.kind === 'extend') {
|
|
54
|
+
const type = refs.main[k]?.type
|
|
55
|
+
if (!type) throw CeroError.INVALID(`'${k}' is not an extendable builtin`)
|
|
56
|
+
extend[type] = v.fields
|
|
57
|
+
} else {
|
|
58
|
+
defs[k] = v
|
|
59
|
+
}
|
|
60
|
+
}
|
|
50
61
|
|
|
51
|
-
const main = compile(splitMain(defs), ns,
|
|
52
|
-
const local = defs.local
|
|
53
|
-
? compile(defs.local, ns, LOCAL_BUILTINS)
|
|
54
|
-
: compile({}, ns, LOCAL_BUILTINS)
|
|
62
|
+
const main = compile(splitMain(defs), ns, 'main')
|
|
63
|
+
const local = compile(defs.local || {}, ns, 'local')
|
|
55
64
|
const handles = {}
|
|
56
65
|
for (const [name, child] of Object.entries(splitHandles(defs))) {
|
|
57
|
-
handles[name] = compile(child, ns)
|
|
66
|
+
handles[name] = compile(child, ns, 'main')
|
|
58
67
|
}
|
|
59
68
|
|
|
60
|
-
emitMain(join(specDir, 'main'), ns, main, { rpc: true })
|
|
69
|
+
emitMain(join(specDir, 'main'), ns, main, { rpc: true, extend })
|
|
61
70
|
emitLocal(join(specDir, 'local'), ns, local)
|
|
62
71
|
for (const [name, handle] of Object.entries(handles)) {
|
|
63
|
-
emitMain(join(specDir, 'handles', name), ns, handle, { rpc: false })
|
|
72
|
+
emitMain(join(specDir, 'handles', name), ns, handle, { rpc: false, extend })
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
const meta = {
|
|
@@ -105,18 +114,10 @@ function isPlainHandle(v) {
|
|
|
105
114
|
return v && typeof v === 'object' && !v.kind && !v.prim
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
function compile(root, ns,
|
|
117
|
+
function compile(root, ns, scope = 'main') {
|
|
109
118
|
const ctx = { types: [], collections: [], dispatches: [], meta: { ns, refs: {} }, ns }
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
ctx.meta.refs[b.name] = {
|
|
113
|
-
kind: b.kind || 'collection',
|
|
114
|
-
path: [b.name],
|
|
115
|
-
builtin: true,
|
|
116
|
-
verb: b.verb,
|
|
117
|
-
schema: `@${ns}/${b.type}`
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
+
Object.assign(ctx.meta.refs, builtinRefs(ns, scope))
|
|
120
121
|
|
|
121
122
|
for (const [name, node] of Object.entries(root)) {
|
|
122
123
|
if (node.kind === 'handle') {
|
|
@@ -174,11 +175,11 @@ function fieldsFor(fields) {
|
|
|
174
175
|
return Object.entries(fields).map(([name, marker]) => ({
|
|
175
176
|
name,
|
|
176
177
|
type: hyperdbType(marker.prim),
|
|
177
|
-
required:
|
|
178
|
+
required: marker.required === true
|
|
178
179
|
}))
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
function emitMain(dir, ns, { types, collections, dispatches }, { rpc }) {
|
|
182
|
+
function emitMain(dir, ns, { types, collections, dispatches }, { rpc, extend = {} }) {
|
|
182
183
|
const schemaDir = join(dir, 'schema')
|
|
183
184
|
const dbDir = join(dir, 'db')
|
|
184
185
|
const dispatchDir = join(dir, 'dispatch')
|
|
@@ -186,14 +187,14 @@ function emitMain(dir, ns, { types, collections, dispatches }, { rpc }) {
|
|
|
186
187
|
|
|
187
188
|
const s = Hyperschema.from(schemaDir)
|
|
188
189
|
const sns = s.namespace(ns)
|
|
189
|
-
for (const desc of builtinTypes()) sns.register(desc)
|
|
190
|
+
for (const desc of builtinTypes('main', extend)) sns.register(desc)
|
|
190
191
|
for (const desc of types) sns.register(desc)
|
|
191
192
|
if (rpc) for (const desc of rpcTypes()) sns.register(desc)
|
|
192
193
|
Hyperschema.toDisk(s, schemaDir, { esm: true })
|
|
193
194
|
|
|
194
195
|
const db = HyperdbBuilder.from(schemaDir, dbDir)
|
|
195
196
|
const dns = db.namespace(ns)
|
|
196
|
-
for (const desc of builtinCollections(ns)) dns.collections.register(desc)
|
|
197
|
+
for (const desc of builtinCollections(ns, 'main')) dns.collections.register(desc)
|
|
197
198
|
for (const desc of collections) dns.collections.register(desc)
|
|
198
199
|
HyperdbBuilder.toDisk(db, dbDir, { esm: true })
|
|
199
200
|
|
|
@@ -217,13 +218,13 @@ function emitLocal(dir, ns, { types, collections }) {
|
|
|
217
218
|
|
|
218
219
|
const s = Hyperschema.from(schemaDir)
|
|
219
220
|
const sns = s.namespace(ns)
|
|
220
|
-
for (const desc of
|
|
221
|
+
for (const desc of builtinTypes('local')) sns.register(desc)
|
|
221
222
|
for (const desc of types) sns.register(desc)
|
|
222
223
|
Hyperschema.toDisk(s, schemaDir, { esm: true })
|
|
223
224
|
|
|
224
225
|
const db = HyperdbBuilder.from(schemaDir, dbDir)
|
|
225
226
|
const dns = db.namespace(ns)
|
|
226
|
-
for (const desc of
|
|
227
|
+
for (const desc of builtinCollections(ns, 'local')) dns.collections.register(desc)
|
|
227
228
|
for (const desc of collections) dns.collections.register(desc)
|
|
228
229
|
HyperdbBuilder.toDisk(db, dbDir, { esm: true })
|
|
229
230
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Builtin type schemas grouped by scope, in the schema DSL — kept separate from
|
|
2
|
+
// the build wiring (builtins.js) so apps can extend them with `t.extend`.
|
|
3
|
+
// Field order is the wire order; do not reorder.
|
|
4
|
+
import { t } from '../lib/spec.js'
|
|
5
|
+
|
|
6
|
+
const { string, bytes, int, uint, bool, required } = t
|
|
7
|
+
|
|
8
|
+
export const main = {
|
|
9
|
+
'del-by-id': {
|
|
10
|
+
id: required(string)
|
|
11
|
+
},
|
|
12
|
+
writer: {
|
|
13
|
+
master: required(bytes),
|
|
14
|
+
writer: required(bytes),
|
|
15
|
+
sig: required(bytes),
|
|
16
|
+
isIndexer: bool
|
|
17
|
+
},
|
|
18
|
+
counter: {
|
|
19
|
+
name: required(string),
|
|
20
|
+
value: required(uint)
|
|
21
|
+
},
|
|
22
|
+
member: {
|
|
23
|
+
id: required(string),
|
|
24
|
+
key: required(bytes),
|
|
25
|
+
role: required(string),
|
|
26
|
+
name: string,
|
|
27
|
+
createdAt: int,
|
|
28
|
+
updatedAt: int,
|
|
29
|
+
sig: bytes,
|
|
30
|
+
index: uint
|
|
31
|
+
},
|
|
32
|
+
device: {
|
|
33
|
+
id: required(string),
|
|
34
|
+
memberId: string,
|
|
35
|
+
name: string,
|
|
36
|
+
isMobile: bool,
|
|
37
|
+
createdAt: int,
|
|
38
|
+
updatedAt: int,
|
|
39
|
+
index: uint
|
|
40
|
+
},
|
|
41
|
+
invite: {
|
|
42
|
+
id: required(string),
|
|
43
|
+
invite: required(bytes),
|
|
44
|
+
publicKey: required(bytes),
|
|
45
|
+
data: bytes,
|
|
46
|
+
sig: bytes,
|
|
47
|
+
role: required(string),
|
|
48
|
+
expires: int,
|
|
49
|
+
createdAt: int,
|
|
50
|
+
index: uint
|
|
51
|
+
},
|
|
52
|
+
handle: {
|
|
53
|
+
id: required(string),
|
|
54
|
+
type: required(string),
|
|
55
|
+
key: required(bytes),
|
|
56
|
+
encryptionKey: bytes,
|
|
57
|
+
name: string,
|
|
58
|
+
createdAt: int,
|
|
59
|
+
updatedAt: int,
|
|
60
|
+
index: uint
|
|
61
|
+
},
|
|
62
|
+
claim: {
|
|
63
|
+
identity: required(bytes),
|
|
64
|
+
writer: required(bytes),
|
|
65
|
+
sig: required(bytes)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const local = {
|
|
70
|
+
master: {
|
|
71
|
+
seed: required(bytes)
|
|
72
|
+
},
|
|
73
|
+
keypair: {
|
|
74
|
+
publicKey: required(bytes),
|
|
75
|
+
secretKey: required(bytes)
|
|
76
|
+
},
|
|
77
|
+
'handle-keypair': {
|
|
78
|
+
id: required(string),
|
|
79
|
+
publicKey: required(bytes),
|
|
80
|
+
secretKey: required(bytes),
|
|
81
|
+
encryptionKey: bytes
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const rpc = {
|
|
86
|
+
'req-empty': {
|
|
87
|
+
ok: bool
|
|
88
|
+
},
|
|
89
|
+
'req-restore': {
|
|
90
|
+
phrase: required(string)
|
|
91
|
+
},
|
|
92
|
+
'req-row': {
|
|
93
|
+
handle: required(string),
|
|
94
|
+
ref: required(string),
|
|
95
|
+
data: required(bytes),
|
|
96
|
+
local: bool
|
|
97
|
+
},
|
|
98
|
+
'req-id': {
|
|
99
|
+
handle: required(string),
|
|
100
|
+
ref: required(string),
|
|
101
|
+
id: required(string),
|
|
102
|
+
local: bool
|
|
103
|
+
},
|
|
104
|
+
'req-query': {
|
|
105
|
+
handle: required(string),
|
|
106
|
+
ref: required(string),
|
|
107
|
+
query: bytes,
|
|
108
|
+
local: bool
|
|
109
|
+
},
|
|
110
|
+
'req-call': {
|
|
111
|
+
handle: required(string),
|
|
112
|
+
op: required(string),
|
|
113
|
+
data: bytes
|
|
114
|
+
},
|
|
115
|
+
'req-invite': {
|
|
116
|
+
handle: required(string),
|
|
117
|
+
role: string
|
|
118
|
+
},
|
|
119
|
+
'req-revoke': {
|
|
120
|
+
handle: required(string),
|
|
121
|
+
invite: required(string)
|
|
122
|
+
},
|
|
123
|
+
'req-join': {
|
|
124
|
+
parent: required(string),
|
|
125
|
+
ref: required(string),
|
|
126
|
+
invite: required(string)
|
|
127
|
+
},
|
|
128
|
+
'req-open': {
|
|
129
|
+
parent: required(string),
|
|
130
|
+
row: required(string)
|
|
131
|
+
},
|
|
132
|
+
'req-handle': {
|
|
133
|
+
handle: required(string)
|
|
134
|
+
},
|
|
135
|
+
'res-data': {
|
|
136
|
+
data: bytes
|
|
137
|
+
},
|
|
138
|
+
'res-rows': {
|
|
139
|
+
data: required(bytes),
|
|
140
|
+
total: required(int),
|
|
141
|
+
size: required(int)
|
|
142
|
+
},
|
|
143
|
+
'res-count': {
|
|
144
|
+
count: required(int)
|
|
145
|
+
},
|
|
146
|
+
'res-invite': {
|
|
147
|
+
invite: required(string)
|
|
148
|
+
},
|
|
149
|
+
'res-handle': {
|
|
150
|
+
id: required(string),
|
|
151
|
+
type: required(string),
|
|
152
|
+
name: string
|
|
153
|
+
},
|
|
154
|
+
'res-identity': {
|
|
155
|
+
id: required(string),
|
|
156
|
+
deviceId: string,
|
|
157
|
+
phrase: string
|
|
158
|
+
},
|
|
159
|
+
'res-ok': {
|
|
160
|
+
ok: bool
|
|
161
|
+
}
|
|
162
|
+
}
|
package/src/handle/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { Pairing } from '@cero-base/core/pairing'
|
|
|
11
11
|
import { toId } from '@cero-base/core/utils'
|
|
12
12
|
import { CeroError } from '@cero-base/core/errors'
|
|
13
13
|
|
|
14
|
+
import { NS, TIMEOUT } from '../lib/constants.js'
|
|
15
|
+
|
|
14
16
|
import { attachRefs } from '../lib/utils.js'
|
|
15
17
|
|
|
16
18
|
export { Ref } from '../lib/utils.js'
|
|
@@ -64,8 +66,6 @@ export { Ref } from '../lib/utils.js'
|
|
|
64
66
|
*
|
|
65
67
|
* @typedef {object} RecoverOpts
|
|
66
68
|
* @property {number} [timeout]
|
|
67
|
-
* @property {string | null} [name]
|
|
68
|
-
* @property {boolean} [isMobile]
|
|
69
69
|
*
|
|
70
70
|
* @typedef {object} HandleExtra
|
|
71
71
|
* @property {string | null} [name] Display name; set on child handles by the owner flow.
|
|
@@ -214,9 +214,9 @@ export class Handle extends ReadyResource {
|
|
|
214
214
|
* @param {RecoverOpts} [opts]
|
|
215
215
|
* @returns {Promise<void>}
|
|
216
216
|
*/
|
|
217
|
-
async recover({ timeout =
|
|
217
|
+
async recover({ timeout = TIMEOUT } = {}) {
|
|
218
218
|
if (this.store.writable) return
|
|
219
|
-
await this.store.claim(
|
|
219
|
+
await this.store.claim()
|
|
220
220
|
await this.store.whenWritable({ timeout })
|
|
221
221
|
await this.store.bee.update()
|
|
222
222
|
}
|
|
@@ -281,9 +281,7 @@ export class Handle extends ReadyResource {
|
|
|
281
281
|
await this.store.call('add-writer', {
|
|
282
282
|
sig,
|
|
283
283
|
master: this.identity.publicKey,
|
|
284
|
-
writer: writerKey
|
|
285
|
-
name: name || null,
|
|
286
|
-
isMobile: false
|
|
284
|
+
writer: writerKey
|
|
287
285
|
})
|
|
288
286
|
await this.store.call('add-member', member)
|
|
289
287
|
})
|
|
@@ -316,7 +314,7 @@ export class Handle extends ReadyResource {
|
|
|
316
314
|
new Handle({
|
|
317
315
|
parent: this,
|
|
318
316
|
spec: pickHandle(this.spec, type),
|
|
319
|
-
namespace:
|
|
317
|
+
namespace: `${NS}/handle/${type}/${writer.id}`,
|
|
320
318
|
routes,
|
|
321
319
|
keyPair: writer
|
|
322
320
|
})
|
|
@@ -332,9 +330,7 @@ export class Handle extends ReadyResource {
|
|
|
332
330
|
await child.store.call('add-writer', {
|
|
333
331
|
master: this.identity.publicKey,
|
|
334
332
|
writer: writerKey,
|
|
335
|
-
sig: this.identity.sign(writerKey)
|
|
336
|
-
name,
|
|
337
|
-
isMobile: false
|
|
333
|
+
sig: this.identity.sign(writerKey)
|
|
338
334
|
})
|
|
339
335
|
await child.store.call('add-member', {
|
|
340
336
|
id: this.identity.id,
|
|
@@ -372,7 +368,7 @@ export class Handle extends ReadyResource {
|
|
|
372
368
|
* @returns {Promise<Handle>}
|
|
373
369
|
*/
|
|
374
370
|
async _join(invite, type, { routes, timeout } = {}) {
|
|
375
|
-
const deadline = timeout ||
|
|
371
|
+
const deadline = timeout || TIMEOUT
|
|
376
372
|
|
|
377
373
|
// Idempotent: if the invite targets a handle we already have, return it —
|
|
378
374
|
// without pairing, so there's no waiting on a peer to confirm.
|
|
@@ -389,7 +385,7 @@ export class Handle extends ReadyResource {
|
|
|
389
385
|
await Handle.join(invite, {
|
|
390
386
|
parent: this,
|
|
391
387
|
spec: pickHandle(this.spec, type),
|
|
392
|
-
namespace:
|
|
388
|
+
namespace: `${NS}/handle/${type}/${randomNs()}`,
|
|
393
389
|
routes,
|
|
394
390
|
timeout
|
|
395
391
|
})
|
|
@@ -439,7 +435,7 @@ export class Handle extends ReadyResource {
|
|
|
439
435
|
const child = new Handle({
|
|
440
436
|
parent: this,
|
|
441
437
|
spec: pickHandle(this.spec, type),
|
|
442
|
-
namespace:
|
|
438
|
+
namespace: `${NS}/handle/${type}/${id}`,
|
|
443
439
|
key: data.key,
|
|
444
440
|
encryptionKey: data.encryptionKey,
|
|
445
441
|
keyPair: /** @type {KeyPair} */ (writer)
|
|
@@ -503,7 +499,7 @@ export class Handle extends ReadyResource {
|
|
|
503
499
|
*/
|
|
504
500
|
static async join(
|
|
505
501
|
invite,
|
|
506
|
-
{ parent, network, identity, store, spec, namespace, routes, timeout =
|
|
502
|
+
{ parent, network, identity, store, spec, namespace, routes, timeout = TIMEOUT } = {}
|
|
507
503
|
) {
|
|
508
504
|
const net = network || parent?.network
|
|
509
505
|
const id = identity || parent?.identity
|
package/src/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Local } from './local/index.js'
|
|
|
11
11
|
import { put, set, get, del, count, watch, call, open } from './lib/operators.js'
|
|
12
12
|
import { peek } from './lib/peek.js'
|
|
13
13
|
import { t, schema } from './lib/spec.js'
|
|
14
|
+
import { FLUSH } from './lib/constants.js'
|
|
14
15
|
|
|
15
16
|
export { Handle, Ref, Local }
|
|
16
17
|
export { put, set, get, del, count, watch, call, open } from './lib/operators.js'
|
|
@@ -65,7 +66,7 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
65
66
|
const network = new Network({ bootstrap: opts.bootstrap })
|
|
66
67
|
await network.ready()
|
|
67
68
|
const discovery = network.join(identity.topic)
|
|
68
|
-
await Promise.race([discovery.flush(), new Promise((r) => setTimeout(r,
|
|
69
|
+
await Promise.race([discovery.flush(), new Promise((r) => setTimeout(r, FLUSH))])
|
|
69
70
|
|
|
70
71
|
const me = new Handle({
|
|
71
72
|
storage,
|
|
@@ -109,12 +110,7 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
109
110
|
})
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
|
-
if (opts.recovery)
|
|
113
|
-
await me.recover({
|
|
114
|
-
timeout: opts.recoveryTimeout,
|
|
115
|
-
name: opts.name || null,
|
|
116
|
-
isMobile: opts.isMobile === true
|
|
117
|
-
})
|
|
113
|
+
if (opts.recovery) await me.recover({ timeout: opts.recoveryTimeout })
|
|
118
114
|
|
|
119
115
|
return me
|
|
120
116
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Shared cero constants.
|
|
2
|
+
|
|
3
|
+
export const NS = 'cero'
|
|
4
|
+
export const COUNTERS = 'counters'
|
|
5
|
+
|
|
6
|
+
// Writer-admission / pairing timeout (ms), and the initial discovery-flush wait (ms).
|
|
7
|
+
export const TIMEOUT = 30000
|
|
8
|
+
export const FLUSH = 500
|
|
9
|
+
|
|
10
|
+
// schema-DSL primitive → HyperDB type (used by the builder).
|
|
11
|
+
export const DB_TYPE = {
|
|
12
|
+
string: 'string',
|
|
13
|
+
uint: 'uint',
|
|
14
|
+
int: 'int',
|
|
15
|
+
bool: 'bool',
|
|
16
|
+
bytes: 'buffer',
|
|
17
|
+
json: 'json',
|
|
18
|
+
fixed32: 'fixed32',
|
|
19
|
+
fixed64: 'fixed64'
|
|
20
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export namespace refs {
|
|
2
|
+
namespace main {
|
|
3
|
+
namespace members {
|
|
4
|
+
let type: string;
|
|
5
|
+
}
|
|
6
|
+
namespace devices {
|
|
7
|
+
let type_1: string;
|
|
8
|
+
export { type_1 as type };
|
|
9
|
+
}
|
|
10
|
+
namespace invites {
|
|
11
|
+
let type_2: string;
|
|
12
|
+
export { type_2 as type };
|
|
13
|
+
}
|
|
14
|
+
namespace handles {
|
|
15
|
+
let type_3: string;
|
|
16
|
+
export { type_3 as type };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
let local: {
|
|
20
|
+
master: {
|
|
21
|
+
type: string;
|
|
22
|
+
kind: string;
|
|
23
|
+
};
|
|
24
|
+
keypair: {
|
|
25
|
+
type: string;
|
|
26
|
+
kind: string;
|
|
27
|
+
};
|
|
28
|
+
'handle-keypairs': {
|
|
29
|
+
type: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function hyperdbType(prim: any): any;
|
|
34
|
+
export function builtinRefs(ns: any, scope: any): {
|
|
35
|
+
[k: string]: {
|
|
36
|
+
schema: string;
|
|
37
|
+
verb: any;
|
|
38
|
+
kind: any;
|
|
39
|
+
path: string[];
|
|
40
|
+
builtin: boolean;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export function builtinTypes(scope: any, extend: any): {
|
|
44
|
+
name: string;
|
|
45
|
+
compact: boolean;
|
|
46
|
+
fields: {
|
|
47
|
+
name: string;
|
|
48
|
+
type: any;
|
|
49
|
+
required: boolean;
|
|
50
|
+
}[];
|
|
51
|
+
}[];
|
|
52
|
+
export function rpcTypes(): {
|
|
53
|
+
name: string;
|
|
54
|
+
compact: boolean;
|
|
55
|
+
fields: {
|
|
56
|
+
name: string;
|
|
57
|
+
type: any;
|
|
58
|
+
required: boolean;
|
|
59
|
+
}[];
|
|
60
|
+
}[];
|
|
61
|
+
export function builtinCollections(ns: any, scope: any): {
|
|
62
|
+
name: string;
|
|
63
|
+
schema: string;
|
|
64
|
+
key: string[];
|
|
65
|
+
}[];
|
|
66
|
+
export function builtinDispatches(ns: any): {
|
|
67
|
+
name: string;
|
|
68
|
+
requestType: string;
|
|
69
|
+
}[];
|
|
70
|
+
export function rpcCommands(ns: any): ({
|
|
71
|
+
name: string;
|
|
72
|
+
request: {
|
|
73
|
+
name: string;
|
|
74
|
+
};
|
|
75
|
+
response: {
|
|
76
|
+
name: string;
|
|
77
|
+
stream?: undefined;
|
|
78
|
+
};
|
|
79
|
+
} | {
|
|
80
|
+
name: string;
|
|
81
|
+
request: {
|
|
82
|
+
name: string;
|
|
83
|
+
};
|
|
84
|
+
response: {
|
|
85
|
+
name: string;
|
|
86
|
+
stream: boolean;
|
|
87
|
+
};
|
|
88
|
+
})[];
|