@serfab/cadre-core 0.0.2
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 +252 -0
- package/dist/arachnode-stub.d.ts +60 -0
- package/dist/arachnode-stub.js +120 -0
- package/dist/arachnode-stub.js.map +1 -0
- package/dist/cadre-node.d.ts +252 -0
- package/dist/cadre-node.js +753 -0
- package/dist/cadre-node.js.map +1 -0
- package/dist/control-database.d.ts +62 -0
- package/dist/control-database.js +295 -0
- package/dist/control-database.js.map +1 -0
- package/dist/enrollment.d.ts +76 -0
- package/dist/enrollment.js +126 -0
- package/dist/enrollment.js.map +1 -0
- package/dist/hibernation-manager.d.ts +68 -0
- package/dist/hibernation-manager.js +212 -0
- package/dist/hibernation-manager.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +164 -0
- package/dist/seed-bootstrap.js +554 -0
- package/dist/seed-bootstrap.js.map +1 -0
- package/dist/strand-database.d.ts +39 -0
- package/dist/strand-database.js +108 -0
- package/dist/strand-database.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +87 -0
- package/dist/strand-formation-manager.js +168 -0
- package/dist/strand-formation-manager.js.map +1 -0
- package/dist/strand-instance-manager.d.ts +60 -0
- package/dist/strand-instance-manager.js +209 -0
- package/dist/strand-instance-manager.js.map +1 -0
- package/dist/strand-solicitation.d.ts +145 -0
- package/dist/strand-solicitation.js +180 -0
- package/dist/strand-solicitation.js.map +1 -0
- package/dist/strand-watcher.d.ts +70 -0
- package/dist/strand-watcher.js +136 -0
- package/dist/strand-watcher.js.map +1 -0
- package/dist/types.d.ts +484 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/src/arachnode-stub.ts +160 -0
- package/src/cadre-node.ts +923 -0
- package/src/control-database.ts +376 -0
- package/src/enrollment.ts +199 -0
- package/src/hibernation-manager.ts +268 -0
- package/src/index.ts +71 -0
- package/src/seed-bootstrap.ts +753 -0
- package/src/strand-database.ts +168 -0
- package/src/strand-formation-manager.ts +277 -0
- package/src/strand-instance-manager.ts +281 -0
- package/src/strand-solicitation.ts +318 -0
- package/src/strand-watcher.ts +197 -0
- package/src/types.ts +553 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import { Database, registerPlugin } from '@quereus/quereus';
|
|
3
|
+
import cryptoPlugin from '@optimystic/quereus-plugin-crypto/plugin';
|
|
4
|
+
import optimysticPlugin from '@optimystic/quereus-plugin-optimystic/plugin';
|
|
5
|
+
import type { Libp2p } from '@libp2p/interface';
|
|
6
|
+
import type { IRepo } from '@optimystic/db-core';
|
|
7
|
+
import type { SAppConfig } from './types.js';
|
|
8
|
+
|
|
9
|
+
const log = debug('sereus:cadre:strand-db');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Minimal interface for the CollectionFactory returned by the optimystic plugin.
|
|
13
|
+
* We only need the methods we actually use.
|
|
14
|
+
*/
|
|
15
|
+
interface CollectionFactory {
|
|
16
|
+
registerLibp2pNode(networkName: string, node: Libp2p, coordinatedRepo: IRepo): void;
|
|
17
|
+
shutdown(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Result of registering the optimystic plugin */
|
|
21
|
+
interface OptimysticPluginResult {
|
|
22
|
+
collectionFactory: CollectionFactory;
|
|
23
|
+
vtables: Array<{ name: string; module: unknown; auxData: unknown }>;
|
|
24
|
+
functions: Array<{ schema: unknown }>;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface StrandDatabaseConfig {
|
|
29
|
+
/** The strand ID */
|
|
30
|
+
strandId: string;
|
|
31
|
+
/** sApp configuration containing the schema */
|
|
32
|
+
sAppConfig: SAppConfig;
|
|
33
|
+
/** Libp2p node for the strand network */
|
|
34
|
+
libp2pNode: Libp2p;
|
|
35
|
+
/** Coordinated repo from the libp2p node */
|
|
36
|
+
coordinatedRepo: IRepo;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* StrandDatabase manages the sApp schema for a strand using Quereus with Optimystic backend.
|
|
41
|
+
* Each strand instance has its own isolated database with the sApp's schema applied.
|
|
42
|
+
*/
|
|
43
|
+
export class StrandDatabase {
|
|
44
|
+
private db: Database | null = null;
|
|
45
|
+
private collectionFactory: CollectionFactory | null = null;
|
|
46
|
+
private readonly config: StrandDatabaseConfig;
|
|
47
|
+
private initialized = false;
|
|
48
|
+
|
|
49
|
+
constructor(config: StrandDatabaseConfig) {
|
|
50
|
+
this.config = config;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initialize the database - register plugins and execute sApp schema
|
|
55
|
+
*/
|
|
56
|
+
async initialize(): Promise<void> {
|
|
57
|
+
if (this.initialized) {
|
|
58
|
+
log('StrandDatabase for strand %s already initialized', this.config.strandId);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
log('Initializing StrandDatabase for strand: %s (sApp: %s v%s)',
|
|
63
|
+
this.config.strandId,
|
|
64
|
+
this.config.sAppConfig.id,
|
|
65
|
+
this.config.sAppConfig.version
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Create database instance
|
|
69
|
+
this.db = new Database();
|
|
70
|
+
|
|
71
|
+
// Register crypto plugin (provides digest, sign, verify functions)
|
|
72
|
+
await registerPlugin(this.db, cryptoPlugin);
|
|
73
|
+
log('Registered crypto plugin');
|
|
74
|
+
|
|
75
|
+
// Register optimystic plugin with network transactor as default
|
|
76
|
+
const networkName = `strand-${this.config.strandId}`;
|
|
77
|
+
const pluginResult = optimysticPlugin(this.db, {
|
|
78
|
+
default_transactor: 'network',
|
|
79
|
+
default_key_network: 'libp2p',
|
|
80
|
+
default_network_name: networkName,
|
|
81
|
+
enable_cache: true,
|
|
82
|
+
}) as OptimysticPluginResult;
|
|
83
|
+
|
|
84
|
+
// Register vtables and functions manually since we need access to collectionFactory
|
|
85
|
+
for (const vtable of pluginResult.vtables as Array<{ name: string; module: unknown; auxData: unknown }>) {
|
|
86
|
+
this.db.registerModule(vtable.name, vtable.module as any, vtable.auxData);
|
|
87
|
+
}
|
|
88
|
+
for (const func of pluginResult.functions as Array<{ schema: unknown }>) {
|
|
89
|
+
this.db.registerFunction(func.schema as any);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.collectionFactory = pluginResult.collectionFactory;
|
|
93
|
+
|
|
94
|
+
// Inject the libp2p node into the collection factory
|
|
95
|
+
this.collectionFactory.registerLibp2pNode(
|
|
96
|
+
networkName,
|
|
97
|
+
this.config.libp2pNode,
|
|
98
|
+
this.config.coordinatedRepo
|
|
99
|
+
);
|
|
100
|
+
log('Registered libp2p node with collection factory');
|
|
101
|
+
|
|
102
|
+
// Set optimystic as the default virtual table module so that
|
|
103
|
+
// `declare schema` tables (which omit USING) are backed by optimystic
|
|
104
|
+
// instead of the built-in memory module.
|
|
105
|
+
this.db.setDefaultVtabName('optimystic');
|
|
106
|
+
this.db.setDefaultVtabArgs({
|
|
107
|
+
networkName,
|
|
108
|
+
transactor: 'network',
|
|
109
|
+
keyNetwork: 'libp2p',
|
|
110
|
+
});
|
|
111
|
+
log('Set default vtab to optimystic (networkName=%s)', networkName);
|
|
112
|
+
|
|
113
|
+
// Execute the sApp schema DDL
|
|
114
|
+
await this.executeSchema();
|
|
115
|
+
|
|
116
|
+
this.initialized = true;
|
|
117
|
+
log('StrandDatabase for strand %s initialized successfully', this.config.strandId);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async executeSchema(): Promise<void> {
|
|
121
|
+
const rawSchema = this.config.sAppConfig.schema;
|
|
122
|
+
log('Applying sApp schema for strand %s', this.config.strandId);
|
|
123
|
+
|
|
124
|
+
// Wrap the raw schema DDL in a declarative schema block and apply it.
|
|
125
|
+
// This ensures proper schema management with diff/apply semantics.
|
|
126
|
+
// The schema is applied to a named schema 'App' to keep it isolated.
|
|
127
|
+
const wrappedSchema = `
|
|
128
|
+
declare schema App {
|
|
129
|
+
${rawSchema}
|
|
130
|
+
}
|
|
131
|
+
apply schema App;
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
await this.db!.exec(wrappedSchema);
|
|
135
|
+
log('sApp schema applied to strand %s', this.config.strandId);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get the underlying database for queries
|
|
140
|
+
*/
|
|
141
|
+
getDatabase(): Database {
|
|
142
|
+
this.ensureInitialized();
|
|
143
|
+
return this.db!;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Close the database and cleanup resources
|
|
148
|
+
*/
|
|
149
|
+
async close(): Promise<void> {
|
|
150
|
+
if (this.collectionFactory) {
|
|
151
|
+
await this.collectionFactory.shutdown();
|
|
152
|
+
this.collectionFactory = null;
|
|
153
|
+
}
|
|
154
|
+
if (this.db) {
|
|
155
|
+
this.db.close();
|
|
156
|
+
this.db = null;
|
|
157
|
+
}
|
|
158
|
+
this.initialized = false;
|
|
159
|
+
log('StrandDatabase for strand %s closed', this.config.strandId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private ensureInitialized(): void {
|
|
163
|
+
if (!this.initialized || !this.db) {
|
|
164
|
+
throw new Error(`StrandDatabase for strand ${this.config.strandId} not initialized. Call initialize() first.`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import type { Libp2p } from '@libp2p/interface';
|
|
3
|
+
import {
|
|
4
|
+
SessionManager,
|
|
5
|
+
createBootstrapManager,
|
|
6
|
+
DEFAULT_PROTOCOL_ID,
|
|
7
|
+
type SessionHooks,
|
|
8
|
+
type SessionConfig,
|
|
9
|
+
type BootstrapLink,
|
|
10
|
+
type BootstrapResult,
|
|
11
|
+
type BootstrapMode,
|
|
12
|
+
type DialogParty,
|
|
13
|
+
type ProvisionResult
|
|
14
|
+
} from '@serfab/strand-proto';
|
|
15
|
+
import type {
|
|
16
|
+
OpenInvitation,
|
|
17
|
+
FormStrandResult,
|
|
18
|
+
StrandFormationDisclosure
|
|
19
|
+
} from './types.js';
|
|
20
|
+
import type {
|
|
21
|
+
DisclosureValidator,
|
|
22
|
+
FormationUsageRecorder,
|
|
23
|
+
StrandProvisioner
|
|
24
|
+
} from './strand-solicitation.js';
|
|
25
|
+
|
|
26
|
+
const log = debug('sereus:cadre:formation-manager');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Configuration for StrandFormationManager
|
|
30
|
+
*/
|
|
31
|
+
export interface StrandFormationManagerConfig {
|
|
32
|
+
/** Session timeout in milliseconds */
|
|
33
|
+
sessionTimeoutMs?: number;
|
|
34
|
+
/** Step timeout in milliseconds */
|
|
35
|
+
stepTimeoutMs?: number;
|
|
36
|
+
/** Maximum concurrent sessions */
|
|
37
|
+
maxConcurrentSessions?: number;
|
|
38
|
+
/** Enable debug logging */
|
|
39
|
+
enableDebugLogging?: boolean;
|
|
40
|
+
/** Protocol ID override */
|
|
41
|
+
protocolId?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Options for creating a StrandFormationManager
|
|
46
|
+
*/
|
|
47
|
+
export interface StrandFormationManagerOptions {
|
|
48
|
+
/** Validates disclosures from initiators */
|
|
49
|
+
disclosureValidator?: DisclosureValidator;
|
|
50
|
+
/** Records and validates token usage */
|
|
51
|
+
formationUsageRecorder?: FormationUsageRecorder;
|
|
52
|
+
/** Provisions strands after validation */
|
|
53
|
+
strandProvisioner?: StrandProvisioner;
|
|
54
|
+
/** This party's ID for identification */
|
|
55
|
+
partyId: string;
|
|
56
|
+
/** This party's cadre peer addresses */
|
|
57
|
+
cadrePeerAddrs?: string[];
|
|
58
|
+
/** Configuration options */
|
|
59
|
+
config?: StrandFormationManagerConfig;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* StrandFormationManager bridges cadre-core's strand solicitation interfaces
|
|
64
|
+
* with strand-proto's SessionManager for actual protocol handling over libp2p.
|
|
65
|
+
*
|
|
66
|
+
* It implements SessionHooks by delegating to the existing interfaces:
|
|
67
|
+
* - DisclosureValidator -> validateIdentity
|
|
68
|
+
* - FormationUsageRecorder -> validateToken
|
|
69
|
+
* - StrandProvisioner -> provisionStrand
|
|
70
|
+
*/
|
|
71
|
+
export class StrandFormationManager {
|
|
72
|
+
private readonly sessionManager: SessionManager;
|
|
73
|
+
private readonly disclosureValidator?: DisclosureValidator;
|
|
74
|
+
private readonly formationUsageRecorder?: FormationUsageRecorder;
|
|
75
|
+
private readonly strandProvisioner?: StrandProvisioner;
|
|
76
|
+
private readonly partyId: string;
|
|
77
|
+
private readonly cadrePeerAddrs: string[];
|
|
78
|
+
private registeredNodes: Set<Libp2p> = new Set();
|
|
79
|
+
|
|
80
|
+
constructor(options: StrandFormationManagerOptions) {
|
|
81
|
+
this.disclosureValidator = options.disclosureValidator;
|
|
82
|
+
this.formationUsageRecorder = options.formationUsageRecorder;
|
|
83
|
+
this.strandProvisioner = options.strandProvisioner;
|
|
84
|
+
this.partyId = options.partyId;
|
|
85
|
+
this.cadrePeerAddrs = options.cadrePeerAddrs ?? [];
|
|
86
|
+
|
|
87
|
+
// Create SessionHooks that delegate to our interfaces
|
|
88
|
+
const hooks = this.createSessionHooks();
|
|
89
|
+
|
|
90
|
+
// Create the underlying SessionManager
|
|
91
|
+
const config: Partial<SessionConfig> = {
|
|
92
|
+
sessionTimeoutMs: options.config?.sessionTimeoutMs ?? 30000,
|
|
93
|
+
stepTimeoutMs: options.config?.stepTimeoutMs ?? 5000,
|
|
94
|
+
maxConcurrentSessions: options.config?.maxConcurrentSessions ?? 100,
|
|
95
|
+
enableDebugLogging: options.config?.enableDebugLogging ?? false,
|
|
96
|
+
protocolId: options.config?.protocolId ?? DEFAULT_PROTOCOL_ID
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
this.sessionManager = createBootstrapManager(hooks, config);
|
|
100
|
+
log('StrandFormationManager created for party: %s', this.partyId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Register this manager as a protocol handler on a libp2p node.
|
|
105
|
+
* Call this on the control network node to handle incoming formation requests.
|
|
106
|
+
*/
|
|
107
|
+
registerResponder(node: Libp2p, protocolId?: string): void {
|
|
108
|
+
if (this.registeredNodes.has(node)) {
|
|
109
|
+
log('Node already registered');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.sessionManager.register(node, protocolId);
|
|
113
|
+
this.registeredNodes.add(node);
|
|
114
|
+
log('Registered as responder on node');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Unregister the protocol handler from a libp2p node.
|
|
119
|
+
*/
|
|
120
|
+
unregisterResponder(node: Libp2p, protocolId?: string): void {
|
|
121
|
+
if (!this.registeredNodes.has(node)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.sessionManager.unregister(node, protocolId);
|
|
125
|
+
this.registeredNodes.delete(node);
|
|
126
|
+
log('Unregistered from node');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Form a strand with a responder via an open invitation.
|
|
131
|
+
*
|
|
132
|
+
* This is the initiator-side operation. It:
|
|
133
|
+
* 1. Converts the OpenInvitation to a BootstrapLink
|
|
134
|
+
* 2. Calls SessionManager.initiateBootstrap()
|
|
135
|
+
* 3. Returns the result as a FormStrandResult
|
|
136
|
+
*/
|
|
137
|
+
async formStrand(
|
|
138
|
+
invitation: OpenInvitation,
|
|
139
|
+
disclosure: StrandFormationDisclosure,
|
|
140
|
+
node: Libp2p
|
|
141
|
+
): Promise<FormStrandResult> {
|
|
142
|
+
log('Forming strand with invitation token: %s', invitation.token);
|
|
143
|
+
|
|
144
|
+
// Convert OpenInvitation to BootstrapLink
|
|
145
|
+
const link: BootstrapLink = {
|
|
146
|
+
responderPeerAddrs: invitation.bootstrap,
|
|
147
|
+
token: invitation.token,
|
|
148
|
+
tokenExpiryUtc: invitation.expiration.toISOString(),
|
|
149
|
+
mode: 'responderCreates' // Responder provisions the strand
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// The disclosure is passed via the identityBundle in the contact message
|
|
153
|
+
// We store it temporarily so the hooks can access it
|
|
154
|
+
// Note: strand-proto's DialerSession constructs the contact message internally
|
|
155
|
+
// We need to extend this to pass the disclosure - for now we use partyId
|
|
156
|
+
|
|
157
|
+
const result = await this.sessionManager.initiateBootstrap(link, node as any);
|
|
158
|
+
|
|
159
|
+
log('Strand formed: %s', result.strand.strandId);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
memberKey: this.partyId, // The initiator's member key
|
|
163
|
+
invitePrivateKey: '', // Would come from key generation
|
|
164
|
+
strandId: result.strand.strandId
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get the number of active sessions
|
|
170
|
+
*/
|
|
171
|
+
getActiveSessionCounts(): { listeners: number; dialers: number } {
|
|
172
|
+
return this.sessionManager.getActiveSessionCounts();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Create SessionHooks that delegate to our interfaces
|
|
177
|
+
*/
|
|
178
|
+
private createSessionHooks(): SessionHooks {
|
|
179
|
+
return {
|
|
180
|
+
validateToken: async (token: string, sessionId: string) => {
|
|
181
|
+
log('validateToken: %s (session: %s)', token, sessionId);
|
|
182
|
+
|
|
183
|
+
if (!this.formationUsageRecorder) {
|
|
184
|
+
// No recorder configured - accept all tokens
|
|
185
|
+
return { mode: 'responderCreates' as BootstrapMode, valid: true };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const tokenCheck = await this.formationUsageRecorder.isTokenValid(token);
|
|
189
|
+
if (!tokenCheck.valid) {
|
|
190
|
+
log('Token invalid: %s', token);
|
|
191
|
+
return { mode: 'responderCreates' as BootstrapMode, valid: false };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const isUsed = await this.formationUsageRecorder.isTokenUsed(token);
|
|
195
|
+
if (isUsed) {
|
|
196
|
+
log('Token already used: %s', token);
|
|
197
|
+
return { mode: 'responderCreates' as BootstrapMode, valid: false };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { mode: 'responderCreates' as BootstrapMode, valid: true };
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
validateIdentity: async (identity: unknown, sessionId: string) => {
|
|
204
|
+
log('validateIdentity (session: %s)', sessionId);
|
|
205
|
+
|
|
206
|
+
if (!this.disclosureValidator) {
|
|
207
|
+
// No validator configured - accept all identities
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Extract disclosure from identity bundle
|
|
212
|
+
const disclosure = identity as StrandFormationDisclosure;
|
|
213
|
+
// We need the token here - it's passed via the contact message
|
|
214
|
+
// For now, use a placeholder - this will be refined
|
|
215
|
+
const token = (identity as any)?.token ?? '';
|
|
216
|
+
|
|
217
|
+
return this.disclosureValidator.validateDisclosure(token, disclosure);
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
provisionStrand: async (
|
|
221
|
+
creator: DialogParty,
|
|
222
|
+
creatorPartyId: string,
|
|
223
|
+
otherPartyId: string,
|
|
224
|
+
sessionId: string
|
|
225
|
+
): Promise<ProvisionResult> => {
|
|
226
|
+
log('provisionStrand: creator=%s, other=%s (session: %s)',
|
|
227
|
+
creatorPartyId, otherPartyId, sessionId);
|
|
228
|
+
|
|
229
|
+
if (!this.strandProvisioner) {
|
|
230
|
+
// No provisioner - return a placeholder
|
|
231
|
+
const strandId = `strand-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
232
|
+
return {
|
|
233
|
+
strand: { strandId, createdBy: creator },
|
|
234
|
+
dbConnectionInfo: { endpoint: 'local', credentialsRef: '' }
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Determine initiator/responder keys based on creator
|
|
239
|
+
const initiatorKey = creator === 'initiator' ? creatorPartyId : otherPartyId;
|
|
240
|
+
const responderKey = creator === 'responder' ? creatorPartyId : otherPartyId;
|
|
241
|
+
|
|
242
|
+
const result = await this.strandProvisioner.provisionStrand(
|
|
243
|
+
'', // sAppId - would come from invitation
|
|
244
|
+
initiatorKey,
|
|
245
|
+
responderKey
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
strand: { strandId: result.strandId, createdBy: creator },
|
|
250
|
+
dbConnectionInfo: { endpoint: 'local', credentialsRef: '' }
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
validateResponse: async (response: unknown, sessionId: string) => {
|
|
255
|
+
log('validateResponse (session: %s)', sessionId);
|
|
256
|
+
// Accept all responses for now
|
|
257
|
+
return true;
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
validateDatabaseResult: async (result: unknown, sessionId: string) => {
|
|
261
|
+
log('validateDatabaseResult (session: %s)', sessionId);
|
|
262
|
+
// Accept all database results for now
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Create a StrandFormationManager with the given options
|
|
271
|
+
*/
|
|
272
|
+
export function createStrandFormationManager(
|
|
273
|
+
options: StrandFormationManagerOptions
|
|
274
|
+
): StrandFormationManager {
|
|
275
|
+
return new StrandFormationManager(options);
|
|
276
|
+
}
|
|
277
|
+
|