@cero-base/core 1.1.1 → 1.3.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/src/lib/utils.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import crypto from 'crypto'
2
+ import b4a from 'b4a'
2
3
  import { Readable } from 'streamx'
3
4
  import z32 from 'z32'
4
- import { encode, decode, isValid } from 'hypercore-id-encoding'
5
+ import { encode, decode } from 'hypercore-id-encoding'
5
6
 
6
7
  import { ROLE_PERMS, RANK } from './constants.js'
7
8
 
@@ -28,12 +29,18 @@ export const toId = encode
28
29
  */
29
30
  export const toKey = decode
30
31
 
31
- /**
32
- * Test whether a string is a well-formed canonical id.
33
- *
34
- * @type {(id: string) => boolean}
35
- */
36
- export const isKeyId = isValid
32
+ // Domain-separated payloads for writer-admission signatures. Binding the db
33
+ // key makes an admission unreplayable across rooms; the tag pins the format.
34
+ // v2 — pre-1.2 signatures (writer‖appender / bare writer) no longer verify.
35
+ const ADD_WRITER_TAG = b4a.from('cero/add-writer/v2')
36
+ const CLAIM_WRITER_TAG = b4a.from('cero/claim-writer/v2')
37
+
38
+ /** @type {(dbKey: Uint8Array, writer: Uint8Array, appender: Uint8Array) => Uint8Array} */
39
+ export const addWriterPayload = (dbKey, writer, appender) =>
40
+ b4a.concat([ADD_WRITER_TAG, dbKey, writer, appender])
41
+
42
+ /** @type {(dbKey: Uint8Array, writer: Uint8Array) => Uint8Array} */
43
+ export const claimWriterPayload = (dbKey, writer) => b4a.concat([CLAIM_WRITER_TAG, dbKey, writer])
37
44
 
38
45
  /**
39
46
  * Whether `role` is granted `perm` under the default policy.
@@ -63,7 +70,6 @@ export const outranks = (a, b) => RANK[a] != null && RANK[b] != null && RANK[a]
63
70
  */
64
71
  export function subscribe({ get, watch }) {
65
72
  let stop = null
66
- let version = 0
67
73
  let last
68
74
  const stream = new Readable({
69
75
  destroy(cb) {
@@ -78,15 +84,30 @@ export function subscribe({ get, watch }) {
78
84
  stream.push(data)
79
85
  }
80
86
 
87
+ // Coalesce: one get() in flight at a time. Ticks that land mid-read mark
88
+ // dirty and fold into a single trailing re-read — a write burst costs one
89
+ // extra snapshot instead of one concurrent scan per tick, and only the
90
+ // latest state is emitted.
91
+ let running = false
92
+ let dirty = false
81
93
  const push = async () => {
82
94
  if (stream.destroyed) return
83
- const v = ++version
95
+ if (running) {
96
+ dirty = true
97
+ return
98
+ }
99
+ running = true
84
100
  try {
85
- const data = await get()
86
- if (v !== version || stream.destroyed) return
87
- emit(data)
101
+ do {
102
+ dirty = false
103
+ const data = await get()
104
+ if (stream.destroyed) return
105
+ emit(data)
106
+ } while (dirty)
88
107
  } catch (e) {
89
108
  stream.destroy(e)
109
+ } finally {
110
+ running = false
90
111
  }
91
112
  }
92
113