@serfab/cadre-core 1.17.0 → 1.18.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/README.md +3 -2
- package/dist/cadre-invite-protocol.d.ts +35 -13
- package/dist/cadre-invite-protocol.js +90 -25
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-node.d.ts +586 -255
- package/dist/cadre-node.js +1176 -564
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-cohort.d.ts +66 -9
- package/dist/control-cohort.js +67 -9
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +451 -193
- package/dist/control-database.js +701 -359
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +46 -43
- package/dist/control-formation-recorder.js +77 -78
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +6 -0
- package/dist/control-protocol-guard.js +14 -2
- package/dist/control-protocol-guard.js.map +1 -1
- package/dist/control-schema-version.d.ts +48 -31
- package/dist/control-schema-version.js +67 -45
- package/dist/control-schema-version.js.map +1 -1
- package/dist/control-schema.d.ts +8 -8
- package/dist/control-schema.js +16 -16
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +42 -7
- package/dist/control-write-retry.js +87 -19
- package/dist/control-write-retry.js.map +1 -1
- package/dist/enrolled-machine-store.d.ts +3 -3
- package/dist/enrolled-machine-store.js +3 -3
- package/dist/formation-token.d.ts +48 -0
- package/dist/formation-token.js +26 -0
- package/dist/formation-token.js.map +1 -0
- package/dist/index.d.ts +14 -13
- package/dist/index.js +16 -13
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +26 -9
- package/dist/joined-strand-store.js +63 -24
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/link-budget.d.ts +9 -7
- package/dist/link-budget.js +9 -7
- package/dist/link-budget.js.map +1 -1
- package/dist/member-redemption.d.ts +15 -9
- package/dist/member-redemption.js +13 -8
- package/dist/member-redemption.js.map +1 -1
- package/dist/membership-view.d.ts +91 -0
- package/dist/membership-view.js +110 -0
- package/dist/membership-view.js.map +1 -0
- package/dist/owner-grant-protocol.d.ts +11 -7
- package/dist/owner-grant-protocol.js +31 -17
- package/dist/owner-grant-protocol.js.map +1 -1
- package/dist/peer-addr-book.d.ts +10 -1
- package/dist/peer-addr-book.js +4 -3
- package/dist/peer-addr-book.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +89 -20
- package/dist/peer-join-backfill.js +265 -108
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/pending-join-runner.d.ts +48 -18
- package/dist/pending-join-runner.js +74 -56
- package/dist/pending-join-runner.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +47 -40
- package/dist/seed-bootstrap.js +66 -61
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-database.d.ts +23 -2
- package/dist/strand-database.js +26 -6
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +176 -89
- package/dist/strand-first-sync-gate.js +288 -122
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +9 -9
- package/dist/strand-formation-deadlines.js +9 -9
- package/dist/strand-formation-manager.d.ts +23 -17
- package/dist/strand-formation-manager.js +46 -44
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +5 -8
- package/dist/strand-formation-protocol.js +101 -16
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +26 -17
- package/dist/strand-instance-manager.js +119 -50
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +16 -13
- package/dist/strand-membership-reconciler.js +30 -22
- package/dist/strand-membership-reconciler.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +40 -28
- package/dist/strand-membership-writer.js +212 -153
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-move.js +5 -2
- package/dist/strand-move.js.map +1 -1
- package/dist/strand-revocation-enforcer.d.ts +134 -60
- package/dist/strand-revocation-enforcer.js +144 -71
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +50 -39
- package/dist/strand-solicitation.js +10 -3
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-watcher.d.ts +80 -13
- package/dist/strand-watcher.js +152 -48
- package/dist/strand-watcher.js.map +1 -1
- package/dist/table-change-watch.d.ts +63 -0
- package/dist/table-change-watch.js +209 -0
- package/dist/table-change-watch.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +4 -3
- package/dist/timeout-scheduler.js +4 -3
- package/dist/timeout-scheduler.js.map +1 -1
- package/dist/types.d.ts +90 -49
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/package.json +8 -8
- package/src/cadre-invite-protocol.ts +119 -31
- package/src/cadre-node.ts +1282 -584
- package/src/control-cohort.ts +86 -9
- package/src/control-database.ts +848 -403
- package/src/control-formation-recorder.ts +359 -353
- package/src/control-protocol-guard.ts +16 -2
- package/src/control-schema-version.ts +101 -51
- package/src/control-schema.ts +16 -16
- package/src/control-write-retry.ts +91 -19
- package/src/delegate-admission.ts +351 -351
- package/src/enrolled-machine-store.ts +3 -3
- package/src/formation-token.ts +63 -0
- package/src/index.ts +30 -17
- package/src/joined-strand-store.ts +69 -24
- package/src/link-budget.ts +9 -7
- package/src/member-redemption.ts +20 -13
- package/src/membership-view.ts +144 -0
- package/src/owner-grant-protocol.ts +33 -18
- package/src/peer-addr-book.ts +4 -3
- package/src/peer-join-backfill.ts +971 -761
- package/src/pending-join-runner.ts +76 -53
- package/src/seed-bootstrap.ts +72 -67
- package/src/strand-database.ts +352 -322
- package/src/strand-first-sync-gate.ts +334 -134
- package/src/strand-formation-deadlines.ts +227 -227
- package/src/strand-formation-manager.ts +731 -725
- package/src/strand-formation-protocol.ts +122 -24
- package/src/strand-instance-manager.ts +2092 -2026
- package/src/strand-membership-reconciler.ts +33 -25
- package/src/strand-membership-writer.ts +2093 -2019
- package/src/strand-move.ts +5 -2
- package/src/strand-revocation-enforcer.ts +740 -654
- package/src/strand-solicitation.ts +61 -34
- package/src/strand-watcher.ts +175 -56
- package/src/table-change-watch.ts +285 -0
- package/src/timeout-scheduler.ts +29 -28
- package/src/types.ts +89 -51
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One way for cadre-core to be told that a control or strand table changed, so an idle loop
|
|
3
|
+
* can wait for a change instead of re-reading on a timer. Built on Quereus's
|
|
4
|
+
* `Database.watch`, which hears both a commit made on this `Database` and, through the
|
|
5
|
+
* Optimystic plugin's `notifyExternalChange` bridge, every change this node's storage takes
|
|
6
|
+
* in from elsewhere: a consensus commit it took part in as a cohort member, a block-transfer
|
|
7
|
+
* push such as join backfill, and a read-repair landing. Events are table-wide; there are no
|
|
8
|
+
* row keys.
|
|
9
|
+
*
|
|
10
|
+
* The consumer's `onChange` never runs inside a commit. The watch handler only sets flags
|
|
11
|
+
* and arms a timer, and runs are single-flight: changes inside the coalescing window share
|
|
12
|
+
* one run, a change during a run produces exactly one follow-up run, and a consumer's own
|
|
13
|
+
* `trigger` (start, retry backoff) and the backstop go through the same rule.
|
|
14
|
+
* `docs/architecture.md` → "Table change subscriptions" says what the seam does not hear,
|
|
15
|
+
* which is why consumers keep a backstop.
|
|
16
|
+
*/
|
|
17
|
+
import debug from 'debug';
|
|
18
|
+
import { defaultTimeoutScheduler } from './timeout-scheduler.js';
|
|
19
|
+
const log = debug('sereus:cadre:table-watch');
|
|
20
|
+
/**
|
|
21
|
+
* Default coalescing window. One local commit fires the watch twice (Quereus's own
|
|
22
|
+
* post-commit dispatch, then the plugin's storage event); this folds both into one run.
|
|
23
|
+
*/
|
|
24
|
+
export const TABLE_CHANGE_COALESCE_MS = 50;
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe `onChange` to changes in `tables` (qualified `'Schema.Table'` names, e.g.
|
|
27
|
+
* `'CadreControl.Strand'`). Throws synchronously on a name that is not qualified, or a table
|
|
28
|
+
* that does not exist.
|
|
29
|
+
*
|
|
30
|
+
* NOTE: the plugin arms its storage bridge for a table on that table's first full
|
|
31
|
+
* initialization on this `Database`, so a table not yet touched hears local commits only,
|
|
32
|
+
* not replicated ones. Consumers' start runs touch their tables; the seam does not, because
|
|
33
|
+
* touching a never-written collection consults the network.
|
|
34
|
+
*
|
|
35
|
+
* NOTE: Quereus silently disposes a subscription whose table is dropped or altered. Control
|
|
36
|
+
* and strand schemas are fixed for a database's life (an app-schema attach adds `App` tables
|
|
37
|
+
* and alters no `Strand` one), so nothing re-subscribes; revisit if that stops holding.
|
|
38
|
+
*/
|
|
39
|
+
export function watchTables(db, tables, onChange, options) {
|
|
40
|
+
return new TableChangeWatcher({ db, tables }, onChange, options);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* {@link watchTables}' schedule — single-flight runs, `trigger`, the backstop — with no table
|
|
44
|
+
* subscription, for a consumer with no database to watch (tests, fakes).
|
|
45
|
+
*/
|
|
46
|
+
export function scheduleRuns(onChange, options) {
|
|
47
|
+
return new TableChangeWatcher(undefined, onChange, options);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The subscriptions one database owner has handed out, so its `close()` can dispose those
|
|
51
|
+
* still live — synchronously, ahead of its own awaits, so nothing a closing commit does
|
|
52
|
+
* schedules a run. (`Database.close()` disposes them too, but only after its own awaits.)
|
|
53
|
+
*/
|
|
54
|
+
export class TableWatchRegistry {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.live = new Set();
|
|
57
|
+
}
|
|
58
|
+
watch(db, tables, onChange, options) {
|
|
59
|
+
const inner = watchTables(db, tables, onChange, options);
|
|
60
|
+
const tracked = {
|
|
61
|
+
trigger: delayMs => inner.trigger(delayMs),
|
|
62
|
+
dispose: () => {
|
|
63
|
+
this.live.delete(tracked);
|
|
64
|
+
inner.dispose();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
this.live.add(tracked);
|
|
68
|
+
return tracked;
|
|
69
|
+
}
|
|
70
|
+
disposeAll() {
|
|
71
|
+
for (const watch of [...this.live]) {
|
|
72
|
+
watch.dispose();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
class TableChangeWatcher {
|
|
77
|
+
constructor(watched, onChange, options) {
|
|
78
|
+
this.onChange = onChange;
|
|
79
|
+
this.options = options;
|
|
80
|
+
/** The consumer's name for each watched table, by Quereus's lowercased `schema.table` key. */
|
|
81
|
+
this.names = new Map();
|
|
82
|
+
/**
|
|
83
|
+
* Every armed timer: the coalescing window, `trigger` delays and the backstop. Whichever
|
|
84
|
+
* fires first starts the run, and starting a run clears the rest.
|
|
85
|
+
*/
|
|
86
|
+
this.wakes = new Set();
|
|
87
|
+
this.running = false;
|
|
88
|
+
/** A timer fired while a run was in flight: one more run, a window after it settles. */
|
|
89
|
+
this.rerun = false;
|
|
90
|
+
this.disposed = false;
|
|
91
|
+
this.warnedThenable = false;
|
|
92
|
+
this.scheduler = options.scheduler ?? defaultTimeoutScheduler;
|
|
93
|
+
this.coalesceMs = options.coalesceMs ?? TABLE_CHANGE_COALESCE_MS;
|
|
94
|
+
this.subscription = watched && this.subscribe(watched);
|
|
95
|
+
this.armBackstop();
|
|
96
|
+
}
|
|
97
|
+
trigger(delayMs = 0) {
|
|
98
|
+
if (!this.disposed) {
|
|
99
|
+
this.arm(delayMs);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
dispose() {
|
|
103
|
+
if (this.disposed) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.disposed = true;
|
|
107
|
+
this.subscription?.unsubscribe();
|
|
108
|
+
this.clearWakes();
|
|
109
|
+
}
|
|
110
|
+
subscribe({ db, tables }) {
|
|
111
|
+
const watches = tables.map(fullTableWatch);
|
|
112
|
+
watches.forEach((watch, i) => this.names.set(tableKey(watch), tables[i]));
|
|
113
|
+
const scope = { watches, nonDeterministicSources: [], unboundParameters: [] };
|
|
114
|
+
// Returns undefined, so Quereus never awaits consumer work inside a commit.
|
|
115
|
+
return db.watch(scope, event => this.handleChange(event));
|
|
116
|
+
}
|
|
117
|
+
/** Runs inside the committing call, possibly under the control write lock: flags and a timer only. */
|
|
118
|
+
handleChange(event) {
|
|
119
|
+
if (this.disposed) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.coalesceWake ?? (this.coalesceWake = this.arm(this.coalesceMs));
|
|
123
|
+
for (const { watch } of event.matched) {
|
|
124
|
+
this.invalidate(this.names.get(tableKey(watch)) ?? tableKey(watch));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
invalidate(table) {
|
|
128
|
+
let result;
|
|
129
|
+
try {
|
|
130
|
+
result = this.options.onInvalidate?.(table);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
log('%s: onInvalidate threw for %s: %O', this.options.label, table, err);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!this.warnedThenable && isThenable(result)) {
|
|
137
|
+
this.warnedThenable = true;
|
|
138
|
+
log('%s: misuse — onInvalidate returned a promise for %s; it is never awaited, and must only set a flag', this.options.label, table);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
arm(delayMs) {
|
|
142
|
+
const wake = { handle: undefined };
|
|
143
|
+
wake.handle = this.scheduler.setTimeout(() => this.fire(wake), delayMs);
|
|
144
|
+
this.wakes.add(wake);
|
|
145
|
+
return wake;
|
|
146
|
+
}
|
|
147
|
+
armBackstop() {
|
|
148
|
+
if (this.options.backstopMs !== undefined) {
|
|
149
|
+
this.arm(this.options.backstopMs);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
clearWakes() {
|
|
153
|
+
for (const wake of this.wakes) {
|
|
154
|
+
this.scheduler.clearTimeout(wake.handle);
|
|
155
|
+
}
|
|
156
|
+
this.wakes.clear();
|
|
157
|
+
this.coalesceWake = undefined;
|
|
158
|
+
}
|
|
159
|
+
fire(wake) {
|
|
160
|
+
if (!this.wakes.delete(wake)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (wake === this.coalesceWake) {
|
|
164
|
+
this.coalesceWake = undefined;
|
|
165
|
+
}
|
|
166
|
+
if (this.running) {
|
|
167
|
+
this.rerun = true;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this.clearWakes();
|
|
171
|
+
this.rerun = false;
|
|
172
|
+
this.running = true;
|
|
173
|
+
void this.run();
|
|
174
|
+
}
|
|
175
|
+
async run() {
|
|
176
|
+
try {
|
|
177
|
+
await this.onChange();
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
log('%s: change run failed: %O', this.options.label, err);
|
|
181
|
+
}
|
|
182
|
+
this.running = false;
|
|
183
|
+
this.settled();
|
|
184
|
+
}
|
|
185
|
+
settled() {
|
|
186
|
+
if (this.disposed) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (this.rerun) {
|
|
190
|
+
this.coalesceWake ?? (this.coalesceWake = this.arm(this.coalesceMs));
|
|
191
|
+
}
|
|
192
|
+
this.armBackstop();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function fullTableWatch(qualifiedTable) {
|
|
196
|
+
const [schema, table, ...rest] = qualifiedTable.split('.');
|
|
197
|
+
if (!schema || !table || rest.length > 0) {
|
|
198
|
+
throw new Error(`watchTables: '${qualifiedTable}' is not a qualified 'Schema.Table' name`);
|
|
199
|
+
}
|
|
200
|
+
return { table: { schema, table }, columns: 'all', scope: { kind: 'full' } };
|
|
201
|
+
}
|
|
202
|
+
/** Quereus's own matching key for a watched table. */
|
|
203
|
+
function tableKey(watch) {
|
|
204
|
+
return `${watch.table.schema}.${watch.table.table}`.toLowerCase();
|
|
205
|
+
}
|
|
206
|
+
function isThenable(value) {
|
|
207
|
+
return typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=table-change-watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-change-watch.js","sourceRoot":"","sources":["../src/table-change-watch.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The timer seam shared by the
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The timer seam shared by the loops that re-arm themselves one step at a time — the
|
|
3
|
+
* membership reconciler's retry ladder (`strand-membership-reconciler.ts`) — and by the table
|
|
4
|
+
* change subscription's coalescing, trigger and backstop timers (`table-change-watch.ts`), on
|
|
5
|
+
* which the first-sync write gate's probes run (`strand-first-sync-gate.ts`). `setTimeout`
|
|
5
6
|
* rather than a repeating interval because the next step is armed only once the previous
|
|
6
7
|
* one settles, so a slow network read never stacks steps behind it, and because the delay
|
|
7
8
|
* can differ per step.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The timer seam shared by the
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The timer seam shared by the loops that re-arm themselves one step at a time — the
|
|
3
|
+
* membership reconciler's retry ladder (`strand-membership-reconciler.ts`) — and by the table
|
|
4
|
+
* change subscription's coalescing, trigger and backstop timers (`table-change-watch.ts`), on
|
|
5
|
+
* which the first-sync write gate's probes run (`strand-first-sync-gate.ts`). `setTimeout`
|
|
5
6
|
* rather than a repeating interval because the next step is armed only once the previous
|
|
6
7
|
* one settles, so a slow network read never stacks steps behind it, and because the delay
|
|
7
8
|
* can differ per step.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout-scheduler.js","sourceRoot":"","sources":["../src/timeout-scheduler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"timeout-scheduler.js","sourceRoot":"","sources":["../src/timeout-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,uBAAuB,GAAqB;IACvD,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACjC,MAAiC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAA4C,CAAC;CACrF,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ConnectionGater, Libp2p, PeerId, PrivateKey } from '@libp2p/interface';
|
|
2
2
|
import type { CircuitRelayServerInit } from '@libp2p/circuit-relay-v2';
|
|
3
3
|
import type { IRawStorage, Libp2pConnectionMonitorInit, Libp2pTransports, NoiseCryptoInterface } from '@optimystic/db-p2p';
|
|
4
|
-
import type { IPeerNetwork, IRepo } from '@optimystic/db-core';
|
|
4
|
+
import type { CollectionChangeListener, IPeerNetwork, IRepo } from '@optimystic/db-core';
|
|
5
5
|
import type { PeerJoinBackfillConfig } from './peer-join-backfill.js';
|
|
6
6
|
import type { StrandReactivityConfig } from './strand-reactivity.js';
|
|
7
7
|
import type { StrandRevocationEnforcementConfig } from './strand-revocation-enforcer.js';
|
|
@@ -18,6 +18,7 @@ import type { EnrolledMachineStore } from './enrolled-machine-store.js';
|
|
|
18
18
|
import type { JoinedStrandStore } from './joined-strand-store.js';
|
|
19
19
|
import type { PushNotifier } from './push-notifier.js';
|
|
20
20
|
import type { CadreInviteSignedFields, OwnerGrantSignedFields, RevocableTable } from './control-authorization.js';
|
|
21
|
+
import type { FoundingInvite } from './control-database.js';
|
|
21
22
|
import type { CadreInvitation } from './cadre-invite-protocol.js';
|
|
22
23
|
import type { OwnerGrantBundle } from './owner-grant-protocol.js';
|
|
23
24
|
import type { RedemptionAddressOutcome } from './member-redemption.js';
|
|
@@ -38,6 +39,23 @@ export interface Libp2pNodeWithRepo extends Libp2p {
|
|
|
38
39
|
* backfill (`peer-join-backfill.ts`) logs once and stays inert when it is absent.
|
|
39
40
|
*/
|
|
40
41
|
keyNetwork?: IPeerNetwork;
|
|
42
|
+
/**
|
|
43
|
+
* The node's block store, attached by the same factory (Optimystic's
|
|
44
|
+
* `OptimysticNodeAttachments.storageRepo`), narrowed to the storage-wide change feed: it
|
|
45
|
+
* fires for every block that lands in this node's store, a push or a commit. Optional for
|
|
46
|
+
* the same reason as `keyNetwork`; the first-sync gate (`strand-first-sync-gate.ts`) wakes
|
|
47
|
+
* on it, and logs once and probes on its backstop alone when it is absent.
|
|
48
|
+
*/
|
|
49
|
+
storageRepo?: {
|
|
50
|
+
onAnyCollectionChange(listener: CollectionChangeListener): () => void;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Tells the node a peer holds a block at a revision or later (Optimystic's
|
|
54
|
+
* `OptimysticNodeAttachments.reportBlockHeld`), so its under-replication drain and rebalance
|
|
55
|
+
* monitor do not push that peer the same block again. Optional for the same reason as
|
|
56
|
+
* `keyNetwork`; the join catch-up (`peer-join-backfill.ts`) reports each block a peer accepted.
|
|
57
|
+
*/
|
|
58
|
+
reportBlockHeld?: (blockId: string, rev: number, peerId: string) => Promise<void>;
|
|
41
59
|
}
|
|
42
60
|
/**
|
|
43
61
|
* Node profile determines storage participation
|
|
@@ -546,8 +564,13 @@ export interface NetworkConfig {
|
|
|
546
564
|
*/
|
|
547
565
|
targetDegree?: number;
|
|
548
566
|
/**
|
|
549
|
-
*
|
|
550
|
-
*
|
|
567
|
+
* The reconcile pass's backstop in ms: a pass runs this long after the last one
|
|
568
|
+
* settled when nothing has started one sooner (a membership table change, a sibling
|
|
569
|
+
* connecting or disconnecting, an address change, a redial or strand-address wake).
|
|
570
|
+
* It also caps the redial ladder. Defaults to
|
|
571
|
+
* {@link DEFAULT_CONTROL_COHORT_RECONCILE_MS} (2 minutes). The membership rows view's
|
|
572
|
+
* maximum age (`MEMBERSHIP_VIEW_MAX_AGE_MS`, 15 s) does not follow this field: a backstop
|
|
573
|
+
* longer than it reads storage on every backstop pass, a shorter one every 15 s at most.
|
|
551
574
|
*/
|
|
552
575
|
reconcileMs?: number;
|
|
553
576
|
/**
|
|
@@ -615,10 +638,10 @@ export interface NetworkConfig {
|
|
|
615
638
|
* pinging node's monitor sees the reset and aborts the connection exactly as it would on its
|
|
616
639
|
* own timeout. So this deadline is only real up to the handler timeout of the node being
|
|
617
640
|
* pinged, which has to stay above it. The deployed relay sets its handler to 60 s for that
|
|
618
|
-
* reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's
|
|
619
|
-
*
|
|
620
|
-
* (`
|
|
621
|
-
*
|
|
641
|
+
* reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's,
|
|
642
|
+
* which derives it from this object: twice `pingTimeout.maxTimeout`, so 60 s
|
|
643
|
+
* (`NodeOptions.pingHandlerTimeoutMs`, unset here, since Optimystic 1.16.0). An app that
|
|
644
|
+
* replaces this object without a `pingTimeout` gets `@libp2p/ping`'s 10 s handler back.
|
|
622
645
|
*
|
|
623
646
|
* WHAT THE LINK NEEDS OF IT. One ping opens a fresh stream and echoes over it: a protocol
|
|
624
647
|
* negotiation plus the echo, two link round trips by `link-budget.ts`'s counts (a `newStream`
|
|
@@ -783,10 +806,11 @@ export interface CadreNodeConfig {
|
|
|
783
806
|
strandClusterSize?: number;
|
|
784
807
|
/**
|
|
785
808
|
* Tuning for the strand peer-join block catch-up (see `peer-join-backfill.ts`):
|
|
786
|
-
* when a strand's libp2p node connects to a peer
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
*
|
|
809
|
+
* when a strand's libp2p node connects (or reconnects) to a peer, every
|
|
810
|
+
* block in the strand's own raw store that the peer is not
|
|
811
|
+
* known to hold is pushed to it, each block once per peer, so a machine that
|
|
812
|
+
* joined the strand after blocks were committed still ends up physically
|
|
813
|
+
* holding them. Applies to every strand this node starts.
|
|
790
814
|
* Omit for the defaults (`DEFAULT_PEER_JOIN_BACKFILL`); `{ enabled: false }`
|
|
791
815
|
* restores the no-backfill behaviour. The control network runs its own,
|
|
792
816
|
* separately tuned catch-up — see {@link controlBackfill}.
|
|
@@ -813,13 +837,14 @@ export interface CadreNodeConfig {
|
|
|
813
837
|
/**
|
|
814
838
|
* Tuning for the CONTROL network's peer-join block catch-up (same module,
|
|
815
839
|
* `peer-join-backfill.ts`, wired in `CadreNode.start`): when the control
|
|
816
|
-
* libp2p node connects to an AUTHORIZED party member
|
|
817
|
-
*
|
|
818
|
-
* it. This is what makes a control
|
|
819
|
-
*
|
|
820
|
-
* that joined later — without it,
|
|
821
|
-
* the affected tables as empty.
|
|
822
|
-
*
|
|
840
|
+
* libp2p node connects (or reconnects) to an AUTHORIZED party member, every
|
|
841
|
+
* block in the control database's own raw store that the
|
|
842
|
+
* member is not known to hold is pushed to it. This is what makes a control
|
|
843
|
+
* block committed while the writer was alone (collection headers written at
|
|
844
|
+
* genesis above all) physically reach members that joined later — without it,
|
|
845
|
+
* such a member that restarts offline reads the affected tables as empty.
|
|
846
|
+
* Pushes are gated on the node's authorized-member snapshot (the rule
|
|
847
|
+
* `CadreNode.isAuthorizedMember` reads), checked at push time, because the control
|
|
823
848
|
* network's inbound gate deliberately admits non-members in several states
|
|
824
849
|
* (seed delivery, enrollment, bootstrap/relay peers). Omit for the defaults
|
|
825
850
|
* (`DEFAULT_PEER_JOIN_BACKFILL`); `{ enabled: false }` disables it.
|
|
@@ -831,9 +856,10 @@ export interface CadreNodeConfig {
|
|
|
831
856
|
* starts: each strand node materializes the peer ids bound to REMOVED members
|
|
832
857
|
* (orphaned `Strand.MemberPeer` rows) and refuses them at the stream and
|
|
833
858
|
* connection layers, so removing a party cuts the network to its machines
|
|
834
|
-
* rather than only deleting its row. Open strands never arm it.
|
|
835
|
-
*
|
|
836
|
-
*
|
|
859
|
+
* rather than only deleting its row. Open strands never arm it. Each strand
|
|
860
|
+
* re-reads its membership when `Strand.Member` or `Strand.MemberPeer` changes,
|
|
861
|
+
* and at the latest every `DEFAULT_REVOCATION_BACKSTOP_MS`. Omit for the
|
|
862
|
+
* defaults; `{ enabled: false }` restores the pre-existing behaviour.
|
|
837
863
|
*/
|
|
838
864
|
strandRevocationEnforcement?: StrandRevocationEnforcementConfig;
|
|
839
865
|
/**
|
|
@@ -845,9 +871,8 @@ export interface CadreNodeConfig {
|
|
|
845
871
|
* background without blocking bring-up. `pollIntervalMs` is the IDLE cadence
|
|
846
872
|
* (how often a machine nobody has admitted re-checks) and the cap of the
|
|
847
873
|
* unfinished-join retry ladder, NOT the retry cadence itself — an unfinished
|
|
848
|
-
* join retries from 1 s, doubling. Omit for the defaults
|
|
849
|
-
* {
|
|
850
|
-
* `{ enabled: false }` disarms the loop — for test fixtures that hand-drive
|
|
874
|
+
* join retries from 1 s, doubling. Omit for the defaults
|
|
875
|
+
* (`DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS`); `{ enabled: false }` disarms the loop — for test fixtures that hand-drive
|
|
851
876
|
* the membership writers and assert exact row sets.
|
|
852
877
|
*/
|
|
853
878
|
strandMembershipReconciliation?: StrandMembershipReconciliationConfig;
|
|
@@ -856,21 +881,27 @@ export interface CadreNodeConfig {
|
|
|
856
881
|
* (`strand-first-sync-gate.ts`), applied to every strand this node launches as a
|
|
857
882
|
* non-founder: the strand's database is withheld from the app (`StrandInstance.database`
|
|
858
883
|
* unset, status `'syncing'`) until the strand's `Strand.Header` has been received from
|
|
859
|
-
* another member and every `App` table has been read once, because a
|
|
860
|
-
* to a table it has never fetched creates a private copy of it that
|
|
884
|
+
* another member and every `Strand` and `App` table has been read once, because a
|
|
885
|
+
* machine that writes to a table it has never fetched creates a private copy of it that
|
|
886
|
+
* never merges.
|
|
861
887
|
* `timeoutMs` bounds how long {@link CadreNode.addStrand} waits before rejecting with
|
|
862
|
-
* `StrandAwaitingFirstSyncError` (retryable — the launch stays up and keeps probing)
|
|
863
|
-
*
|
|
888
|
+
* `StrandAwaitingFirstSyncError` (retryable — the launch stays up and keeps probing).
|
|
889
|
+
* While gated the machine probes whenever data lands in the strand's store, and
|
|
890
|
+
* `backstopMs` after its last probe when none has. Omit for the defaults (`DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`,
|
|
864
891
|
* sized for a machine re-attaching through a relay on a slow link, the slowest attach
|
|
865
892
|
* measured; that constant's doc comment carries the number, the measurement behind it and
|
|
866
|
-
* what the budget costs, and is the only copy of them; `
|
|
867
|
-
*
|
|
893
|
+
* what the budget costs, and is the only copy of them; `DEFAULT_STRAND_FIRST_SYNC_BACKSTOP_MS`,
|
|
894
|
+
* 2 s). There is deliberately no way to disable the gate: a machine whose store already
|
|
868
895
|
* holds the Header is never gated, so nothing that works today is blocked by it.
|
|
869
896
|
*/
|
|
870
897
|
strandFirstSync?: StrandFirstSyncConfig;
|
|
871
898
|
/** Hibernation configuration */
|
|
872
899
|
hibernation?: HibernationConfig;
|
|
873
|
-
/**
|
|
900
|
+
/**
|
|
901
|
+
* The strand watcher's backstop in ms (default 300000): the longest it goes without
|
|
902
|
+
* re-reading `Strand` and `JoinedStrand` when no change to them is heard. It otherwise runs
|
|
903
|
+
* on a change. Also caps the first step of its retry ladders, which is 5 s otherwise.
|
|
904
|
+
*/
|
|
874
905
|
strandWatchInterval?: number;
|
|
875
906
|
/**
|
|
876
907
|
* Require a valid author signature on every sApp schema before a strand is
|
|
@@ -1363,6 +1394,14 @@ export interface FoundStrandConfig {
|
|
|
1363
1394
|
memberPrivateKey?: string;
|
|
1364
1395
|
/** sApp configuration the hosting application provides, as for {@link StrandConfig}. */
|
|
1365
1396
|
sAppConfig: SAppConfig;
|
|
1397
|
+
/**
|
|
1398
|
+
* A formation invitation to publish bound to this strand, for the app `sAppConfig.id`, in the
|
|
1399
|
+
* same control transaction and owner approval as the strand row. Mint it first with
|
|
1400
|
+
* `createOpenInvitation` (its `expiration` is `expiresAtMs`). When the strand is already
|
|
1401
|
+
* published the invitation is published on its own, under its own approval, as
|
|
1402
|
+
* `publishFormationInvite` would; one already stored under `token` for this strand is kept.
|
|
1403
|
+
*/
|
|
1404
|
+
formationInvite?: Omit<FoundingInvite, 'sAppId'>;
|
|
1366
1405
|
/**
|
|
1367
1406
|
* Forwarded to the attach ({@link StrandConfig.awaitFirstSync}). Matters only when the
|
|
1368
1407
|
* call ATTACHES rather than founds — the stored row was published by a sibling machine
|
|
@@ -1682,10 +1721,10 @@ export interface CadreNodeEvents {
|
|
|
1682
1721
|
* row is the product of the formation's consent, so claiming it needs no second one.
|
|
1683
1722
|
*
|
|
1684
1723
|
* **Fired once per strand per session, and it can fire before your listener is
|
|
1685
|
-
* attached.** The strand watcher's first
|
|
1724
|
+
* attached.** The strand watcher's first run is inside `CadreNode.start()`
|
|
1686
1725
|
* (100 ms after the watcher starts), so every strand already stored for this
|
|
1687
1726
|
* party is normally offered while the embedding app is still inside its own
|
|
1688
|
-
* `start()` await. The watcher then records the strand as seen and no later
|
|
1727
|
+
* `start()` await. The watcher then records the strand as seen and no later run
|
|
1689
1728
|
* re-offers it, so a listener attached a moment late misses those strands for the
|
|
1690
1729
|
* life of the process.
|
|
1691
1730
|
*
|
|
@@ -1746,10 +1785,17 @@ export interface CadreNodeEvents {
|
|
|
1746
1785
|
partyId: string;
|
|
1747
1786
|
peerId: string;
|
|
1748
1787
|
};
|
|
1749
|
-
/**
|
|
1788
|
+
/**
|
|
1789
|
+
* Emitted when a seed delivered over the seed protocol has been verified, merged and
|
|
1790
|
+
* acknowledged to its sender. The dials of its preferred peers follow this event, so it
|
|
1791
|
+
* waits on no connection. `dialTargets` are those peers (`seedDialTargets`): an embedder
|
|
1792
|
+
* that restarts into another party's stores carries them there, since this node retained
|
|
1793
|
+
* them in its own.
|
|
1794
|
+
*/
|
|
1750
1795
|
'seed:applied': {
|
|
1751
1796
|
partyId: string;
|
|
1752
1797
|
peersAdded: number;
|
|
1798
|
+
dialTargets: SeedDialTarget[];
|
|
1753
1799
|
};
|
|
1754
1800
|
/** Emitted when seed application fails */
|
|
1755
1801
|
'seed:error': {
|
|
@@ -1761,10 +1807,10 @@ export interface CadreNodeEvents {
|
|
|
1761
1807
|
* valid claim proof has been recorded (`claim.record`) and has durably anchored its
|
|
1762
1808
|
* signer as this node's owner. Carries the claim's party beside the owner key: the
|
|
1763
1809
|
* party this node should serve from now on, which its own config did not know. Fires
|
|
1764
|
-
* BEFORE the seed is acknowledged to the claimant
|
|
1765
|
-
*
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
1810
|
+
* BEFORE the seed is acknowledged to the claimant and its peers merged, so it precedes
|
|
1811
|
+
* that seed's `seed:applied`; an embedder that restarts into the claimed party on this
|
|
1812
|
+
* event should wait for that `seed:applied` (or `seed:error`) so the acknowledgement is
|
|
1813
|
+
* not lost with the node. `CadreNode.isAwaitingClaim` is the
|
|
1768
1814
|
* matching predicate for a reader that subscribed too late to see the event.
|
|
1769
1815
|
*/
|
|
1770
1816
|
'claim:accepted': NodeClaimRecord;
|
|
@@ -2007,7 +2053,7 @@ export interface CadreOwnerKey {
|
|
|
2007
2053
|
}
|
|
2008
2054
|
/** Why `CadreNode.addOwner` refuses by name (`OwnerKeyAddRefusedError.code`). */
|
|
2009
2055
|
export type OwnerKeyAddRefusal =
|
|
2010
|
-
/** No
|
|
2056
|
+
/** No sibling reachable (`CadreNode.hasReachableControlSibling`) while the party has other members: the row would stay on this node, and nothing re-issues an `OwnerKey` add. */
|
|
2011
2057
|
'alone';
|
|
2012
2058
|
/** Why `CadreNode.removeMachine` refuses by name (`MachineRemovalRefusedError.code`). */
|
|
2013
2059
|
export type MachineRemovalRefusal =
|
|
@@ -2044,7 +2090,7 @@ export type OwnerKeyRemovalRefusal =
|
|
|
2044
2090
|
| 'last-owner'
|
|
2045
2091
|
/** The key is this node's own owner key: an owner cannot sign its own row's removal (`A.Key <> old.Key`), so ask another owner key. */
|
|
2046
2092
|
| 'self'
|
|
2047
|
-
/** A re-vouch of an `OwnerKey` row was asked for while this node
|
|
2093
|
+
/** A re-vouch of an `OwnerKey` row was asked for while this node reaches no sibling and the party has other members: the re-added row has no re-issue path. Decided at call time, so never planned. */
|
|
2048
2094
|
| 'alone';
|
|
2049
2095
|
/** The rows sharing the owner keys' voucher column that a removed owner key strands unless re-vouched first. */
|
|
2050
2096
|
export interface OwnerKeyDependents {
|
|
@@ -2148,8 +2194,8 @@ export interface OwnerGrantRedemptionResult {
|
|
|
2148
2194
|
* `ControlDatabase.queryRevocations`: the identity triple plus the `ReissuedAt`
|
|
2149
2195
|
* counter an owner bumps (`ControlDatabase.reissueRevocations`) to re-write — and
|
|
2150
2196
|
* therefore re-broadcast — a tombstone that committed while the node was alone.
|
|
2151
|
-
* Never the ledger marker: that row names no guarded table, and
|
|
2152
|
-
* skips it, which is what keeps `tableName` a {@link RevocableTable}.
|
|
2197
|
+
* Never the ledger marker an older party may hold: that row names no guarded table, and
|
|
2198
|
+
* `queryRevocations` skips it, which is what keeps `tableName` a {@link RevocableTable}.
|
|
2153
2199
|
*/
|
|
2154
2200
|
export interface RevocationRow {
|
|
2155
2201
|
/** Which guarded table's stamp was retired. */
|
|
@@ -2165,13 +2211,6 @@ export interface RevocationRow {
|
|
|
2165
2211
|
/** That owner's signature over the tombstone's `'remove'` digest (`Revocation.SignerSig`). */
|
|
2166
2212
|
signerSig: string;
|
|
2167
2213
|
}
|
|
2168
|
-
/**
|
|
2169
|
-
* Outcome of `ControlDatabase.openRevocationLedger`: `'opened'` when this call filed the
|
|
2170
|
-
* singleton `Revocation` ledger marker, `'already-open'` when it was already filed (seen
|
|
2171
|
-
* by the in-lock guard, or filed first by another owner and refused on the primary key).
|
|
2172
|
-
* Either way the marker exists afterwards.
|
|
2173
|
-
*/
|
|
2174
|
-
export type RevocationLedgerOpenResult = 'opened' | 'already-open';
|
|
2175
2214
|
/**
|
|
2176
2215
|
* Outcome of {@link CadreNode.registerSelf}, surfaced so callers (e.g. the CLI
|
|
2177
2216
|
* `--owner` branch) can log what happened:
|
|
@@ -2347,6 +2386,8 @@ export interface SeedPeer {
|
|
|
2347
2386
|
*/
|
|
2348
2387
|
preferred: boolean;
|
|
2349
2388
|
}
|
|
2389
|
+
/** A peer a seed nominates for dialing, and the addresses to dial it at (`seedDialTargets`). */
|
|
2390
|
+
export type SeedDialTarget = Pick<SeedPeer, 'peerId' | 'multiaddrs'>;
|
|
2350
2391
|
/**
|
|
2351
2392
|
* Control network seed for bootstrapping new nodes.
|
|
2352
2393
|
* Pre-populates a new node's cache to solve the cold-start problem:
|
package/dist/types.js
CHANGED
|
@@ -71,10 +71,10 @@ export const HIBERNATION_TIMEOUTS = {
|
|
|
71
71
|
* pinging node's monitor sees the reset and aborts the connection exactly as it would on its
|
|
72
72
|
* own timeout. So this deadline is only real up to the handler timeout of the node being
|
|
73
73
|
* pinged, which has to stay above it. The deployed relay sets its handler to 60 s for that
|
|
74
|
-
* reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's
|
|
75
|
-
*
|
|
76
|
-
* (`
|
|
77
|
-
*
|
|
74
|
+
* reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's,
|
|
75
|
+
* which derives it from this object: twice `pingTimeout.maxTimeout`, so 60 s
|
|
76
|
+
* (`NodeOptions.pingHandlerTimeoutMs`, unset here, since Optimystic 1.16.0). An app that
|
|
77
|
+
* replaces this object without a `pingTimeout` gets `@libp2p/ping`'s 10 s handler back.
|
|
78
78
|
*
|
|
79
79
|
* WHAT THE LINK NEEDS OF IT. One ping opens a fresh stream and echoes over it: a protocol
|
|
80
80
|
* negotiation plus the echo, two link round trips by `link-budget.ts`'s counts (a `newStream`
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0GA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;AA6eF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,CAAC;IACtD,uFAAuF;IACvF,mFAAmF;IACnF,YAAY,EAAE,KAAM;IACpB,kFAAkF;IAClF,kFAAkF;IAClF,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,KAAM,EAAE,UAAU,EAAE,KAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAmBzC;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AAYnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AAEvC;;;;;GAKG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serfab/cadre-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core library for Sereus cadre nodes - manages control networks and strand participation",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"@libp2p/logger": "^6.2.13",
|
|
91
91
|
"@libp2p/peer-store": "^12.0.28",
|
|
92
92
|
"@libp2p/webrtc": "6.0.33",
|
|
93
|
-
"@optimystic/db-p2p-storage-fs": "^1.
|
|
93
|
+
"@optimystic/db-p2p-storage-fs": "^1.18.0",
|
|
94
94
|
"@types/debug": "^4.1.12",
|
|
95
95
|
"@types/node": "^25.0.10",
|
|
96
96
|
"@vitest/coverage-v8": "^4.0.17",
|
|
@@ -111,12 +111,12 @@
|
|
|
111
111
|
"@multiformats/multiaddr": "^13.0.3",
|
|
112
112
|
"@noble/ciphers": "^2.0.1",
|
|
113
113
|
"@noble/hashes": "^2.0.0",
|
|
114
|
-
"@optimystic/db-core": "^1.
|
|
115
|
-
"@optimystic/db-p2p": "^1.
|
|
116
|
-
"@optimystic/quereus-plugin-crypto": "^1.
|
|
117
|
-
"@optimystic/quereus-plugin-optimystic": "^1.
|
|
118
|
-
"@quereus/quereus": "^4.20.
|
|
119
|
-
"@serfab/quereus-plugin-sereus": "^1.
|
|
114
|
+
"@optimystic/db-core": "^1.18.0",
|
|
115
|
+
"@optimystic/db-p2p": "^1.18.0",
|
|
116
|
+
"@optimystic/quereus-plugin-crypto": "^1.18.0",
|
|
117
|
+
"@optimystic/quereus-plugin-optimystic": "^1.18.0",
|
|
118
|
+
"@quereus/quereus": "^4.20.3",
|
|
119
|
+
"@serfab/quereus-plugin-sereus": "^1.18.0",
|
|
120
120
|
"debug": "^4.4.3",
|
|
121
121
|
"libp2p": "^3.3.11",
|
|
122
122
|
"uint8arrays": "^6.1.1"
|