@cero-base/cero 1.7.0 → 1.7.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/package.json +2 -2
- package/src/handle/index.js +45 -0
- package/src/lib/operators.js +1 -37
- package/types/handle/index.d.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cero-base/cero",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "The ideal p2p API — everything is a handle, handles contain refs, refs contain rows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"test:node": "ls test/*.test.js | xargs -P1 -n1 brittle-node"
|
|
96
96
|
},
|
|
97
97
|
"dependencies": {
|
|
98
|
-
"@cero-base/core": "^1.7.
|
|
98
|
+
"@cero-base/core": "^1.7.1",
|
|
99
99
|
"b4a": "^1.8.1",
|
|
100
100
|
"bare-abort-controller": "^1.1.2",
|
|
101
101
|
"bare-crypto": "^1.15.3",
|
package/src/handle/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { Pairing } from '@cero-base/core/pairing'
|
|
|
14
14
|
import { toId, grants, addWriterPayload } from '@cero-base/core/utils'
|
|
15
15
|
import { CeroError } from '@cero-base/core/errors'
|
|
16
16
|
import { Blobs } from '@cero-base/core/blobs'
|
|
17
|
+
import { decodeId } from '@cero-base/core/blobs/codec'
|
|
17
18
|
import { FileServer } from '@cero-base/core/blobs/server'
|
|
18
19
|
|
|
19
20
|
import { NS, TIMEOUT } from '../lib/constants.js'
|
|
@@ -339,6 +340,50 @@ export class Handle extends ReadyResource {
|
|
|
339
340
|
return blobs
|
|
340
341
|
}
|
|
341
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Remember the blob-core key a file id points at so the file server can
|
|
345
|
+
* open the core. Lives on the Handle (not the shared operators) because the
|
|
346
|
+
* epoch-key derivation pulls native crypto — the RPC client must stay
|
|
347
|
+
* bundleable without it.
|
|
348
|
+
*
|
|
349
|
+
* @param {string} id
|
|
350
|
+
* @param {number} [stamp]
|
|
351
|
+
*/
|
|
352
|
+
_registerBlobCore(id, stamp) {
|
|
353
|
+
if (!id || !this.root?._coreKeys) return
|
|
354
|
+
try {
|
|
355
|
+
const { coreKey } = decodeId(id)
|
|
356
|
+
const hex = b4a.toString(coreKey, 'hex')
|
|
357
|
+
if (!this.root._coreKeys.has(hex)) {
|
|
358
|
+
// a file-field value carries no stamp — look it up from the files row
|
|
359
|
+
// (fire-and-forget: idempotent, resolution happens again per request)
|
|
360
|
+
if (stamp === undefined) {
|
|
361
|
+
this.store
|
|
362
|
+
.get('files', id)
|
|
363
|
+
.then(({ data }) => data && this._registerBlobCore(id, data.stamp || 0))
|
|
364
|
+
.catch(safetyCatch)
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
const key = this._blobCoreKey(stamp)
|
|
368
|
+
if (!key) return // unknown epoch — this device is not entitled to the core
|
|
369
|
+
this.root._coreKeys.set(hex, key)
|
|
370
|
+
}
|
|
371
|
+
// remember which handle read it, so close prunes the entry (re-registered
|
|
372
|
+
// on the next read if another handle still serves the same core)
|
|
373
|
+
if (this !== this.root) (this._blobKeys ??= new Set()).add(hex)
|
|
374
|
+
} catch {
|
|
375
|
+
// ignore invalid ids
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Base-era blob cores use the OWNING handle's key (not the root's — rooms
|
|
380
|
+
// have their own keys); rotated-era cores derive from the epoch entropy.
|
|
381
|
+
_blobCoreKey(stamp) {
|
|
382
|
+
if (!stamp) return this.store.encryptionKey
|
|
383
|
+
const entropy = this.store.keyring.entropy(stamp)
|
|
384
|
+
return entropy ? blobEpochKey(entropy) : null
|
|
385
|
+
}
|
|
386
|
+
|
|
342
387
|
/** Canonical id — identity id for the root handle, store key for children. */
|
|
343
388
|
get id() {
|
|
344
389
|
if (!this.parent) return this.identity.id
|
package/src/lib/operators.js
CHANGED
|
@@ -2,7 +2,6 @@ import { Readable } from 'streamx'
|
|
|
2
2
|
import b4a from 'b4a'
|
|
3
3
|
|
|
4
4
|
import { encodeId, decodeId } from '@cero-base/core/blobs/codec'
|
|
5
|
-
import { blobEpochKey } from '@cero-base/core/database/encryption'
|
|
6
5
|
import { CeroError } from '@cero-base/core/errors'
|
|
7
6
|
import { onAbort } from './utils.js'
|
|
8
7
|
|
|
@@ -169,7 +168,7 @@ function resolveRow(ref, row) {
|
|
|
169
168
|
if (ref.handle.rpc) return ref.handle._resolveRow(ref.name, ref.handle._refInfo(ref.name), row)
|
|
170
169
|
const handle = ref.handle
|
|
171
170
|
const resolve = (id, name, stamp) => {
|
|
172
|
-
|
|
171
|
+
handle._registerBlobCore(id, stamp)
|
|
173
172
|
return resolveFile(handle, id, name)
|
|
174
173
|
}
|
|
175
174
|
if (ref.name === 'files') {
|
|
@@ -186,41 +185,6 @@ function resolveRow(ref, row) {
|
|
|
186
185
|
return out
|
|
187
186
|
}
|
|
188
187
|
|
|
189
|
-
function registerBlobCore(handle, id, stamp) {
|
|
190
|
-
if (!id || !handle.root?._coreKeys) return
|
|
191
|
-
try {
|
|
192
|
-
const { coreKey } = decodeId(id)
|
|
193
|
-
const hex = b4a.toString(coreKey, 'hex')
|
|
194
|
-
if (!handle.root._coreKeys.has(hex)) {
|
|
195
|
-
// a file-field value carries no stamp — look it up from the files row
|
|
196
|
-
// (fire-and-forget: idempotent, resolution happens again per request)
|
|
197
|
-
if (stamp === undefined) {
|
|
198
|
-
handle.store
|
|
199
|
-
.get('files', id)
|
|
200
|
-
.then(({ data }) => data && registerBlobCore(handle, id, data.stamp || 0))
|
|
201
|
-
.catch(() => {})
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
const key = blobCoreKey(handle, stamp)
|
|
205
|
-
if (!key) return // unknown epoch — this device is not entitled to the core
|
|
206
|
-
handle.root._coreKeys.set(hex, key)
|
|
207
|
-
}
|
|
208
|
-
// remember which handle read it, so close prunes the entry (re-registered
|
|
209
|
-
// on the next read if another handle still serves the same core)
|
|
210
|
-
if (handle !== handle.root) (handle._blobKeys ??= new Set()).add(hex)
|
|
211
|
-
} catch {
|
|
212
|
-
// ignore invalid ids
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Base-era blob cores use the OWNING handle's key (not the root's — rooms have
|
|
217
|
-
// their own keys); rotated-era cores derive from the epoch entropy.
|
|
218
|
-
function blobCoreKey(handle, stamp) {
|
|
219
|
-
if (!stamp) return handle.store.encryptionKey
|
|
220
|
-
const entropy = handle.store.keyring.entropy(stamp)
|
|
221
|
-
return entropy ? blobEpochKey(entropy) : null
|
|
222
|
-
}
|
|
223
|
-
|
|
224
188
|
/**
|
|
225
189
|
* Read from `ref`. For data refs, dispatches to the underlying store. For
|
|
226
190
|
* `handle`-kind refs, lists existing child handles of that type from the
|
package/types/handle/index.d.ts
CHANGED
|
@@ -172,6 +172,17 @@ export class Handle extends ReadyResource {
|
|
|
172
172
|
get blobs(): Blobs;
|
|
173
173
|
_baseBlobs(): Blobs;
|
|
174
174
|
_makeBlobs(name: any, encryptionKey: any, stamp: any): Blobs;
|
|
175
|
+
/**
|
|
176
|
+
* Remember the blob-core key a file id points at so the file server can
|
|
177
|
+
* open the core. Lives on the Handle (not the shared operators) because the
|
|
178
|
+
* epoch-key derivation pulls native crypto — the RPC client must stay
|
|
179
|
+
* bundleable without it.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} id
|
|
182
|
+
* @param {number} [stamp]
|
|
183
|
+
*/
|
|
184
|
+
_registerBlobCore(id: string, stamp?: number): void;
|
|
185
|
+
_blobCoreKey(stamp: any): Uint8Array<ArrayBufferLike>;
|
|
175
186
|
/** Canonical id — identity id for the root handle, store key for children. */
|
|
176
187
|
get id(): any;
|
|
177
188
|
/** This device's id + name. `null` on child handles. */
|