@cero-base/core 0.2.0 → 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.
Files changed (52) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +180 -136
  3. package/package.json +85 -26
  4. package/src/blobs/index.js +297 -0
  5. package/src/database/CLAUDE.md +3 -0
  6. package/src/database/bootstrap.js +76 -0
  7. package/src/database/dispatch.js +156 -0
  8. package/src/database/index.js +572 -0
  9. package/src/identity/CLAUDE.md +3 -0
  10. package/src/identity/index.js +232 -0
  11. package/src/index.js +20 -1
  12. package/src/lib/CLAUDE.md +3 -0
  13. package/src/lib/constants.js +24 -4
  14. package/src/lib/errors.js +150 -0
  15. package/src/lib/schema.js +58 -440
  16. package/src/lib/spec/index.js +353 -0
  17. package/src/lib/spec/schema.json +284 -0
  18. package/src/lib/utils.js +54 -49
  19. package/src/network/discovery.js +80 -0
  20. package/src/network/index.js +231 -0
  21. package/src/pairing/index.js +482 -0
  22. package/src/pairing/invite.js +199 -0
  23. package/src/rpc/client.js +45 -0
  24. package/src/rpc/index.js +141 -0
  25. package/src/rpc/server.js +45 -0
  26. package/src/storage/index.js +261 -0
  27. package/types/blobs/index.d.ts +169 -0
  28. package/types/database/bootstrap.d.ts +17 -0
  29. package/types/database/dispatch.d.ts +8 -0
  30. package/types/database/index.d.ts +329 -0
  31. package/types/identity/index.d.ts +160 -0
  32. package/types/index.d.ts +11 -0
  33. package/types/lib/constants.d.ts +13 -0
  34. package/types/lib/errors.d.ts +110 -0
  35. package/types/lib/schema.d.ts +53 -0
  36. package/types/lib/spec/index.d.ts +95 -0
  37. package/types/lib/utils.d.ts +39 -0
  38. package/types/network/discovery.d.ts +44 -0
  39. package/types/network/index.d.ts +115 -0
  40. package/types/pairing/index.d.ts +194 -0
  41. package/types/pairing/invite.d.ts +157 -0
  42. package/types/rpc/client.d.ts +18 -0
  43. package/types/rpc/index.d.ts +67 -0
  44. package/types/rpc/server.d.ts +18 -0
  45. package/types/storage/index.d.ts +163 -0
  46. package/src/lib/base.js +0 -84
  47. package/src/lib/batch.js +0 -98
  48. package/src/lib/builder.js +0 -24
  49. package/src/lib/collection.js +0 -252
  50. package/src/lib/crypto.js +0 -6
  51. package/src/lib/index.js +0 -6
  52. package/src/lib/room.js +0 -145
