@serfab/cadre-core 0.7.1 → 0.8.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/README.md +9 -8
- package/dist/authority-key.d.ts +41 -0
- package/dist/authority-key.js +52 -0
- package/dist/authority-key.js.map +1 -0
- package/dist/cadre-node.d.ts +770 -11
- package/dist/cadre-node.js +1900 -88
- package/dist/cadre-node.js.map +1 -1
- package/dist/canonical-datetime.d.ts +33 -0
- package/dist/canonical-datetime.js +38 -0
- package/dist/canonical-datetime.js.map +1 -0
- package/dist/canonical-json.d.ts +12 -0
- package/dist/canonical-json.js +33 -0
- package/dist/canonical-json.js.map +1 -0
- package/dist/control-cohort.d.ts +63 -0
- package/dist/control-cohort.js +80 -0
- package/dist/control-cohort.js.map +1 -0
- package/dist/control-database.d.ts +259 -5
- package/dist/control-database.js +541 -128
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +89 -0
- package/dist/control-formation-recorder.js +139 -0
- package/dist/control-formation-recorder.js.map +1 -0
- package/dist/control-schema.d.ts +12 -0
- package/dist/control-schema.js +199 -0
- package/dist/control-schema.js.map +1 -0
- package/dist/control-stream.d.ts +54 -0
- package/dist/control-stream.js +104 -0
- package/dist/control-stream.js.map +1 -0
- package/dist/device-token.d.ts +50 -0
- package/dist/device-token.js +66 -0
- package/dist/device-token.js.map +1 -0
- package/dist/diagnostics/connection-path.d.ts +118 -0
- package/dist/diagnostics/connection-path.js +204 -0
- package/dist/diagnostics/connection-path.js.map +1 -0
- package/dist/diagnostics/webrtc-turn-tracker.d.ts +50 -0
- package/dist/diagnostics/webrtc-turn-tracker.js +196 -0
- package/dist/diagnostics/webrtc-turn-tracker.js.map +1 -0
- package/dist/hibernation-manager.d.ts +79 -0
- package/dist/hibernation-manager.js +173 -23
- package/dist/hibernation-manager.js.map +1 -1
- package/dist/index.d.ts +25 -4
- package/dist/index.js +59 -4
- package/dist/index.js.map +1 -1
- package/dist/key-store-file.d.ts +52 -0
- package/dist/key-store-file.js +194 -0
- package/dist/key-store-file.js.map +1 -0
- package/dist/key-store.d.ts +75 -0
- package/dist/key-store.js +66 -0
- package/dist/key-store.js.map +1 -0
- package/dist/peer-authorization.d.ts +26 -0
- package/dist/peer-authorization.js +40 -0
- package/dist/peer-authorization.js.map +1 -0
- package/dist/peer-record.d.ts +89 -0
- package/dist/peer-record.js +117 -0
- package/dist/peer-record.js.map +1 -0
- package/dist/push-credentials.d.ts +34 -0
- package/dist/push-credentials.js +81 -0
- package/dist/push-credentials.js.map +1 -0
- package/dist/push-fanout.d.ts +137 -0
- package/dist/push-fanout.js +197 -0
- package/dist/push-fanout.js.map +1 -0
- package/dist/push-notifier-apns.d.ts +53 -0
- package/dist/push-notifier-apns.js +195 -0
- package/dist/push-notifier-apns.js.map +1 -0
- package/dist/push-notifier-fcm.d.ts +38 -0
- package/dist/push-notifier-fcm.js +183 -0
- package/dist/push-notifier-fcm.js.map +1 -0
- package/dist/push-notifier-shared.d.ts +18 -0
- package/dist/push-notifier-shared.js +25 -0
- package/dist/push-notifier-shared.js.map +1 -0
- package/dist/push-notifier.d.ts +71 -0
- package/dist/push-notifier.js +50 -0
- package/dist/push-notifier.js.map +1 -0
- package/dist/schema-verification.d.ts +11 -2
- package/dist/schema-verification.js +15 -4
- package/dist/schema-verification.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +197 -9
- package/dist/seed-bootstrap.js +396 -137
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +63 -0
- package/dist/seed-trust-policy.js +56 -0
- package/dist/seed-trust-policy.js.map +1 -0
- package/dist/strand-addr-protocol.d.ts +132 -0
- package/dist/strand-addr-protocol.js +313 -0
- package/dist/strand-addr-protocol.js.map +1 -0
- package/dist/strand-cohort.d.ts +54 -0
- package/dist/strand-cohort.js +38 -0
- package/dist/strand-cohort.js.map +1 -0
- package/dist/strand-database.d.ts +54 -6
- package/dist/strand-database.js +88 -100
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +79 -19
- package/dist/strand-formation-manager.js +196 -104
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +162 -0
- package/dist/strand-formation-protocol.js +279 -0
- package/dist/strand-formation-protocol.js.map +1 -0
- package/dist/strand-instance-manager.d.ts +73 -4
- package/dist/strand-instance-manager.js +167 -41
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-key.d.ts +29 -0
- package/dist/strand-member-key.js +38 -0
- package/dist/strand-member-key.js.map +1 -0
- package/dist/strand-member-registry.d.ts +98 -0
- package/dist/strand-member-registry.js +129 -0
- package/dist/strand-member-registry.js.map +1 -0
- package/dist/strand-membership-writer.d.ts +361 -0
- package/dist/strand-membership-writer.js +449 -0
- package/dist/strand-membership-writer.js.map +1 -0
- package/dist/strand-solicitation.d.ts +77 -2
- package/dist/strand-solicitation.js +25 -4
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-payload.d.ts +30 -0
- package/dist/strand-wake-payload.js +20 -0
- package/dist/strand-wake-payload.js.map +1 -0
- package/dist/strand-wake-protocol.d.ts +114 -0
- package/dist/strand-wake-protocol.js +257 -0
- package/dist/strand-wake-protocol.js.map +1 -0
- package/dist/strand-watcher.d.ts +6 -2
- package/dist/strand-watcher.js +57 -17
- package/dist/strand-watcher.js.map +1 -1
- package/dist/types.d.ts +406 -23
- package/dist/types.js +22 -5
- package/dist/types.js.map +1 -1
- package/package.json +14 -8
- package/src/authority-key.ts +68 -0
- package/src/cadre-node.ts +2937 -948
- package/src/canonical-datetime.ts +39 -0
- package/src/canonical-json.ts +30 -0
- package/src/control-cohort.ts +103 -0
- package/src/control-database.ts +931 -383
- package/src/control-formation-recorder.ts +151 -0
- package/src/control-schema.ts +198 -0
- package/src/control-stream.ts +128 -0
- package/src/device-token.ts +89 -0
- package/src/diagnostics/connection-path.ts +277 -0
- package/src/diagnostics/webrtc-turn-tracker.ts +222 -0
- package/src/hibernation-manager.ts +197 -28
- package/src/index.ts +273 -79
- package/src/key-store-file.ts +203 -0
- package/src/key-store.ts +99 -0
- package/src/peer-authorization.ts +53 -0
- package/src/peer-record.ts +146 -0
- package/src/push-credentials.ts +84 -0
- package/src/push-fanout.ts +272 -0
- package/src/push-notifier-apns.ts +243 -0
- package/src/push-notifier-fcm.ts +221 -0
- package/src/push-notifier-shared.ts +28 -0
- package/src/push-notifier.ts +93 -0
- package/src/schema-verification.ts +18 -4
- package/src/seed-bootstrap.ts +464 -188
- package/src/seed-trust-policy.ts +107 -0
- package/src/strand-addr-protocol.ts +410 -0
- package/src/strand-cohort.ts +77 -0
- package/src/strand-database.ts +113 -141
- package/src/strand-formation-manager.ts +233 -142
- package/src/strand-formation-protocol.ts +391 -0
- package/src/strand-instance-manager.ts +207 -56
- package/src/strand-member-key.ts +39 -0
- package/src/strand-member-registry.ts +177 -0
- package/src/strand-membership-writer.ts +640 -0
- package/src/strand-solicitation.ts +93 -4
- package/src/strand-wake-payload.ts +32 -0
- package/src/strand-wake-protocol.ts +331 -0
- package/src/strand-watcher.ts +61 -17
- package/src/types.ts +1012 -582
package/src/strand-database.ts
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
import debug from 'debug';
|
|
2
|
-
import { Database
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { Database } from '@quereus/quereus';
|
|
3
|
+
import { connectToStrand } from '@serfab/quereus-plugin-sereus';
|
|
4
|
+
import type { SereusPluginResult } from '@serfab/quereus-plugin-sereus';
|
|
5
5
|
import type { Libp2p } from '@libp2p/interface';
|
|
6
6
|
import type { IRepo } from '@optimystic/db-core';
|
|
7
7
|
import type { IRawStorage } from '@optimystic/db-p2p';
|
|
8
8
|
import type { SAppConfig, StrandMode } from './types.js';
|
|
9
|
+
import { bootstrapFounderMembership } from './strand-membership-writer.js';
|
|
10
|
+
import { strandMemberKeyPair } from './strand-member-key.js';
|
|
9
11
|
|
|
10
12
|
const log = debug('sereus:cadre:strand-db');
|
|
11
13
|
const timing = debug('sereus:cadre:timing');
|
|
12
14
|
|
|
13
|
-
/**
|
|
14
|
-
* Minimal interface for the CollectionFactory returned by the optimystic plugin.
|
|
15
|
-
* We only need the methods we actually use.
|
|
16
|
-
*/
|
|
17
|
-
interface CollectionFactory {
|
|
18
|
-
registerLibp2pNode(networkName: string, node: Libp2p, coordinatedRepo: IRepo): void;
|
|
19
|
-
shutdown(): Promise<void>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Result of registering the optimystic plugin */
|
|
23
|
-
interface OptimysticPluginResult {
|
|
24
|
-
collectionFactory: CollectionFactory;
|
|
25
|
-
vtables: Array<{ name: string; module: unknown; auxData: unknown }>;
|
|
26
|
-
functions: Array<{ schema: unknown }>;
|
|
27
|
-
hydrate: (db: Database) => Promise<{ tables: number; indexes: number }>;
|
|
28
|
-
[key: string]: unknown;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
15
|
export interface StrandDatabaseConfig {
|
|
32
16
|
/** The strand ID */
|
|
33
17
|
strandId: string;
|
|
@@ -51,15 +35,43 @@ export interface StrandDatabaseConfig {
|
|
|
51
35
|
* across the two consumers.
|
|
52
36
|
*/
|
|
53
37
|
rawStorage?: IRawStorage;
|
|
38
|
+
/**
|
|
39
|
+
* Strand type (`'o'` open / `'c'` closed) from the control-network strand row.
|
|
40
|
+
* Drives the founder bootstrap: open strands get a `Header` only; closed strands
|
|
41
|
+
* also get the founding `Member`+`Authority`.
|
|
42
|
+
*/
|
|
43
|
+
strandType: 'o' | 'c';
|
|
44
|
+
/**
|
|
45
|
+
* The closed-strand `MemberPrivateKey` (base64 protobuf) from the strand row.
|
|
46
|
+
* Required when `founder` is true and `strandType` is `'c'` — it derives the
|
|
47
|
+
* founding `Member.Key`/`Authority.MemberKey`. Absent for open strands.
|
|
48
|
+
*/
|
|
49
|
+
memberPrivateKey?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Whether this node founds the strand. When true, {@link initialize} runs the
|
|
52
|
+
* one-time founder membership bootstrap after the schema is applied. Joiners
|
|
53
|
+
* leave this false and write nothing (rows arrive via sync). Defaults to false.
|
|
54
|
+
*/
|
|
55
|
+
founder?: boolean;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
* StrandDatabase manages the sApp schema for a strand using Quereus with
|
|
58
|
-
* Each strand instance has its own isolated database with
|
|
59
|
+
* StrandDatabase manages the sApp schema for a strand using Quereus with the
|
|
60
|
+
* Optimystic backend. Each strand instance has its own isolated database with
|
|
61
|
+
* the sApp's schema applied.
|
|
62
|
+
*
|
|
63
|
+
* This class owns the `Database` lifecycle (creation, `getDatabase()`, `close()`)
|
|
64
|
+
* but delegates the actual SQL-surface composition — plugin registration, node
|
|
65
|
+
* wiring, catalog hydration, schema apply — to `connectToStrand` from
|
|
66
|
+
* `@serfab/quereus-plugin-sereus`, the single shared composition. The libp2p
|
|
67
|
+
* node is injected here, so `connectToStrand` never *creates* the node;
|
|
68
|
+
* `StrandInstanceManager` owns the node lifecycle. (The strand connection's
|
|
69
|
+
* `shutdown` still stops the injected node via the collection factory, so the
|
|
70
|
+
* manager's own `node.stop()` is an idempotent second stop — see `close()`.)
|
|
59
71
|
*/
|
|
60
72
|
export class StrandDatabase {
|
|
61
73
|
private db: Database | null = null;
|
|
62
|
-
private
|
|
74
|
+
private shutdownStrand: SereusPluginResult['shutdown'] | null = null;
|
|
63
75
|
private readonly config: StrandDatabaseConfig;
|
|
64
76
|
private initialized = false;
|
|
65
77
|
|
|
@@ -68,7 +80,9 @@ export class StrandDatabase {
|
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
/**
|
|
71
|
-
* Initialize the database
|
|
83
|
+
* Initialize the database — create the `Database` and delegate the strand
|
|
84
|
+
* SQL-surface composition (plugins, node wiring, hydrate, schema apply) to
|
|
85
|
+
* `connectToStrand` with the injected libp2p node.
|
|
72
86
|
*/
|
|
73
87
|
async initialize(): Promise<void> {
|
|
74
88
|
if (this.initialized) {
|
|
@@ -76,127 +90,83 @@ export class StrandDatabase {
|
|
|
76
90
|
return;
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
log('Initializing StrandDatabase for strand: %s (sApp: %s v%s)',
|
|
80
|
-
this.config.strandId,
|
|
81
|
-
this.config.sAppConfig.id,
|
|
82
|
-
this.config.sAppConfig.version
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
// Create database instance
|
|
86
|
-
this.db = new Database();
|
|
87
93
|
const sid = this.config.strandId;
|
|
88
|
-
|
|
89
|
-
// Register crypto plugin (provides digest, sign, verify functions)
|
|
90
|
-
let t0 = performance.now();
|
|
91
|
-
await registerPlugin(this.db, cryptoPlugin);
|
|
92
|
-
timing('[strandDb:%s] cryptoPlugin: %dms', sid, Math.round(performance.now() - t0));
|
|
93
|
-
log('Registered crypto plugin');
|
|
94
|
-
|
|
95
|
-
// Register optimystic plugin. In `bootstrap` mode we route through the
|
|
96
|
-
// local transactor so a solo node can initialize (schema apply etc.)
|
|
97
|
-
// without network round trips. In `networked` mode the network transactor
|
|
98
|
-
// is used. Mode is fixed for the lifetime of this Database instance; the
|
|
99
|
-
// caller must restart the strand to transition modes.
|
|
100
|
-
t0 = performance.now();
|
|
101
|
-
const networkName = `strand-${sid}`;
|
|
102
94
|
const mode: StrandMode = this.config.mode ?? 'networked';
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// the strand's raw storage (same instance the libp2p node uses) so writes
|
|
106
|
-
// persist to the host backend instead of in-memory storage.
|
|
107
|
-
const rawStorage = this.config.rawStorage;
|
|
108
|
-
const pluginConfig: Record<string, unknown> = {
|
|
109
|
-
default_transactor: defaultTransactor,
|
|
110
|
-
default_key_network: 'libp2p',
|
|
111
|
-
default_network_name: networkName,
|
|
112
|
-
enable_cache: true,
|
|
113
|
-
};
|
|
114
|
-
if (mode === 'bootstrap' && rawStorage) {
|
|
115
|
-
pluginConfig.rawStorageFactory = () => rawStorage;
|
|
116
|
-
}
|
|
117
|
-
// The plugin's published signature is `Record<string, SqlValue>` but it
|
|
118
|
-
// also reads `rawStorageFactory` (a function reference) from the same map.
|
|
119
|
-
// Cast through unknown rather than widen the public type.
|
|
120
|
-
const pluginResult = optimysticPlugin(
|
|
121
|
-
this.db,
|
|
122
|
-
pluginConfig as unknown as Parameters<typeof optimysticPlugin>[1]
|
|
123
|
-
) as OptimysticPluginResult;
|
|
124
|
-
log('Optimystic plugin registered for strand %s (mode=%s, transactor=%s, persistentStorage=%s)',
|
|
125
|
-
sid, mode, defaultTransactor, mode === 'bootstrap' && !!rawStorage);
|
|
95
|
+
log('Initializing StrandDatabase for strand: %s (sApp: %s v%s, mode=%s)',
|
|
96
|
+
sid, this.config.sAppConfig.id, this.config.sAppConfig.version, mode);
|
|
126
97
|
|
|
127
|
-
|
|
128
|
-
for (const vtable of pluginResult.vtables as Array<{ name: string; module: unknown; auxData: unknown }>) {
|
|
129
|
-
this.db.registerModule(vtable.name, vtable.module as any, vtable.auxData);
|
|
130
|
-
}
|
|
131
|
-
for (const func of pluginResult.functions as Array<{ schema: unknown }>) {
|
|
132
|
-
this.db.registerFunction(func.schema as any);
|
|
133
|
-
}
|
|
134
|
-
timing('[strandDb:%s] optimysticPlugin: %dms', sid, Math.round(performance.now() - t0));
|
|
135
|
-
|
|
136
|
-
this.collectionFactory = pluginResult.collectionFactory;
|
|
137
|
-
|
|
138
|
-
// Inject the libp2p node into the collection factory
|
|
139
|
-
t0 = performance.now();
|
|
140
|
-
this.collectionFactory.registerLibp2pNode(
|
|
141
|
-
networkName,
|
|
142
|
-
this.config.libp2pNode,
|
|
143
|
-
this.config.coordinatedRepo
|
|
144
|
-
);
|
|
145
|
-
timing('[strandDb:%s] registerLibp2pNode: %dms', sid, Math.round(performance.now() - t0));
|
|
146
|
-
log('Registered libp2p node with collection factory');
|
|
98
|
+
this.db = new Database();
|
|
147
99
|
|
|
148
|
-
//
|
|
149
|
-
// `
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
100
|
+
// Delegate to the shared composition. The node is injected, so:
|
|
101
|
+
// - `connectToStrand` never creates a node (its `createdNode` stays null);
|
|
102
|
+
// `StrandInstanceManager` owns the node lifecycle. Its `shutdown` does
|
|
103
|
+
// still stop the injected node via the collection factory, so the
|
|
104
|
+
// manager's later `node.stop()` is an idempotent second stop;
|
|
105
|
+
// - `storage` (the strand's raw storage, same instance the node uses) is
|
|
106
|
+
// handed to the plugin's local transactor in bootstrap mode so DML lands
|
|
107
|
+
// on the host backend rather than in-memory.
|
|
108
|
+
const t0 = performance.now();
|
|
109
|
+
const result = await connectToStrand(this.db, {
|
|
110
|
+
strandId: sid,
|
|
111
|
+
schema: this.config.sAppConfig.schema,
|
|
112
|
+
mode,
|
|
113
|
+
storage: this.config.rawStorage,
|
|
114
|
+
libp2pNode: this.config.libp2pNode,
|
|
115
|
+
coordinatedRepo: this.config.coordinatedRepo,
|
|
116
|
+
enableCache: true,
|
|
157
117
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
timing('[strandDb:%s] hydrate: %dms (tables=%d, indexes=%d)',
|
|
171
|
-
sid, Math.round(performance.now() - t0), hydrated.tables, hydrated.indexes);
|
|
172
|
-
log('Hydrated catalog for strand %s (tables=%d, indexes=%d)',
|
|
173
|
-
sid, hydrated.tables, hydrated.indexes);
|
|
174
|
-
|
|
175
|
-
// Execute the sApp schema DDL
|
|
176
|
-
t0 = performance.now();
|
|
177
|
-
await this.executeSchema();
|
|
178
|
-
timing('[strandDb:%s] executeSchema: %dms', sid, Math.round(performance.now() - t0));
|
|
118
|
+
this.shutdownStrand = result.shutdown;
|
|
119
|
+
timing('[strandDb:%s] connectToStrand: %dms (hydrated tables=%d, indexes=%d)',
|
|
120
|
+
sid, Math.round(performance.now() - t0),
|
|
121
|
+
result.hydrated?.tables ?? 0, result.hydrated?.indexes ?? 0);
|
|
122
|
+
|
|
123
|
+
// Founder-only: write the one-time membership bootstrap now that the schema is
|
|
124
|
+
// applied (connectToStrand has returned). A throw here propagates out of
|
|
125
|
+
// initialize() so buildStrandRuntime's rollback tears the half-built strand
|
|
126
|
+
// down rather than leaking a node. Joiners skip this — their rows arrive via sync.
|
|
127
|
+
if (this.config.founder === true) {
|
|
128
|
+
await this.bootstrapFounder();
|
|
129
|
+
}
|
|
179
130
|
|
|
180
131
|
this.initialized = true;
|
|
181
132
|
log('StrandDatabase for strand %s initialized successfully', sid);
|
|
182
133
|
}
|
|
183
134
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Run the founder membership bootstrap against the freshly-composed strand DB.
|
|
137
|
+
*
|
|
138
|
+
* Derives the founding keypair from the closed-strand `memberPrivateKey` (the
|
|
139
|
+
* `Member.Key`/`Authority.MemberKey` are its public key); a closed strand with no
|
|
140
|
+
* `memberPrivateKey` throws, because it could never seat a founding authority.
|
|
141
|
+
* Open strands derive no keypair (Header only). Idempotent — see
|
|
142
|
+
* {@link bootstrapFounderMembership}.
|
|
143
|
+
*/
|
|
144
|
+
private async bootstrapFounder(): Promise<void> {
|
|
145
|
+
const { strandId, strandType, memberPrivateKey, sAppConfig } = this.config;
|
|
146
|
+
const founderKeyPair = strandType === 'c'
|
|
147
|
+
? this.deriveFounderKeyPair(strandId, memberPrivateKey)
|
|
148
|
+
: undefined;
|
|
149
|
+
await bootstrapFounderMembership(this.db!, {
|
|
150
|
+
strandId,
|
|
151
|
+
type: strandType,
|
|
152
|
+
sApp: sAppConfig,
|
|
153
|
+
founderKeyPair,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
197
156
|
|
|
198
|
-
|
|
199
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Derive the founding keypair for a closed strand from its `memberPrivateKey`,
|
|
159
|
+
* failing loudly when the key is absent (a closed strand with no founding
|
|
160
|
+
* Authority can never admit anyone).
|
|
161
|
+
*/
|
|
162
|
+
private deriveFounderKeyPair(strandId: string, memberPrivateKey: string | undefined) {
|
|
163
|
+
if (!memberPrivateKey) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Cannot found closed strand ${strandId}: the strand row has no MemberPrivateKey. ` +
|
|
166
|
+
'A closed strand needs a founding Member/Authority derived from that key.',
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return strandMemberKeyPair(memberPrivateKey);
|
|
200
170
|
}
|
|
201
171
|
|
|
202
172
|
/**
|
|
@@ -208,15 +178,18 @@ export class StrandDatabase {
|
|
|
208
178
|
}
|
|
209
179
|
|
|
210
180
|
/**
|
|
211
|
-
* Close the database and cleanup resources
|
|
181
|
+
* Close the database and cleanup resources. Runs the strand-connection
|
|
182
|
+
* shutdown (collection-factory teardown, which also stops the injected node),
|
|
183
|
+
* then closes the `Database`. `StrandInstanceManager.releaseRuntime` issues a
|
|
184
|
+
* further idempotent `node.stop()` after this returns.
|
|
212
185
|
*/
|
|
213
186
|
async close(): Promise<void> {
|
|
214
|
-
if (this.
|
|
215
|
-
await this.
|
|
216
|
-
this.
|
|
187
|
+
if (this.shutdownStrand) {
|
|
188
|
+
await this.shutdownStrand();
|
|
189
|
+
this.shutdownStrand = null;
|
|
217
190
|
}
|
|
218
191
|
if (this.db) {
|
|
219
|
-
this.db.close();
|
|
192
|
+
void this.db.close();
|
|
220
193
|
this.db = null;
|
|
221
194
|
}
|
|
222
195
|
this.initialized = false;
|
|
@@ -229,4 +202,3 @@ export class StrandDatabase {
|
|
|
229
202
|
}
|
|
230
203
|
}
|
|
231
204
|
}
|
|
232
|
-
|