@net-mesh/sdk 0.19.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/dist/deck.js ADDED
@@ -0,0 +1,717 @@
1
+ "use strict";
2
+ /**
3
+ * Deck SDK — operator-side TypeScript wrapper.
4
+ *
5
+ * Sits on top of the napi-rs binding at `@net-mesh/core`. Adds:
6
+ *
7
+ * - {@link DeckSdkError} typed Error subclass that parses the
8
+ * substrate `<<deck-sdk-kind:KIND>>MSG` envelope.
9
+ * - Auto-JSON-parsing for `status()` and `snapshots()`.
10
+ * - `AsyncIterable<unknown>` (parsed `MeshOsSnapshot` JSON) / `AsyncIterable<StatusSummary>`
11
+ * wrappers over the raw `nextSnapshot()` / `nextSummary()`
12
+ * methods.
13
+ *
14
+ * Slice 1 ships client + admin (all 9 methods) + snapshot/status
15
+ * streams + operator identity. Audit / logs / failures land in
16
+ * slice 2; ICE in slice 3.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { MeshOsDaemonSdk } from '@net-mesh/sdk/meshos';
21
+ * import { DeckClient, OperatorIdentity } from '@net-mesh/sdk/deck';
22
+ *
23
+ * const sdk = await MeshOsDaemonSdk.start();
24
+ * const identity = OperatorIdentity.generate();
25
+ * const client = await DeckClient.fromMeshos(sdk, identity);
26
+ *
27
+ * const commit = await client.admin.enterMaintenance(0xABCDn, 600_000n);
28
+ * console.log(`committed at ${commit.commitId} kind=${commit.eventKind}`);
29
+ *
30
+ * for await (const snap of client.snapshots()) {
31
+ * // snap is a parsed object
32
+ * break;
33
+ * }
34
+ *
35
+ * await sdk.shutdown();
36
+ * ```
37
+ */
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AdminVerifier = exports.OperatorRegistry = exports.SimulatedIceProposal = exports.IceProposal = exports.IceCommands = exports.AuditQuery = exports.DeckClient = exports.AdminCommands = exports.OperatorIdentity = exports.DeckSdkError = void 0;
40
+ const core_1 = require("@net-mesh/core");
41
+ Object.defineProperty(exports, "OperatorIdentity", { enumerable: true, get: function () { return core_1.OperatorIdentity; } });
42
+ // ----------------------------------------------------------------------------
43
+ // Typed error envelope (mirrors MeshOS SDK's MeshOsSdkError)
44
+ // ----------------------------------------------------------------------------
45
+ class DeckSdkError extends Error {
46
+ kind;
47
+ constructor(kind, message) {
48
+ super(`<<deck-sdk-kind:${kind}>>${message}`);
49
+ this.name = 'DeckSdkError';
50
+ this.kind = kind;
51
+ Object.setPrototypeOf(this, DeckSdkError.prototype);
52
+ }
53
+ static fromCaught(err) {
54
+ if (err instanceof DeckSdkError)
55
+ return err;
56
+ if (!(err instanceof Error)) {
57
+ return new Error(String(err));
58
+ }
59
+ const parsed = parseEnvelope(err.message);
60
+ if (!parsed)
61
+ return err;
62
+ return new DeckSdkError(parsed.kind, parsed.body);
63
+ }
64
+ }
65
+ exports.DeckSdkError = DeckSdkError;
66
+ function parseEnvelope(message) {
67
+ const marker = '<<deck-sdk-kind:';
68
+ const start = message.indexOf(marker);
69
+ if (start === -1)
70
+ return null;
71
+ const kindStart = start + marker.length;
72
+ const end = message.indexOf('>>', kindStart);
73
+ if (end === -1)
74
+ return null;
75
+ return { kind: message.slice(kindStart, end), body: message.slice(end + 2) };
76
+ }
77
+ async function rethrowAsync(fn) {
78
+ try {
79
+ return await fn();
80
+ }
81
+ catch (e) {
82
+ throw DeckSdkError.fromCaught(e);
83
+ }
84
+ }
85
+ // ----------------------------------------------------------------------------
86
+ // AdminCommands
87
+ // ----------------------------------------------------------------------------
88
+ class AdminCommands {
89
+ raw;
90
+ constructor(raw) {
91
+ this.raw = raw;
92
+ }
93
+ async drain(node, drainForMs) {
94
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.drain(node, drainForMs)));
95
+ }
96
+ async enterMaintenance(node, drainForMs) {
97
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.enterMaintenance(node, drainForMs)));
98
+ }
99
+ async exitMaintenance(node) {
100
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.exitMaintenance(node)));
101
+ }
102
+ async cordon(node) {
103
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.cordon(node)));
104
+ }
105
+ async uncordon(node) {
106
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.uncordon(node)));
107
+ }
108
+ async dropReplicas(node, chains) {
109
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.dropReplicas(node, chains)));
110
+ }
111
+ async invalidatePlacement(node) {
112
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.invalidatePlacement(node)));
113
+ }
114
+ async restartAllDaemons(node) {
115
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.restartAllDaemons(node)));
116
+ }
117
+ async clearAvoidList(node) {
118
+ return chainCommitFromJs(await rethrowAsync(() => this.raw.clearAvoidList(node)));
119
+ }
120
+ }
121
+ exports.AdminCommands = AdminCommands;
122
+ function chainCommitFromJs(c) {
123
+ return {
124
+ commitId: c.commitId,
125
+ operatorId: c.operatorId,
126
+ eventKind: c.eventKind,
127
+ committedAtMs: c.committedAtMs,
128
+ };
129
+ }
130
+ function statusSummaryFromJs(s) {
131
+ return {
132
+ peers: { ...s.peers },
133
+ daemons: { ...s.daemons },
134
+ replicaChains: s.replicaChains,
135
+ avoidListEntries: s.avoidListEntries,
136
+ recentlyEmittedCount: s.recentlyEmittedCount,
137
+ recentFailureCount: s.recentFailureCount,
138
+ adminAuditRingDepth: s.adminAuditRingDepth,
139
+ freezeRemainingMs: s.freezeRemainingMs ?? null,
140
+ localMaintenanceActive: s.localMaintenanceActive,
141
+ };
142
+ }
143
+ // ----------------------------------------------------------------------------
144
+ // Streams — AsyncIterable wrappers around the raw napi handles
145
+ // ----------------------------------------------------------------------------
146
+ /**
147
+ * Wrap a raw napi snapshot stream as `AsyncIterable<unknown>` (parsed `MeshOsSnapshot` JSON).
148
+ * The napi side emits JSON-encoded snapshots; we parse here so
149
+ * consumers see a native object. Returns `null` from `nextSnapshot()`
150
+ * when the underlying stream closes.
151
+ */
152
+ function snapshotsToAsyncIterable(raw) {
153
+ return {
154
+ [Symbol.asyncIterator]() {
155
+ return {
156
+ async next() {
157
+ try {
158
+ const json = await raw.nextSnapshot();
159
+ if (json === null)
160
+ return { value: undefined, done: true };
161
+ return { value: JSON.parse(json), done: false };
162
+ }
163
+ catch (e) {
164
+ throw DeckSdkError.fromCaught(e);
165
+ }
166
+ },
167
+ async return() {
168
+ await raw.close();
169
+ return { value: undefined, done: true };
170
+ },
171
+ };
172
+ },
173
+ async close() {
174
+ await raw.close();
175
+ },
176
+ };
177
+ }
178
+ function statusSummariesToAsyncIterable(raw) {
179
+ return {
180
+ [Symbol.asyncIterator]() {
181
+ return {
182
+ async next() {
183
+ try {
184
+ const item = await raw.nextSummary();
185
+ if (item === null)
186
+ return { value: undefined, done: true };
187
+ return { value: statusSummaryFromJs(item), done: false };
188
+ }
189
+ catch (e) {
190
+ throw DeckSdkError.fromCaught(e);
191
+ }
192
+ },
193
+ async return() {
194
+ await raw.close();
195
+ return { value: undefined, done: true };
196
+ },
197
+ };
198
+ },
199
+ async close() {
200
+ await raw.close();
201
+ },
202
+ };
203
+ }
204
+ // ----------------------------------------------------------------------------
205
+ // DeckClient
206
+ // ----------------------------------------------------------------------------
207
+ class DeckClient {
208
+ raw;
209
+ constructor(raw) {
210
+ this.raw = raw;
211
+ }
212
+ /**
213
+ * Construct a deck client that owns a private supervisor
214
+ * runtime. Mirrors the cdylib's `net_deck_client_new`
215
+ * (operator-only mode) for consumers without a separately-
216
+ * managed `MeshOsDaemonSdk`.
217
+ *
218
+ * `operatorSeed` must be exactly 32 bytes of ed25519 seed
219
+ * material. Call `.close()` (or use `await using`) to drain
220
+ * the supervisor at end of scope; otherwise the runtime
221
+ * releases on GC.
222
+ */
223
+ static async new(operatorSeed, meshosConfig, deckConfig) {
224
+ return rethrowAsync(async () => {
225
+ const meshos = meshosConfig
226
+ ? {
227
+ thisNode: meshosConfig.thisNode,
228
+ tickIntervalMs: meshosConfig.tickIntervalMs,
229
+ eventQueueCapacity: meshosConfig.eventQueueCapacity,
230
+ actionQueueCapacity: meshosConfig.actionQueueCapacity,
231
+ }
232
+ : undefined;
233
+ const deck = deckConfig
234
+ ? {
235
+ snapshotPollIntervalMs: deckConfig.snapshotPollIntervalMs,
236
+ iceSignatureThreshold: deckConfig.iceSignatureThreshold,
237
+ }
238
+ : undefined;
239
+ const raw = await core_1.DeckClient.new(operatorSeed, meshos, deck);
240
+ return new DeckClient(raw);
241
+ });
242
+ }
243
+ /**
244
+ * Construct against a running `MeshOsDaemonSdk`. Reuses the
245
+ * SDK's tokio runtime so streams + admin commits run on the
246
+ * same supervisor scheduler.
247
+ */
248
+ static async fromMeshos(sdk, identity, config) {
249
+ return rethrowAsync(async () => {
250
+ const rawSdk = sdk.__rawNapiSdk();
251
+ const cfg = config
252
+ ? {
253
+ snapshotPollIntervalMs: config.snapshotPollIntervalMs,
254
+ iceSignatureThreshold: config.iceSignatureThreshold,
255
+ }
256
+ : undefined;
257
+ const raw = await core_1.DeckClient.fromMeshos(rawSdk, identity, cfg);
258
+ return new DeckClient(raw);
259
+ });
260
+ }
261
+ /**
262
+ * Tear down the private supervisor runtime if this client owns
263
+ * one (constructed via `DeckClient.new`). No-op for clients
264
+ * built via `fromMeshos` against an externally-managed SDK.
265
+ * Idempotent: subsequent calls return without throwing.
266
+ */
267
+ async close() {
268
+ await rethrowAsync(() => this.raw.shutdown());
269
+ }
270
+ /**
271
+ * `await using` hook so `await using deck = await DeckClient.new(...)`
272
+ * drains the supervisor at scope exit.
273
+ */
274
+ async [Symbol.asyncDispose]() {
275
+ await this.close();
276
+ }
277
+ /** Operator identity bound to this client. */
278
+ identity() {
279
+ return this.raw.identity();
280
+ }
281
+ /** Typed admin-event surface. */
282
+ get admin() {
283
+ return new AdminCommands(this.raw.admin);
284
+ }
285
+ /** Break-glass surface. Returns `IceCommands` whose factories
286
+ * produce `IceProposal`s. Each must be `.simulate()`-d
287
+ * (yielding a `SimulatedIceProposal`) before `.commit(...)`. */
288
+ get ice() {
289
+ return new IceCommands(this.raw.ice);
290
+ }
291
+ /**
292
+ * One-shot read of the latest `MeshOsSnapshot` (parsed JSON;
293
+ * `unknown` because the substrate snapshot's exact shape isn't
294
+ * mirrored in the TS surface — consumers cast or use a runtime
295
+ * validator).
296
+ */
297
+ status() {
298
+ try {
299
+ return JSON.parse(this.raw.status());
300
+ }
301
+ catch (e) {
302
+ throw DeckSdkError.fromCaught(e);
303
+ }
304
+ }
305
+ /** One-shot read of the rolled-up `StatusSummary`. */
306
+ statusSummary() {
307
+ return statusSummaryFromJs(this.raw.statusSummary());
308
+ }
309
+ /**
310
+ * Live snapshot stream as `AsyncIterable<unknown>` (parsed `MeshOsSnapshot` JSON). JSON
311
+ * parsing happens automatically.
312
+ *
313
+ * Async on the napi side because the substrate creates a
314
+ * `tokio::time::Interval` that needs a runtime context.
315
+ */
316
+ async snapshots() {
317
+ return snapshotsToAsyncIterable(await this.raw.snapshots());
318
+ }
319
+ /** Live status-summary stream. */
320
+ async statusSummaryStream() {
321
+ return statusSummariesToAsyncIterable(await this.raw.statusSummaryStream());
322
+ }
323
+ // =========================================================================
324
+ // Slice 2 — audit + logs + failures
325
+ // =========================================================================
326
+ /**
327
+ * Fluent admin-audit query builder. Chain `.recent(n)` /
328
+ * `.byOperator(id)` / `.between(start, end)` / `.forceOnly()` /
329
+ * `.since(seq)` before calling `.collect()` (eager list of
330
+ * parsed audit records) or `.stream()` (`AsyncIterable`).
331
+ */
332
+ audit() {
333
+ return new AuditQuery(this.raw.audit());
334
+ }
335
+ /**
336
+ * Subscribe to the runtime's log ring. Returns an
337
+ * `AsyncIterable<LogRecord>`. Filter fields are all optional
338
+ * — missing fields match every record.
339
+ */
340
+ async subscribeLogs(filter) {
341
+ return rethrowAsync(async () => {
342
+ const raw = await this.raw.subscribeLogs(filter ? toNapiLogFilter(filter) : null);
343
+ return logStreamToAsyncIterable(raw);
344
+ });
345
+ }
346
+ /**
347
+ * Subscribe to the executor failure ring starting at
348
+ * `sinceSeq + 1`. Pass `0n` (or omit) to start from whatever
349
+ * is still in the ring.
350
+ */
351
+ async subscribeFailures(sinceSeq) {
352
+ return rethrowAsync(async () => {
353
+ const raw = await this.raw.subscribeFailures(sinceSeq);
354
+ return failureStreamToAsyncIterable(raw);
355
+ });
356
+ }
357
+ }
358
+ exports.DeckClient = DeckClient;
359
+ function toNapiLogFilter(f) {
360
+ return {
361
+ minLevel: f.minLevel,
362
+ daemonId: f.daemonId,
363
+ nodeId: f.nodeId,
364
+ sinceSeq: f.sinceSeq,
365
+ };
366
+ }
367
+ function logRecordFromJs(r) {
368
+ return {
369
+ seq: r.seq,
370
+ tsMs: r.tsMs,
371
+ level: r.level,
372
+ daemonId: r.daemonId ?? null,
373
+ nodeId: r.nodeId ?? null,
374
+ message: r.message,
375
+ };
376
+ }
377
+ function failureRecordFromJs(r) {
378
+ return {
379
+ seq: r.seq,
380
+ source: r.source,
381
+ reason: r.reason,
382
+ recordedAtMs: r.recordedAtMs,
383
+ };
384
+ }
385
+ // ----------------------------------------------------------------------------
386
+ // Slice 2 — Log + Failure AsyncIterable wrappers
387
+ // ----------------------------------------------------------------------------
388
+ function logStreamToAsyncIterable(raw) {
389
+ return {
390
+ [Symbol.asyncIterator]() {
391
+ return {
392
+ async next() {
393
+ try {
394
+ const item = await raw.nextRecord();
395
+ if (item === null)
396
+ return { value: undefined, done: true };
397
+ return { value: logRecordFromJs(item), done: false };
398
+ }
399
+ catch (e) {
400
+ throw DeckSdkError.fromCaught(e);
401
+ }
402
+ },
403
+ async return() {
404
+ await raw.close();
405
+ return { value: undefined, done: true };
406
+ },
407
+ };
408
+ },
409
+ async close() {
410
+ await raw.close();
411
+ },
412
+ };
413
+ }
414
+ function failureStreamToAsyncIterable(raw) {
415
+ return {
416
+ [Symbol.asyncIterator]() {
417
+ return {
418
+ async next() {
419
+ try {
420
+ const item = await raw.nextRecord();
421
+ if (item === null)
422
+ return { value: undefined, done: true };
423
+ return { value: failureRecordFromJs(item), done: false };
424
+ }
425
+ catch (e) {
426
+ throw DeckSdkError.fromCaught(e);
427
+ }
428
+ },
429
+ async return() {
430
+ await raw.close();
431
+ return { value: undefined, done: true };
432
+ },
433
+ };
434
+ },
435
+ async close() {
436
+ await raw.close();
437
+ },
438
+ };
439
+ }
440
+ function auditStreamToAsyncIterable(raw) {
441
+ return {
442
+ [Symbol.asyncIterator]() {
443
+ return {
444
+ async next() {
445
+ try {
446
+ const json = await raw.nextRecord();
447
+ if (json === null)
448
+ return { value: undefined, done: true };
449
+ return { value: JSON.parse(json), done: false };
450
+ }
451
+ catch (e) {
452
+ throw DeckSdkError.fromCaught(e);
453
+ }
454
+ },
455
+ async return() {
456
+ await raw.close();
457
+ return { value: undefined, done: true };
458
+ },
459
+ };
460
+ },
461
+ async close() {
462
+ await raw.close();
463
+ },
464
+ };
465
+ }
466
+ // ----------------------------------------------------------------------------
467
+ // Slice 2 — AuditQuery fluent builder
468
+ // ----------------------------------------------------------------------------
469
+ /**
470
+ * Fluent admin-audit query builder.
471
+ *
472
+ * @example
473
+ * ```ts
474
+ * const records = await client.audit()
475
+ * .recent(100)
476
+ * .byOperator(operatorId)
477
+ * .forceOnly()
478
+ * .collect();
479
+ *
480
+ * for await (const record of (await client.audit().since(lastSeq).stream())) {
481
+ * handle(record);
482
+ * }
483
+ * ```
484
+ */
485
+ class AuditQuery {
486
+ raw;
487
+ constructor(raw) {
488
+ this.raw = raw;
489
+ }
490
+ recent(limit) {
491
+ this.raw.recent(limit);
492
+ return this;
493
+ }
494
+ byOperator(operatorId) {
495
+ this.raw.byOperator(operatorId);
496
+ return this;
497
+ }
498
+ between(startMs, endMs) {
499
+ this.raw.between(startMs, endMs);
500
+ return this;
501
+ }
502
+ forceOnly() {
503
+ this.raw.forceOnly();
504
+ return this;
505
+ }
506
+ since(seq) {
507
+ this.raw.since(seq);
508
+ return this;
509
+ }
510
+ /** Eager — returns a list of audit records (JSON-parsed into
511
+ * native objects). */
512
+ async collect() {
513
+ return rethrowAsync(async () => {
514
+ const raw = this.raw.collect();
515
+ return raw.map((s) => JSON.parse(s));
516
+ });
517
+ }
518
+ /** Async iterator over audit records. */
519
+ async stream() {
520
+ return rethrowAsync(async () => auditStreamToAsyncIterable(await this.raw.stream()));
521
+ }
522
+ }
523
+ exports.AuditQuery = AuditQuery;
524
+ function avoidScopeToJs(scope) {
525
+ switch (scope.kind) {
526
+ case 'global':
527
+ return { kind: 'global', node: undefined, peer: undefined };
528
+ case 'local':
529
+ return { kind: 'local', node: scope.node, peer: undefined };
530
+ case 'onPeer':
531
+ return { kind: 'onPeer', node: undefined, peer: scope.peer };
532
+ }
533
+ }
534
+ function operatorSignatureToJs(sig) {
535
+ return {
536
+ operatorId: sig.operatorId,
537
+ signature: sig.signature,
538
+ };
539
+ }
540
+ class IceCommands {
541
+ raw;
542
+ constructor(raw) {
543
+ this.raw = raw;
544
+ }
545
+ freezeCluster(ttlMs) {
546
+ return rethrow(() => new IceProposal(this.raw.freezeCluster(ttlMs)));
547
+ }
548
+ flushAvoidLists(scope) {
549
+ return rethrow(() => new IceProposal(this.raw.flushAvoidLists(avoidScopeToJs(scope))));
550
+ }
551
+ forceEvictReplica(chain, victim) {
552
+ return rethrow(() => new IceProposal(this.raw.forceEvictReplica(chain, victim)));
553
+ }
554
+ /** Propose force-restarting a daemon. `id` is the registry-
555
+ * local daemon id; `name` is `MeshDaemon::name()`. */
556
+ forceRestartDaemon(id, name) {
557
+ return rethrow(() => new IceProposal(this.raw.forceRestartDaemon(id, name)));
558
+ }
559
+ forceCutover(chain, target) {
560
+ return rethrow(() => new IceProposal(this.raw.forceCutover(chain, target)));
561
+ }
562
+ killMigration(migration) {
563
+ return rethrow(() => new IceProposal(this.raw.killMigration(migration)));
564
+ }
565
+ thawCluster() {
566
+ return new IceProposal(this.raw.thawCluster());
567
+ }
568
+ }
569
+ exports.IceCommands = IceCommands;
570
+ /** Pre-simulation ICE proposal. No `commit` method —
571
+ * `simulate()` must run first. */
572
+ class IceProposal {
573
+ raw;
574
+ constructor(raw) {
575
+ this.raw = raw;
576
+ }
577
+ get issuedAtMs() {
578
+ return this.raw.issuedAtMs;
579
+ }
580
+ /** Pre-execution preview. Consumes the proposal — subsequent
581
+ * `simulate()` calls throw `DeckSdkError(kind: "already_simulated")`. */
582
+ async simulate() {
583
+ return rethrowAsync(async () => new SimulatedIceProposal(await this.raw.simulate()));
584
+ }
585
+ }
586
+ exports.IceProposal = IceProposal;
587
+ /** A simulated ICE proposal. Only class exposing `commit`. */
588
+ class SimulatedIceProposal {
589
+ raw;
590
+ constructor(raw) {
591
+ this.raw = raw;
592
+ }
593
+ get issuedAtMs() {
594
+ return this.raw.issuedAtMs;
595
+ }
596
+ /** Pre-execution blast radius, parsed from the binding's JSON. */
597
+ async blastRadius() {
598
+ return rethrowAsync(async () => JSON.parse(await this.raw.blastRadius()));
599
+ }
600
+ /** Blake3 digest of the blast radius (32 bytes). */
601
+ async blastHash() {
602
+ return rethrowAsync(() => this.raw.blastHash());
603
+ }
604
+ /** Commit with operator signatures. Consumes the proposal —
605
+ * subsequent calls throw `already_committed`. */
606
+ async commit(signatures) {
607
+ return rethrowAsync(async () => {
608
+ const raw = await this.raw.commit(signatures.map(operatorSignatureToJs));
609
+ return chainCommitFromJs(raw);
610
+ });
611
+ }
612
+ }
613
+ exports.SimulatedIceProposal = SimulatedIceProposal;
614
+ // `rethrow` for sync entry points — mirrors `rethrowAsync`.
615
+ function rethrow(fn) {
616
+ try {
617
+ return fn();
618
+ }
619
+ catch (e) {
620
+ throw DeckSdkError.fromCaught(e);
621
+ }
622
+ }
623
+ // ----------------------------------------------------------------------------
624
+ // Operator-policy verifier surface — OperatorRegistry + AdminVerifier
625
+ // ----------------------------------------------------------------------------
626
+ /** Cluster operator-policy registry. Authoring tool for the
627
+ * substrate's known-operator set; offline-friendly verifier for
628
+ * bundles before invoking
629
+ * {@link SimulatedIceProposal.commit}.
630
+ *
631
+ * Mutations are thread-safe at the napi binding layer. */
632
+ class OperatorRegistry {
633
+ /** @internal — exposed so {@link AdminVerifier} can re-use the
634
+ * underlying napi handle without round-tripping the public-key
635
+ * set through JS. */
636
+ raw;
637
+ constructor(raw) {
638
+ this.raw = raw ?? new core_1.OperatorRegistry();
639
+ }
640
+ /** Insert an operator's 32-byte ed25519 public key under
641
+ * `operatorId`. */
642
+ insert(operatorId, publicKey) {
643
+ rethrow(() => this.raw.insert(operatorId, publicKey));
644
+ }
645
+ /** Register an `OperatorIdentity`'s public key under its
646
+ * derived operator id. */
647
+ register(identity) {
648
+ rethrow(() => this.raw.register(identity));
649
+ }
650
+ /** `true` iff `operatorId` is registered. */
651
+ contains(operatorId) {
652
+ return rethrow(() => this.raw.contains(operatorId));
653
+ }
654
+ /** Number of registered operators. */
655
+ get size() {
656
+ return rethrow(() => this.raw.size);
657
+ }
658
+ /** `true` iff no operators are registered. */
659
+ isEmpty() {
660
+ return rethrow(() => this.raw.isEmpty());
661
+ }
662
+ /** Verify a single signature over `payload`. Throws
663
+ * `DeckSdkError` with the substrate's stable kind discriminator
664
+ * (`not_authorized`, `signature_invalid`, etc.). */
665
+ verify(signature, payload) {
666
+ rethrow(() => this.raw.verify(operatorSignatureToJs(signature), payload));
667
+ }
668
+ /** Verify every signature in the bundle and confirm at least
669
+ * `threshold` *distinct* operator ids signed `payload`. */
670
+ verifyBundle(signatures, payload, threshold) {
671
+ rethrow(() => this.raw.verifyBundle(signatures.map(operatorSignatureToJs), payload, threshold));
672
+ }
673
+ }
674
+ exports.OperatorRegistry = OperatorRegistry;
675
+ /** Substrate-side admin commit verifier. Bundles an
676
+ * {@link OperatorRegistry} snapshot with the cluster's signature
677
+ * threshold + freshness/skew/ICE-cooldown windows. Useful for
678
+ * offline unit testing of operator-policy decisions.
679
+ *
680
+ * Constructors snapshot the registry at build time — later
681
+ * mutations on the source registry are not reflected. Rebuild
682
+ * the verifier after every policy change. */
683
+ class AdminVerifier {
684
+ raw;
685
+ constructor(raw) {
686
+ this.raw = raw;
687
+ }
688
+ /** Build a verifier with `threshold` minimum signatures and
689
+ * the substrate defaults (300s freshness, 30s future-skew,
690
+ * 300s ICE cooldown). `threshold = 0` is clamped to `1`. */
691
+ static new(registry, threshold) {
692
+ return rethrow(() => new AdminVerifier(new core_1.AdminVerifier(registry.raw, threshold)));
693
+ }
694
+ /** Build with explicit freshness + future-skew windows and
695
+ * the default ICE cooldown. */
696
+ static withFreshness(registry, threshold, freshnessWindowMs, futureSkewMs) {
697
+ return rethrow(() => new AdminVerifier(core_1.AdminVerifier.withFreshness(registry.raw, threshold, freshnessWindowMs, futureSkewMs)));
698
+ }
699
+ /** Build with every policy knob explicit. Primarily for tests
700
+ * that need a short cooldown window. */
701
+ static withFullPolicy(registry, threshold, freshnessWindowMs, futureSkewMs, iceCooldownMs) {
702
+ return rethrow(() => new AdminVerifier(core_1.AdminVerifier.withFullPolicy(registry.raw, threshold, freshnessWindowMs, futureSkewMs, iceCooldownMs)));
703
+ }
704
+ get threshold() {
705
+ return this.raw.threshold;
706
+ }
707
+ get freshnessWindowMs() {
708
+ return this.raw.freshnessWindowMs;
709
+ }
710
+ get futureSkewMs() {
711
+ return this.raw.futureSkewMs;
712
+ }
713
+ get iceCooldownMs() {
714
+ return this.raw.iceCooldownMs;
715
+ }
716
+ }
717
+ exports.AdminVerifier = AdminVerifier;