@@ -0,0 +1,45 @@
1
+ import ReadyResource from 'ready-resource'
2
+ import safetyCatch from 'safety-catch'
3
+ import FramedStream from 'framed-stream'
4
+
5
+ import { bindCodec } from './index.js'
6
+
7
+ /**
8
+ * Client-side RPC adapter. Mirror of `RPCServer` — wraps an IPC duplex
9
+ * in a length-framed stream and constructs the spec's hrpc binding ready
10
+ * to issue requests. Frames are paused until `_open` runs so handlers
11
+ * registered before `ready()` see a clean slate.
12
+ */
13
+ export class RPCClient extends ReadyResource {
14
+ /**
15
+ * @param {any} ipc Duplex IPC stream (e.g. a socket or pipe).
16
+ * @param {import('./index.js').Spec} spec Spec object exposing `rpc` and `schema`.
17
+ */
18
+ constructor(ipc, spec) {
19
+ super()
20
+ if (!ipc) throw new TypeError('ipc is required')
21
+ if (!spec) throw new TypeError('spec is required')
22
+ if (!spec.rpc) throw new TypeError('spec.rpc is required')
23
+
24
+ this.ipc = ipc
25
+ this.spec = spec
26
+ this.framed = new FramedStream(ipc)
27
+ this.framed.pause()
28
+ this.rpc = new spec.rpc(this.framed)
29
+ }
30
+
31
+ async _open() {
32
+ if (!this.spec.codec) bindCodec(this.spec)
33
+ this.framed.resume()
34
+ }
35
+
36
+ async _close() {
37
+ if (this.framed && !this.framed.destroyed) {
38
+ try {
39
+ this.framed.destroy()
40
+ } catch (err) {
41
+ safetyCatch(err)
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,141 @@
1
+ import b4a from 'b4a'
2
+ import c from 'compact-encoding'
3
+
4
+ import { getEncoding } from '../lib/spec/index.js'
5
+
6
+ export { RPCServer } from './server.js'
7
+ export { RPCClient } from './client.js'
8
+
9
+ const EMPTY = b4a.alloc(0)
10
+
11
+ // Default envelope encodings for the @cero namespace, used when the supplied
12
+ // spec does not provide its own rows/query/create types in its schema.
13
+ const DEFAULT_ROWS = getEncoding('@cero/rows')
14
+ const DEFAULT_QUERY = getEncoding('@cero/query')
15
+ const DEFAULT_CREATE = getEncoding('@cero/create')
16
+
17
+ /**
18
+ * @typedef {object} Spec
19
+ * @property {{ encode: (type: string, value: any) => Uint8Array, decode: (type: string, buf: Uint8Array) => any, getEncoding?: (name: string) => any }} schema hyperschema-shaped module.
20
+ * @property {{ ns?: string }} [meta] Optional metadata; `ns` controls envelope type fqns.
21
+ * @property {any} [rpc] hrpc constructor used by RPCServer/RPCClient.
22
+ * @property {Codec} [codec] Filled in by `bindCodec`.
23
+ *
24
+ * @typedef {object} Codec
25
+ * @property {(type: string, row: any) => Uint8Array} encodeRow
26
+ * @property {(type: string, buf: Uint8Array) => any} decodeRow
27
+ * @property {(type: string, rows: any[]) => Uint8Array} encodeRows
28
+ * @property {(type: string, buf: Uint8Array) => any[]} decodeRows
29
+ * @property {(q: any) => Uint8Array} encodeQuery
30
+ * @property {(buf: Uint8Array) => any} decodeQuery
31
+ * @property {(row: any) => Uint8Array} encodeCreate
32
+ * @property {(buf: Uint8Array) => any} decodeCreate
33
+ * @property {(handle: any, op: string, data: any) => Uint8Array} encodeAction
34
+ * @property {(handle: any, op: string, buf: Uint8Array) => any} decodeAction
35
+ */
36
+
37
+ /**
38
+ * Attach a `codec` namespace to a hyperschema-shaped `spec`. Looks up
39
+ * envelope types using the spec's namespace (`@<ns>/rows` etc.) and falls
40
+ * back to the bundled `@cero/*` envelopes when the spec has not declared
41
+ * its own. Returns the spec for chaining.
42
+ *
43
+ * @param {Spec} spec
44
+ * @returns {Spec}
45
+ */
46
+ export function bindCodec(spec) {
47
+ if (!spec) throw new TypeError('spec is required')
48
+ const schema = spec.schema
49
+ if (!schema) throw new TypeError('spec.schema is required')
50
+
51
+ const ns = spec.meta?.ns
52
+ const ROWS = ns ? `@${ns}/rows` : null
53
+ const QUERY = ns ? `@${ns}/query` : null
54
+ const CREATE = ns ? `@${ns}/create` : null
55
+
56
+ spec.codec = {
57
+ encodeRow(type, row) {
58
+ if (row == null) return EMPTY
59
+ return schema.encode(type, row)
60
+ },
61
+ decodeRow(type, buf) {
62
+ if (!buf || buf.length === 0) return undefined
63
+ return schema.decode(type, buf)
64
+ },
65
+ encodeRows(type, rows) {
66
+ const data = (rows ?? []).map((r) => schema.encode(type, r))
67
+ return encodeEnvelope(schema, ROWS, DEFAULT_ROWS, { data })
68
+ },
69
+ decodeRows(type, buf) {
70
+ if (!buf || buf.length === 0) return []
71
+ const env = decodeEnvelope(schema, ROWS, DEFAULT_ROWS, buf)
72
+ const data = env?.data || []
73
+ return data.map((b) => schema.decode(type, b))
74
+ },
75
+ encodeQuery(q) {
76
+ if (q == null) return encodeEnvelope(schema, QUERY, DEFAULT_QUERY, {})
77
+ const { gt, gte, lt, lte, limit, reverse, ...rest } = q
78
+ const env = { gt, gte, lt, lte, limit, reverse }
79
+ if (Object.keys(rest).length > 0) env.data = rest
80
+ return encodeEnvelope(schema, QUERY, DEFAULT_QUERY, env)
81
+ },
82
+ decodeQuery(buf) {
83
+ if (!buf || buf.length === 0) return undefined
84
+ const env = decodeEnvelope(schema, QUERY, DEFAULT_QUERY, buf)
85
+ const out = {}
86
+ // Hyperschema fills optional uint/bool with 0/false defaults; only
87
+ // include fields the encoder actually wrote (truthy).
88
+ if (env.gt) out.gt = env.gt
89
+ if (env.gte) out.gte = env.gte
90
+ if (env.lt) out.lt = env.lt
91
+ if (env.lte) out.lte = env.lte
92
+ if (env.limit) out.limit = env.limit
93
+ if (env.reverse) out.reverse = env.reverse
94
+ if (env.data) Object.assign(out, env.data)
95
+ return Object.keys(out).length === 0 ? undefined : out
96
+ },
97
+ encodeCreate(row) {
98
+ if (row == null) return EMPTY
99
+ return encodeEnvelope(schema, CREATE, DEFAULT_CREATE, row)
100
+ },
101
+ decodeCreate(buf) {
102
+ if (!buf || buf.length === 0) return {}
103
+ return decodeEnvelope(schema, CREATE, DEFAULT_CREATE, buf)
104
+ },
105
+ encodeAction(handle, op, data) {
106
+ const ref = handle?.[op]
107
+ if (!ref || !ref.schema) throw new Error(`unknown action: ${op}`)
108
+ if (data == null) return EMPTY
109
+ return schema.encode(ref.schema, data)
110
+ },
111
+ decodeAction(handle, op, buf) {
112
+ const ref = handle?.[op]
113
+ if (!ref || !ref.schema) throw new Error(`unknown action: ${op}`)
114
+ if (!buf || buf.length === 0) return undefined
115
+ return schema.decode(ref.schema, buf)
116
+ }
117
+ }
118
+ return spec
119
+ }
120
+
121
+ // Tries the spec's own envelope type first; falls back to the built-in @cero
122
+ // encoding bundled with this primitive. This lets callers point a foreign spec
123
+ // at the codec without re-declaring the rows/query/create envelopes.
124
+ function encodeEnvelope(schema, fqn, fallback, value) {
125
+ if (fqn && schemaHas(schema, fqn)) return schema.encode(fqn, value)
126
+ return c.encode(fallback, value)
127
+ }
128
+
129
+ function decodeEnvelope(schema, fqn, fallback, buf) {
130
+ if (fqn && schemaHas(schema, fqn)) return schema.decode(fqn, buf)
131
+ return c.decode(fallback, buf)
132
+ }
133
+
134
+ function schemaHas(schema, name) {
135
+ if (typeof schema.getEncoding !== 'function') return false
136
+ try {
137
+ return schema.getEncoding(name) != null
138
+ } catch {
139
+ return false
140
+ }
141
+ }
@@ -0,0 +1,45 @@
1
+ import ReadyResource from 'ready-resource'
2
+ import safetyCatch from 'safety-catch'
3
+ import FramedStream from 'framed-stream'
4
+
5
+ import { bindCodec } from './index.js'
6
+
7
+ /**
8
+ * Server-side RPC adapter. Wraps an IPC duplex in a length-framed stream
9
+ * and instantiates the spec's hrpc binding once `bindCodec` has populated
10
+ * envelope encoders. Reads are paused until `_open` runs so the
11
+ * application has a chance to register handlers before frames flow.
12
+ */
13
+ export class RPCServer extends ReadyResource {
14
+ /**
15
+ * @param {any} ipc Duplex IPC stream (e.g. a socket or pipe).
16
+ * @param {import('./index.js').Spec} spec Spec object exposing `rpc` and `schema`.
17
+ */
18
+ constructor(ipc, spec) {
19
+ super()
20
+ if (!ipc) throw new TypeError('ipc is required')
21
+ if (!spec) throw new TypeError('spec is required')
22
+ if (!spec.rpc) throw new TypeError('spec.rpc is required')
23
+
24
+ this.ipc = ipc
25
+ this.spec = spec
26
+ this.framed = new FramedStream(ipc)
27
+ this.framed.pause()
28
+ this.rpc = new spec.rpc(this.framed)
29
+ }
30
+
31
+ async _open() {
32
+ if (!this.spec.codec) bindCodec(this.spec)
33
+ this.framed.resume()
34
+ }
35
+
36
+ async _close() {
37
+ if (this.framed && !this.framed.destroyed) {
38
+ try {
39
+ this.framed.destroy()
40
+ } catch (err) {
41
+ safetyCatch(err)
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,261 @@
1
+ import HypercoreStorage from 'hypercore-storage'
2
+ import Corestore from 'corestore'
3
+ import HyperDB from 'hyperdb'
4
+ import ReadyResource from 'ready-resource'
5
+
6
+ import { ROCKS, BEE, SINGLE, COLLECTION } from '../lib/constants.js'
7
+ import { genId, subscribe } from '../lib/utils.js'
8
+ import { CeroError } from '../lib/errors.js'
9
+
10
+ /**
11
+ * @typedef {object} StorageOpts
12
+ * @property {{ database: any, meta?: { ns?: string, refs?: Record<string, { kind?: string }> } }} spec
13
+ * @property {'rocks' | 'bee'} backend
14
+ * @property {any} [root] Pre-existing HypercoreStorage to reuse.
15
+ * @property {any} [store] Pre-existing Corestore to reuse.
16
+ *
17
+ * @typedef {{ name: string, kind: string }} Ref
18
+ * @typedef {{ id?: string, createdAt: number, updatedAt: number, [k: string]: any }} StoredRow
19
+ * @typedef {{ data: any }} SingleResult
20
+ * @typedef {{ data: any[], total: number, size: number }} ListResult
21
+ * @typedef {{ data: any | null }} GetByIdResult
22
+ */
23
+
24
+ /**
25
+ * Local, single-writer storage. Backed by either RocksDB (`rocks`) or a
26
+ * Hyperbee on top of corestore (`bee`). Exposes the same `put/get/...`
27
+ * surface as `Database` but without networking or multi-writer apply.
28
+ */
29
+ export class Storage extends ReadyResource {
30
+ /**
31
+ * @param {string} dir
32
+ * @param {StorageOpts} [opts]
33
+ */
34
+ constructor(dir, { spec, backend, root, store } = {}) {
35
+ super()
36
+ if (!root && !store && (typeof dir !== 'string' || !dir))
37
+ throw CeroError.REQUIRED('dir, root, or store')
38
+ if (!spec || !spec.database) throw CeroError.REQUIRED('spec.database')
39
+ if (backend !== ROCKS && backend !== BEE)
40
+ throw CeroError.INVALID('backend must be "rocks" or "bee"')
41
+
42
+ this.dir = dir
43
+ this.spec = spec
44
+ this.backend = backend
45
+ this.ns = spec.meta?.ns || 'cero'
46
+ this.refs = spec.meta?.refs || {}
47
+
48
+ this._cf = `${this.ns}/local`
49
+ this._ownsRoot = !root && !store
50
+ this._ownsStore = !store
51
+ this.root = root || null
52
+ this.store = store || null
53
+ this.db = null
54
+ }
55
+
56
+ /**
57
+ * Construct a RocksDB-backed Storage.
58
+ *
59
+ * @param {string} dir
60
+ * @param {Omit<StorageOpts, 'backend'>} opts
61
+ */
62
+ static rocks(dir, opts) {
63
+ return new Storage(dir, { ...opts, backend: ROCKS })
64
+ }
65
+
66
+ /**
67
+ * Construct a Hyperbee-backed Storage.
68
+ *
69
+ * @param {string} dir
70
+ * @param {Omit<StorageOpts, 'backend'>} opts
71
+ */
72
+ static bee(dir, opts) {
73
+ return new Storage(dir, { ...opts, backend: BEE })
74
+ }
75
+
76
+ async _open() {
77
+ if (this._ownsRoot) {
78
+ this.root = new HypercoreStorage(this.dir, { columnFamilies: [this._cf] })
79
+ await this.root.ready()
80
+ }
81
+
82
+ if (this._ownsStore) {
83
+ this.store = new Corestore(this.root, { manifestVersion: 2 })
84
+ await this.store.ready()
85
+ }
86
+
87
+ if (this.backend === ROCKS) {
88
+ const cf = this.root.rocks.columnFamily(this._cf)
89
+ this.db = HyperDB.rocks(cf, this.spec.database)
90
+ } else {
91
+ const core = this.store.get({ name: 'local' })
92
+ await core.ready()
93
+ this.db = HyperDB.bee(core, this.spec.database, { extension: false, autoUpdate: true })
94
+ }
95
+ await this.db.ready()
96
+ }
97
+
98
+ async _close() {
99
+ if (this.db) {
100
+ if (this.db.flush) await this.db.flush()
101
+ await this.db.close()
102
+ this.db = null
103
+ }
104
+ if (this._ownsStore && this.store) {
105
+ await this.store.close()
106
+ this.store = null
107
+ }
108
+ if (this._ownsRoot && this.root) {
109
+ await this.root.close()
110
+ this.root = null
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Insert (or overwrite by id) a row, stamping `id`/`createdAt`/`updatedAt`.
116
+ *
117
+ * @param {string} name
118
+ * @param {Record<string, any>} row
119
+ * @returns {Promise<SingleResult>}
120
+ */
121
+ async put(name, row) {
122
+ this._guard()
123
+ const ref = this._ref(name)
124
+ const id = row.id ?? genId()
125
+ const ts = Date.now()
126
+ const stored = { id, createdAt: ts, updatedAt: ts, ...row }
127
+ await this._write(ref, stored)
128
+ return { data: stored }
129
+ }
130
+
131
+ /**
132
+ * Upsert by merging with the existing row, preserving `createdAt`.
133
+ *
134
+ * @param {string} name
135
+ * @param {Record<string, any>} row
136
+ * @returns {Promise<SingleResult>}
137
+ */
138
+ async set(name, row) {
139
+ this._guard()
140
+ const ref = this._ref(name)
141
+ const ts = Date.now()
142
+ const existing = await this._read(ref, row?.id)
143
+ /** @type {StoredRow} */
144
+ const stored = {
145
+ ...row,
146
+ createdAt: existing?.createdAt ?? ts,
147
+ updatedAt: ts
148
+ }
149
+ if (ref.kind === COLLECTION && !stored.id) stored.id = genId()
150
+ await this._write(ref, stored)
151
+ return { data: stored }
152
+ }
153
+
154
+ /**
155
+ * Delete by id (collection) or wipe the whole single-row table.
156
+ *
157
+ * @param {string} name
158
+ * @param {string} [id]
159
+ * @returns {Promise<void>}
160
+ */
161
+ async del(name, id) {
162
+ this._guard()
163
+ const ref = this._ref(name)
164
+ const col = this._col(ref)
165
+ await this.db.delete(col, id != null ? { id } : {})
166
+ await this.db.flush()
167
+ }
168
+
169
+ /**
170
+ * Read a row. With no `query`: list all (collection) or fetch the one
171
+ * record (single). With a string id: fetch that specific row.
172
+ *
173
+ * @param {string} name
174
+ * @param {string | Record<string, any>} [query]
175
+ * @returns {Promise<SingleResult | ListResult | GetByIdResult>}
176
+ */
177
+ async get(name, query) {
178
+ this._guard()
179
+ const ref = this._ref(name)
180
+ const col = this._col(ref)
181
+
182
+ if (ref.kind === SINGLE) return { data: await this.db.findOne(col, {}) }
183
+
184
+ if (typeof query === 'string') {
185
+ const rows = await this.db.find(col, {}).toArray()
186
+ return { data: rows.find((r) => r.id === query) ?? null }
187
+ }
188
+
189
+ const data = await this.db.find(col, query || {}).toArray()
190
+ const total = (await this.db.find(col, {}).toArray()).length
191
+ return { data, total, size: data.length }
192
+ }
193
+
194
+ /**
195
+ * Number of rows that match `query` (or total if omitted).
196
+ *
197
+ * @param {string} name
198
+ * @param {Record<string, any>} [query]
199
+ * @returns {Promise<{ data: number }>}
200
+ */
201
+ async count(name, query) {
202
+ this._guard()
203
+ const ref = this._ref(name)
204
+ const col = this._col(ref)
205
+ const rows = await this.db.find(col, query || {}).toArray()
206
+ return { data: rows.length }
207
+ }
208
+
209
+ /**
210
+ * Live snapshot stream — re-emits the latest `get()` result on every
211
+ * underlying mutation. Destroy the stream to stop watching.
212
+ *
213
+ * @param {string} name
214
+ * @param {Record<string, any>} [query]
215
+ * @returns {import('streamx').Readable}
216
+ */
217
+ watch(name, query) {
218
+ this._guard()
219
+ this._ref(name)
220
+ return subscribe({
221
+ get: () => this.get(name, query),
222
+ watch: (fn) => {
223
+ this.db.watch(fn)
224
+ return () => this.db.unwatch(fn)
225
+ }
226
+ })
227
+ }
228
+
229
+ _guard() {
230
+ if (this.closing || this.closed) throw CeroError.CLOSED('Storage')
231
+ if (!this.db) throw CeroError.NOT_READY('Storage', 'storage')
232
+ }
233
+
234
+ /** @param {string} name @returns {Ref} */
235
+ _ref(name) {
236
+ if (typeof name !== 'string' || !name)
237
+ throw CeroError.INVALID('name must be a non-empty string')
238
+ const ref = this.refs[name]
239
+ if (!ref) throw CeroError.UNKNOWN('ref', name)
240
+ return { name, kind: ref.kind || COLLECTION }
241
+ }
242
+
243
+ /** @param {Ref} ref @returns {string} */
244
+ _col(ref) {
245
+ return `@${this.ns}/${ref.name}`
246
+ }
247
+
248
+ /** @param {Ref} ref @param {string} [id] @returns {Promise<any | null>} */
249
+ async _read(ref, id) {
250
+ if (ref.kind === SINGLE) return this.db.findOne(this._col(ref), {})
251
+ if (id == null) return null
252
+ const rows = await this.db.find(this._col(ref), {}).toArray()
253
+ return rows.find((r) => r.id === id) ?? null
254
+ }
255
+
256
+ /** @param {Ref} ref @param {Record<string, any>} row @returns {Promise<void>} */
257
+ async _write(ref, row) {
258
+ await this.db.insert(this._col(ref), row)
259
+ await this.db.flush()
260
+ }
261
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * @typedef {object} BlobsOpts
3
+ * @property {any} store Corestore (or compatible) used to host the blob core.
4
+ * @property {import('../identity/index.js').Identity} [identity] Identity supplying the default encryption key.
5
+ * @property {import('../network/index.js').Network} [network] Optional network used to announce + replicate the core.
6
+ * @property {Uint8Array} [key] Pre-existing blob core key — joins an existing blob feed.
7
+ * @property {Uint8Array} [encryptionKey] Explicit encryption key, overrides `identity.encryptionKey`.
8
+ *
9
+ * @typedef {object} BlobEntry
10
+ * @property {string} id Opaque z32 id used by `get`/`info`/`delete`.
11
+ * @property {string} hash Content hash (z32-encoded blake2b of the bytes).
12
+ * @property {number} size Logical byte length recorded at put-time.
13
+ * @property {any} meta Caller-provided metadata, stored verbatim.
14
+ * @property {number} createdAt Wall-clock timestamp of the put.
15
+ *
16
+ * @typedef {object} PutOpts
17
+ * @property {any} [meta] Arbitrary metadata persisted alongside the blob.
18
+ * @property {number} [size] Override the recorded size (defaults to byte length).
19
+ */
20
+ /**
21
+ * Content-addressable blob store backed by a single Hyperblobs core.
22
+ * Writes dedupe locally by content hash, reads/streams are positional
23
+ * descriptors, and the underlying core is attached to the supplied
24
+ * network for replication.
25
+ */
26
+ export class Blobs extends ReadyResource {
27
+ /** @param {BlobsOpts} [opts] */
28
+ constructor({ store, identity, network, key, encryptionKey }?: BlobsOpts);
29
+ store: any;
30
+ identity: import("../index.js").Identity;
31
+ network: import("../index.js").Network;
32
+ encryptionKey: Uint8Array<ArrayBufferLike>;
33
+ _coreKey: Uint8Array<ArrayBufferLike>;
34
+ core: any;
35
+ hyperblobs: any;
36
+ _discovery: import("../network/discovery.js").Discovery;
37
+ /** @type {Map<string, BlobEntry>} contentHash → entry */
38
+ _index: Map<string, BlobEntry>;
39
+ /** @type {Map<string, BlobEntry>} id → entry (so info/has/delete by id work) */
40
+ _byId: Map<string, BlobEntry>;
41
+ /**
42
+ * Canonical z32 id of the underlying core (null until ready).
43
+ *
44
+ * @returns {string | null}
45
+ */
46
+ get id(): string | null;
47
+ /**
48
+ * Public key of the underlying core.
49
+ *
50
+ * @returns {Uint8Array | null}
51
+ */
52
+ get key(): Uint8Array | null;
53
+ /**
54
+ * Discovery key derived from the core key, used for swarm topics.
55
+ *
56
+ * @returns {Uint8Array | null}
57
+ */
58
+ get discoveryKey(): Uint8Array | null;
59
+ /**
60
+ * Store a buffer or readable stream, returning its opaque id. Identical
61
+ * content from the same writer is deduplicated locally.
62
+ *
63
+ * @param {Uint8Array | import('streamx').Readable} input
64
+ * @param {PutOpts} [opts]
65
+ * @returns {Promise<string>}
66
+ */
67
+ put(input: Uint8Array | any, { meta, size }?: PutOpts): Promise<string>;
68
+ /**
69
+ * Delete the blob with the given id and forget its local index entry.
70
+ *
71
+ * @param {string} id
72
+ * @returns {Promise<void>}
73
+ */
74
+ delete(id: string): Promise<void>;
75
+ /**
76
+ * Resolve the blob bytes for an id. Fetches from peers if not local.
77
+ *
78
+ * @param {string} id
79
+ * @param {any} [opts]
80
+ * @returns {Promise<Uint8Array>}
81
+ */
82
+ get(id: string, opts?: any): Promise<Uint8Array>;
83
+ /**
84
+ * Streaming counterpart of `get` — returns a Readable over the blob bytes.
85
+ *
86
+ * @param {string} id
87
+ * @param {any} [opts]
88
+ * @returns {import('streamx').Readable}
89
+ */
90
+ read(id: string, opts?: any): any;
91
+ /**
92
+ * Look up the locally-cached metadata for a blob id, or `null` if absent.
93
+ *
94
+ * @param {string} id
95
+ * @returns {Promise<BlobEntry | null>}
96
+ */
97
+ info(id: string): Promise<BlobEntry | null>;
98
+ /**
99
+ * Whether the underlying blocks for the blob are present in the local core.
100
+ *
101
+ * @param {string} id
102
+ * @returns {Promise<boolean>}
103
+ */
104
+ has(id: string): Promise<boolean>;
105
+ /**
106
+ * Pre-fetch the blocks for `id` from peers without reading the bytes.
107
+ *
108
+ * @param {string} id
109
+ * @param {any} [opts]
110
+ * @returns {Promise<void>}
111
+ */
112
+ fetch(id: string, opts?: any): Promise<void>;
113
+ _guard(): void;
114
+ }
115
+ export type BlobsOpts = {
116
+ /**
117
+ * Corestore (or compatible) used to host the blob core.
118
+ */
119
+ store: any;
120
+ /**
121
+ * Identity supplying the default encryption key.
122
+ */
123
+ identity?: import("../identity/index.js").Identity;
124
+ /**
125
+ * Optional network used to announce + replicate the core.
126
+ */
127
+ network?: import("../network/index.js").Network;
128
+ /**
129
+ * Pre-existing blob core key — joins an existing blob feed.
130
+ */
131
+ key?: Uint8Array;
132
+ /**
133
+ * Explicit encryption key, overrides `identity.encryptionKey`.
134
+ */
135
+ encryptionKey?: Uint8Array;
136
+ };
137
+ export type BlobEntry = {
138
+ /**
139
+ * Opaque z32 id used by `get`/`info`/`delete`.
140
+ */
141
+ id: string;
142
+ /**
143
+ * Content hash (z32-encoded blake2b of the bytes).
144
+ */
145
+ hash: string;
146
+ /**
147
+ * Logical byte length recorded at put-time.
148
+ */
149
+ size: number;
150
+ /**
151
+ * Caller-provided metadata, stored verbatim.
152
+ */
153
+ meta: any;
154
+ /**
155
+ * Wall-clock timestamp of the put.
156
+ */
157
+ createdAt: number;
158
+ };
159
+ export type PutOpts = {
160
+ /**
161
+ * Arbitrary metadata persisted alongside the blob.
162
+ */
163
+ meta?: any;
164
+ /**
165
+ * Override the recorded size (defaults to byte length).
166
+ */
167
+ size?: number;
168
+ };
169
+ import ReadyResource from 'ready-resource';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * First-run device provisioning: mint a device writer keypair, persist it as a
3
+ * writer, then swap the autobee's local core over to it. With `recovering`,
4
+ * waits for the first peer-replicated append before swapping.
5
+ *
6
+ * @param {import('./index.js').Database} db
7
+ * @param {{ name?: string | null, isMobile?: boolean, recovering?: boolean }} [opts]
8
+ * @returns {Promise<{ id: Uint8Array, writer: import('../identity/index.js').KeyPair }>}
9
+ */
10
+ export function bootstrap(db: import("./index.js").Database, { name, isMobile, recovering }?: {
11
+ name?: string | null;
12
+ isMobile?: boolean;
13
+ recovering?: boolean;
14
+ }): Promise<{
15
+ id: Uint8Array;
16
+ writer: import("../identity/index.js").KeyPair;
17
+ }>;
@@ -0,0 +1,8 @@
1
+ export function makeDispatcher(spec: any, ns: any, routes: any): {
2
+ dispatcher: any;
3
+ apply(nodes: any, view: any, host: any): Promise<void>;
4
+ };
5
+ export const BUILTINS: {
6
+ name: string;
7
+ verb: string;
8
+ }[];