@cero-base/core 0.8.3 → 0.8.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cero-base/core",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "cero p2p primitives — identity, storage, network, database, blobs, rpc, pairing.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -71,6 +71,8 @@ export class Database extends ReadyResource {
71
71
  this.beforeHooks = new Map()
72
72
  this.afterHooks = new Map()
73
73
  this.updaters = new Set()
74
+ this.onApplyHooks = new Set()
75
+ this._applySeq = 0
74
76
  this.txQueue = null
75
77
  }
76
78
 
@@ -102,8 +104,11 @@ export class Database extends ReadyResource {
102
104
  optimistic: true,
103
105
  wakeup: this.network?.wakeup || undefined,
104
106
  open: (b) => HyperDB.bee2(b, this.spec.database, { autoUpdate: true }),
105
- apply: (nodes, view, host) =>
106
- (this.applyOverride || this.dispatcher.apply)(nodes, view, host),
107
+ apply: async (nodes, view, host) => {
108
+ const result = await (this.applyOverride || this.dispatcher.apply)(nodes, view, host)
109
+ if (this.onApplyHooks.size) this._observe(nodes)
110
+ return result
111
+ },
107
112
  update: async (db) => {
108
113
  await db.update()
109
114
  for (const fn of this.updaters) fn()
@@ -178,6 +183,49 @@ export class Database extends ReadyResource {
178
183
  return () => this.updaters.delete(fn)
179
184
  }
180
185
 
186
+ /**
187
+ * Observe every applied op — local AND replicated (apply processes the merged
188
+ * log). The callback receives `{ op, name, row, writerKey, seq }` and runs
189
+ * synchronously, so it must be cheap and non-blocking (enqueue and return).
190
+ * Gated: zero cost when no subscriber is registered. Returns a disposer.
191
+ *
192
+ * @param {(event: { op: string, name: string, row: any, writerKey: any, seq: number }) => void} fn
193
+ * @returns {() => void} disposer
194
+ */
195
+ onApply(fn) {
196
+ if (typeof fn !== 'function') return () => {}
197
+ this.onApplyHooks.add(fn)
198
+ return () => this.onApplyHooks.delete(fn)
199
+ }
200
+
201
+ _observe(nodes) {
202
+ for (const node of nodes) {
203
+ let event
204
+ try {
205
+ const { name, value } = this.spec.dispatch.decode(node.value)
206
+ const verb = name.slice(name.indexOf('/') + 1) // '@cero/set-messages' → 'set-messages'
207
+ const dash = verb.indexOf('-')
208
+ event = {
209
+ op: dash < 0 ? verb : verb.slice(0, dash),
210
+ name: dash < 0 ? verb : verb.slice(dash + 1),
211
+ row: value,
212
+ writerKey: node.key,
213
+ seq: this._applySeq++
214
+ }
215
+ } catch (err) {
216
+ safetyCatch(err)
217
+ continue
218
+ }
219
+ for (const fn of this.onApplyHooks) {
220
+ try {
221
+ fn(event)
222
+ } catch (err) {
223
+ safetyCatch(err)
224
+ }
225
+ }
226
+ }
227
+ }
228
+
181
229
  /**
182
230
  * Run all registered `before:op` hooks and emit the corresponding event.
183
231
  *
@@ -69,6 +69,8 @@ export class Database extends ReadyResource {
69
69
  beforeHooks: Map<any, any>;
70
70
  afterHooks: Map<any, any>;
71
71
  updaters: Set<any>;
72
+ onApplyHooks: Set<any>;
73
+ _applySeq: number;
72
74
  txQueue: any[];
73
75
  _discovery: import("../network/discovery.js").Discovery;
74
76
  /** Open the underlying autobee, wire dispatcher + apply, attach to network. */
@@ -101,6 +103,23 @@ export class Database extends ReadyResource {
101
103
  * @returns {() => void} disposer
102
104
  */
103
105
  onUpdate(fn: () => void): () => void;
106
+ /**
107
+ * Observe every applied op — local AND replicated (apply processes the merged
108
+ * log). The callback receives `{ op, name, row, writerKey, seq }` and runs
109
+ * synchronously, so it must be cheap and non-blocking (enqueue and return).
110
+ * Gated: zero cost when no subscriber is registered. Returns a disposer.
111
+ *
112
+ * @param {(event: { op: string, name: string, row: any, writerKey: any, seq: number }) => void} fn
113
+ * @returns {() => void} disposer
114
+ */
115
+ onApply(fn: (event: {
116
+ op: string;
117
+ name: string;
118
+ row: any;
119
+ writerKey: any;
120
+ seq: number;
121
+ }) => void): () => void;
122
+ _observe(nodes: any): void;
104
123
  /**
105
124
  * Run all registered `before:op` hooks and emit the corresponding event.
106
125
  *