@c7-digital/ledger 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BUILD.md +1 -1
- package/README.md +1 -3
- package/lib/src/PackageIdEmitterMap.d.ts +42 -0
- package/lib/src/PackageIdEmitterMap.js +60 -0
- package/lib/src/generated/asyncapi-schema.js +1 -1
- package/lib/src/generated/openapi-schema.js +1 -1
- package/lib/src/ledger.d.ts +15 -5
- package/lib/src/ledger.js +132 -24
- package/lib/src/multistream.d.ts +34 -7
- package/lib/src/multistream.js +69 -25
- package/lib/src/token.d.ts +13 -0
- package/lib/src/token.js +64 -0
- package/lib/src/types.d.ts +62 -2
- package/lib/src/websocket.d.ts +9 -0
- package/lib/src/websocket.js +16 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib-lite/src/PackageIdEmitterMap.d.ts +42 -0
- package/lib-lite/src/PackageIdEmitterMap.js +60 -0
- package/lib-lite/src/ledger.d.ts +15 -5
- package/lib-lite/src/ledger.js +132 -24
- package/lib-lite/src/multistream.d.ts +34 -7
- package/lib-lite/src/multistream.js +69 -25
- package/lib-lite/src/token.d.ts +13 -0
- package/lib-lite/src/token.js +64 -0
- package/lib-lite/src/types.d.ts +62 -2
- package/lib-lite/src/websocket.d.ts +9 -0
- package/lib-lite/src/websocket.js +16 -0
- package/lib-lite/tsconfig.temp.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/lib/src/TemplateEmitterMap.d.ts +0 -40
- package/lib/src/TemplateEmitterMap.js +0 -57
- package/lib-lite/src/TemplateEmitterMap.d.ts +0 -40
- package/lib-lite/src/TemplateEmitterMap.js +0 -57
package/BUILD.md
CHANGED
package/README.md
CHANGED
|
@@ -54,9 +54,7 @@ const contracts = await ledger.query(MyTemplate);
|
|
|
54
54
|
const result = await ledger.create(MyTemplate, payload, [actAsParty]);
|
|
55
55
|
|
|
56
56
|
// Exercise choices
|
|
57
|
-
const choiceResult = await ledger.exercise(MyChoice, contractId, argument, [
|
|
58
|
-
actAsParty,
|
|
59
|
-
]);
|
|
57
|
+
const choiceResult = await ledger.exercise(MyChoice, contractId, argument, [actAsParty]);
|
|
60
58
|
```
|
|
61
59
|
|
|
62
60
|
## Migration from @daml/ledger
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { EventEmitter } from "eventemitter3";
|
|
2
|
+
import { PackageIdString } from "./valueTypes.js";
|
|
3
|
+
/**
|
|
4
|
+
* A specialized Map wrapper for package ID emitters that uses partiallyQualified
|
|
5
|
+
* IDs for comparison to ensure consistent lookup regardless of package ID.
|
|
6
|
+
* Can be used for both template IDs and interface IDs.
|
|
7
|
+
*/
|
|
8
|
+
export declare class PackageIdEmitterMap {
|
|
9
|
+
private emitters;
|
|
10
|
+
/**
|
|
11
|
+
* Set an EventEmitter for a specific package ID
|
|
12
|
+
* @param packageId The full package ID (template or interface)
|
|
13
|
+
* @param emitter The EventEmitter instance
|
|
14
|
+
*/
|
|
15
|
+
set(packageId: PackageIdString, emitter: EventEmitter): void;
|
|
16
|
+
/**
|
|
17
|
+
* Get the EventEmitter for a specific package ID
|
|
18
|
+
* @param packageId The package ID (full or partial)
|
|
19
|
+
* @returns The EventEmitter or undefined if not found
|
|
20
|
+
*/
|
|
21
|
+
get(packageId: PackageIdString): EventEmitter | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Check if an emitter exists for a specific package ID
|
|
24
|
+
* @param packageId The package ID (full or partial)
|
|
25
|
+
* @returns True if an emitter exists, false otherwise
|
|
26
|
+
*/
|
|
27
|
+
has(packageId: PackageIdString): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Get all emitters in the map
|
|
30
|
+
* @returns An iterator of all EventEmitter instances
|
|
31
|
+
*/
|
|
32
|
+
values(): IterableIterator<EventEmitter>;
|
|
33
|
+
/**
|
|
34
|
+
* Clear all emitters from the map
|
|
35
|
+
*/
|
|
36
|
+
clear(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get the number of emitters in the map
|
|
39
|
+
*/
|
|
40
|
+
get size(): number;
|
|
41
|
+
}
|
|
42
|
+
export declare const TemplateEmitterMap: typeof PackageIdEmitterMap;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { partiallyQualified } from "./util.js";
|
|
2
|
+
/**
|
|
3
|
+
* A specialized Map wrapper for package ID emitters that uses partiallyQualified
|
|
4
|
+
* IDs for comparison to ensure consistent lookup regardless of package ID.
|
|
5
|
+
* Can be used for both template IDs and interface IDs.
|
|
6
|
+
*/
|
|
7
|
+
export class PackageIdEmitterMap {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.emitters = new Map();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Set an EventEmitter for a specific package ID
|
|
13
|
+
* @param packageId The full package ID (template or interface)
|
|
14
|
+
* @param emitter The EventEmitter instance
|
|
15
|
+
*/
|
|
16
|
+
set(packageId, emitter) {
|
|
17
|
+
const partialId = partiallyQualified(packageId);
|
|
18
|
+
this.emitters.set(partialId, { fullId: packageId, emitter });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the EventEmitter for a specific package ID
|
|
22
|
+
* @param packageId The package ID (full or partial)
|
|
23
|
+
* @returns The EventEmitter or undefined if not found
|
|
24
|
+
*/
|
|
25
|
+
get(packageId) {
|
|
26
|
+
const partialId = partiallyQualified(packageId);
|
|
27
|
+
return this.emitters.get(partialId)?.emitter;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if an emitter exists for a specific package ID
|
|
31
|
+
* @param packageId The package ID (full or partial)
|
|
32
|
+
* @returns True if an emitter exists, false otherwise
|
|
33
|
+
*/
|
|
34
|
+
has(packageId) {
|
|
35
|
+
const partialId = partiallyQualified(packageId);
|
|
36
|
+
return this.emitters.has(partialId);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get all emitters in the map
|
|
40
|
+
* @returns An iterator of all EventEmitter instances
|
|
41
|
+
*/
|
|
42
|
+
values() {
|
|
43
|
+
return Array.from(this.emitters.values())
|
|
44
|
+
.map(entry => entry.emitter)[Symbol.iterator]();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Clear all emitters from the map
|
|
48
|
+
*/
|
|
49
|
+
clear() {
|
|
50
|
+
this.emitters.clear();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the number of emitters in the map
|
|
54
|
+
*/
|
|
55
|
+
get size() {
|
|
56
|
+
return this.emitters.size;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Backward compatibility alias
|
|
60
|
+
export const TemplateEmitterMap = PackageIdEmitterMap;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Auto-generated file - do not edit manually
|
|
2
|
-
// Generated from /Users/stefanpla/Development/c7/sandbox/
|
|
2
|
+
// Generated from /Users/stefanpla/Development/c7/sandbox/stream_interfaces/c7_ledger/ledger/specs/asyncapi_3.4.7.yaml
|
|
3
3
|
export const ASYNCAPI_SCHEMA = `asyncapi: 2.6.0
|
|
4
4
|
info:
|
|
5
5
|
title: JSON Ledger API WebSocket endpoints
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Auto-generated file - do not edit manually
|
|
2
|
-
// Generated from /Users/stefanpla/Development/c7/sandbox/
|
|
2
|
+
// Generated from /Users/stefanpla/Development/c7/sandbox/stream_interfaces/c7_ledger/ledger/specs/openapi_3.4.7.yaml
|
|
3
3
|
export const OPENAPI_SCHEMA = `openapi: 3.0.3
|
|
4
4
|
info:
|
|
5
5
|
title: JSON Ledger API HTTP endpoints
|
package/lib/src/ledger.d.ts
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* Use this for most Daml application needs. Use TypedHttpClient directly when
|
|
17
17
|
* you need access to Canton-specific endpoints or full control over API calls.
|
|
18
18
|
*/
|
|
19
|
-
import { ContractId, Party, Choice, InterfaceCompanion, Template
|
|
20
|
-
import { AllocatePartyRequest, AllocatePartyResponse, Command, CreateCommand, CreateAndExerciseCommand, CreateEvent, ExerciseCommand, Event, Interface, LedgerOffset, Stream, PartyDetails, User, MultiStream, TemplateMapping, VersionedRegistry } from "./types.js";
|
|
19
|
+
import { ContractId, Party, Choice, InterfaceCompanion, Template } from "@daml/types";
|
|
20
|
+
import { AllocatePartyRequest, AllocatePartyResponse, Command, CreateCommand, CreateAndExerciseCommand, CreateEvent, ExerciseCommand, Event, Interface, LedgerOffset, Stream, InterfaceStream, InterfaceMapping, InterfaceMultiStream, PartyDetails, User, MultiStream, TemplateMapping, VersionedRegistry } from "./types.js";
|
|
21
21
|
import { ValidationMode } from "./validation.js";
|
|
22
22
|
import { PackageIdString } from "./valueTypes.js";
|
|
23
23
|
export declare function createCmd<T extends object, K = unknown>(template: Template<T, K, string>, payload: T): CreateCommand<T, K>;
|
|
@@ -63,6 +63,11 @@ export interface LedgerOptions {
|
|
|
63
63
|
* querying and decoding interfaces views.
|
|
64
64
|
*/
|
|
65
65
|
versionedRegistry?: VersionedRegistry;
|
|
66
|
+
/**
|
|
67
|
+
* Whether to automatically reconnect WebSocket streams on abnormal close (1006).
|
|
68
|
+
* Defaults to true for backward compatibility.
|
|
69
|
+
*/
|
|
70
|
+
autoReconnect?: boolean;
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
68
73
|
* Meant to be a simple replacement for Ledger from @daml/ledger
|
|
@@ -140,9 +145,12 @@ export declare class Ledger {
|
|
|
140
145
|
* @param offset Optional offset to start streaming from
|
|
141
146
|
* @param skipAcs Whether to skip archived contracts
|
|
142
147
|
* @param includeCreatedEventBlob Whether to include created event blobs
|
|
143
|
-
* @
|
|
148
|
+
* @param readAsParties Array of parties to stream for, if not specified default to
|
|
149
|
+
* the actAs parties of the user in the token.
|
|
150
|
+
* @returns A stream of events for the specified template or interface
|
|
144
151
|
*/
|
|
145
|
-
streamQuery<T extends object, K = unknown>(template:
|
|
152
|
+
streamQuery<T extends object, K = unknown>(template: Template<T, K>, offset?: LedgerOffset, skipAcs?: boolean, includeCreatedEventBlob?: boolean, readAsParties?: Party[]): Promise<Stream<T, K>>;
|
|
153
|
+
streamQueryInterface<I extends object, K = unknown>(interface_: InterfaceCompanion<I, K>, offset?: LedgerOffset, skipAcs?: boolean, includeCreatedEventBlob?: boolean, readAsParties?: Party[]): Promise<InterfaceStream<I>>;
|
|
146
154
|
/**
|
|
147
155
|
* Create a type-safe MultiStream for working with multiple templates
|
|
148
156
|
*
|
|
@@ -174,14 +182,16 @@ export declare class Ledger {
|
|
|
174
182
|
* stream.start();
|
|
175
183
|
* ```
|
|
176
184
|
*
|
|
177
|
-
* @param
|
|
185
|
+
* @param tm Template mapping for the streams
|
|
178
186
|
* @param offset Optional offset to start streaming from
|
|
187
|
+
* @param skipAcs Whether to skip loading the initial active contract set
|
|
179
188
|
* @param includeCreatedEventBlob Whether to include created event blobs
|
|
180
189
|
* @param readAsParties Array of parties to stream for, if not specified default to
|
|
181
190
|
* the actAs parties of the user in the token.
|
|
182
191
|
* @returns A type-safe MultiStream for working with multiple templates
|
|
183
192
|
*/
|
|
184
193
|
createMultiStream<TM extends TemplateMapping>(tm: TM, offset?: LedgerOffset, skipAcs?: boolean, includeCreatedEventBlob?: boolean, readAsParties?: Party[]): Promise<MultiStream<TM>>;
|
|
194
|
+
createMultiInterfaceStream<IM extends InterfaceMapping>(im: IM, offset?: LedgerOffset, skipAcs?: boolean, includeCreatedEventBlob?: boolean, readAsParties?: Party[]): Promise<InterfaceMultiStream<IM>>;
|
|
185
195
|
getUserInfo(userId: string): Promise<User | null>;
|
|
186
196
|
getParties(): Promise<PartyDetails[]>;
|
|
187
197
|
allocateParty(request: AllocatePartyRequest): Promise<AllocatePartyResponse>;
|
package/lib/src/ledger.js
CHANGED
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
* you need access to Canton-specific endpoints or full control over API calls.
|
|
18
18
|
*/
|
|
19
19
|
import { lookupTemplate, } from "@daml/types";
|
|
20
|
-
import { decodeJwt } from "jose";
|
|
21
20
|
import { EventEmitter } from "eventemitter3";
|
|
22
21
|
import { logger } from "./logger.js";
|
|
22
|
+
import { logTokenExpiration } from "./token.js";
|
|
23
23
|
import { TypedHttpClient } from "./client.js";
|
|
24
24
|
import { WebSocketClient, isTransaction, } from "./websocket.js";
|
|
25
|
-
import { MultiStreamAdapter } from "./multistream.js";
|
|
25
|
+
import { MultiStreamAdapter, InterfaceMultiStreamImpl } from "./multistream.js";
|
|
26
26
|
import { matchesPartiallyQualified } from "./util.js";
|
|
27
27
|
import * as translate from "./translate.js";
|
|
28
28
|
import { createLedgerString, createPartyIdString, createNameString, createUserIdString, } from "./valueTypes.js";
|
|
@@ -30,6 +30,18 @@ import { createLedgerString, createPartyIdString, createNameString, createUserId
|
|
|
30
30
|
function isCreateEvent(event) {
|
|
31
31
|
return "CreatedEvent" in event;
|
|
32
32
|
}
|
|
33
|
+
function createEventWithoutDecoder(cantonEvent) {
|
|
34
|
+
return {
|
|
35
|
+
type: "create",
|
|
36
|
+
templateId: cantonEvent.templateId,
|
|
37
|
+
contractId: cantonEvent.contractId,
|
|
38
|
+
payload: cantonEvent.createArgument ?? {},
|
|
39
|
+
signatories: (cantonEvent.signatories || []),
|
|
40
|
+
observers: (cantonEvent.observers || []),
|
|
41
|
+
key: undefined,
|
|
42
|
+
createdEventBlob: cantonEvent.createdEventBlob || "",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
33
45
|
// This term is so overloaded, lets add a '_' to help differentiate
|
|
34
46
|
function createEvent_(cantonEvent, versionedRegistry) {
|
|
35
47
|
let t;
|
|
@@ -76,8 +88,8 @@ function archiveEvent_(cantonEvent) {
|
|
|
76
88
|
offset: cantonEvent.offset,
|
|
77
89
|
};
|
|
78
90
|
}
|
|
79
|
-
// It is possible that we query for a given
|
|
80
|
-
// but the underlying contract has multiple
|
|
91
|
+
// It is possible that we query for a given interface that we are interested in,
|
|
92
|
+
// but the underlying contract has multiple interface implementations that we are NOT
|
|
81
93
|
// interested in, and consequently not registered in our versionedRegistry.
|
|
82
94
|
// In this case we return null.
|
|
83
95
|
function interfaceEvent_(cantonEvent, interfaceView, versionedRegistry) {
|
|
@@ -103,6 +115,7 @@ function interfaceEvent_(cantonEvent, interfaceView, versionedRegistry) {
|
|
|
103
115
|
key: cantonEvent.contractKey,
|
|
104
116
|
createdEventBlob: cantonEvent.createdEventBlob || "",
|
|
105
117
|
interfaceView: decodedInterfaceView,
|
|
118
|
+
interfaceId: interfaceView.interfaceId,
|
|
106
119
|
packageVersion,
|
|
107
120
|
};
|
|
108
121
|
}
|
|
@@ -192,12 +205,12 @@ class LedgerStream {
|
|
|
192
205
|
/**
|
|
193
206
|
* Creates a new stream for active contracts
|
|
194
207
|
*
|
|
195
|
-
* @param
|
|
208
|
+
* @param filters The filters to stream contracts for - FilterSpec[]
|
|
196
209
|
* @param parties The parties to stream contracts for
|
|
197
210
|
* @param wsClient The WebSocket client to use
|
|
198
211
|
* @param activeAtOffset Optional offset to start streaming from
|
|
199
212
|
*/
|
|
200
|
-
constructor(
|
|
213
|
+
constructor(filters, parties, wsClient, startOffset, skipAcs = false, includeCreatedEventBlob = true, versionedRegistry, autoReconnect = false) {
|
|
201
214
|
this.eventEmitter = new EventEmitter();
|
|
202
215
|
this.state_ = "start";
|
|
203
216
|
this.parties = parties;
|
|
@@ -205,9 +218,12 @@ class LedgerStream {
|
|
|
205
218
|
this.offset = startOffset;
|
|
206
219
|
this.skipAcs = skipAcs;
|
|
207
220
|
this.versionedRegistry = versionedRegistry;
|
|
208
|
-
|
|
221
|
+
this.autoReconnect = autoReconnect;
|
|
222
|
+
let cumulative = filters.map(filter => {
|
|
209
223
|
return {
|
|
210
|
-
identifierFilter:
|
|
224
|
+
identifierFilter: filter.type === "template"
|
|
225
|
+
? templateFilter(filter.templateId, includeCreatedEventBlob)
|
|
226
|
+
: interfaceFilter(filter.interfaceId, includeCreatedEventBlob),
|
|
211
227
|
};
|
|
212
228
|
});
|
|
213
229
|
this.filtersByParty = this.parties.reduce((acc, party) => {
|
|
@@ -232,6 +248,12 @@ class LedgerStream {
|
|
|
232
248
|
// Start streaming and store the stop function
|
|
233
249
|
this.stopClient = this.wsClient.streamActiveContracts(request, response => this.handleActiveContractsResponse(response), error => this.handleError(error), (code, reason) => this.handleActiveContractsClose(code, reason));
|
|
234
250
|
}
|
|
251
|
+
handleCreatedEvent(createdEvent) {
|
|
252
|
+
this.eventEmitter.emit("create", createEvent_(createdEvent, this.versionedRegistry));
|
|
253
|
+
}
|
|
254
|
+
handleArchivedEvent(archivedEvent) {
|
|
255
|
+
this.eventEmitter.emit("archive", archiveEvent_(archivedEvent));
|
|
256
|
+
}
|
|
235
257
|
/**
|
|
236
258
|
* Handle responses from the active contracts stream
|
|
237
259
|
*/
|
|
@@ -251,7 +273,7 @@ class LedgerStream {
|
|
|
251
273
|
logger.warn(`While receiving ACS, updating offset from ${this.offset} to ${activeContract.createdEvent.offset}`);
|
|
252
274
|
this.offset = activeContract.createdEvent.offset;
|
|
253
275
|
}
|
|
254
|
-
this.
|
|
276
|
+
this.handleCreatedEvent(activeContract.createdEvent);
|
|
255
277
|
}
|
|
256
278
|
}
|
|
257
279
|
/**
|
|
@@ -308,11 +330,11 @@ class LedgerStream {
|
|
|
308
330
|
for (const event of jsTransaction.events || []) {
|
|
309
331
|
if ("CreatedEvent" in event) {
|
|
310
332
|
this.offset = Math.max(this.offset, event.CreatedEvent.offset);
|
|
311
|
-
this.
|
|
333
|
+
this.handleCreatedEvent(event.CreatedEvent);
|
|
312
334
|
}
|
|
313
335
|
else if ("ArchivedEvent" in event) {
|
|
314
336
|
this.offset = Math.max(this.offset, event.ArchivedEvent.offset);
|
|
315
|
-
this.
|
|
337
|
+
this.handleArchivedEvent(event.ArchivedEvent);
|
|
316
338
|
}
|
|
317
339
|
else {
|
|
318
340
|
logger.warn(`Unexpected event type in transaction stream: ${JSON.stringify(event)}`);
|
|
@@ -333,17 +355,22 @@ class LedgerStream {
|
|
|
333
355
|
// Emit close event
|
|
334
356
|
const closeEvent = { code, reason };
|
|
335
357
|
this.eventEmitter.emit("close", closeEvent);
|
|
336
|
-
// Auto-reconnect on abnormal close (1006) if stream is still active
|
|
337
|
-
if (code === 1006 && this.state_ === "live") {
|
|
358
|
+
// Auto-reconnect on abnormal close (1006) if stream is still active and auto-reconnect is enabled
|
|
359
|
+
if (code === 1006 && this.state_ === "live" && this.autoReconnect) {
|
|
338
360
|
logger.log(`WebSocket closed abnormally (1006), reconnecting in 3 seconds...`);
|
|
339
361
|
setTimeout(() => {
|
|
340
362
|
if (this.state_ === "live") {
|
|
341
363
|
// Double-check we're still active
|
|
342
364
|
logger.log(`Attempting to reconnect stream...`);
|
|
365
|
+
// Log token expiration info before reconnection attempt
|
|
366
|
+
logTokenExpiration(this.wsClient.getToken(), "WebSocket reconnection attempt");
|
|
343
367
|
this.startUpdatesStream();
|
|
344
368
|
}
|
|
345
369
|
}, 3000);
|
|
346
370
|
}
|
|
371
|
+
else if (code === 1006 && this.state_ === "live" && !this.autoReconnect) {
|
|
372
|
+
logger.log(`WebSocket closed abnormally (1006), but auto-reconnect is disabled`);
|
|
373
|
+
}
|
|
347
374
|
}
|
|
348
375
|
// This is a websocket error, not a Canton error
|
|
349
376
|
handleError(error) {
|
|
@@ -400,6 +427,65 @@ class LedgerStream {
|
|
|
400
427
|
// Clean up event listeners
|
|
401
428
|
this.eventEmitter.removeAllListeners();
|
|
402
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Update the authentication token and restart the stream
|
|
432
|
+
*/
|
|
433
|
+
updateToken(newToken) {
|
|
434
|
+
logger.debug(`🔄 Stream token update requested for state: ${this.state_}`);
|
|
435
|
+
logTokenExpiration(newToken, "Stream updateToken()");
|
|
436
|
+
// Update the token in the WebSocket client
|
|
437
|
+
this.wsClient.setToken(newToken);
|
|
438
|
+
logger.debug("WebSocket client token updated");
|
|
439
|
+
// Handle token update based on current stream state
|
|
440
|
+
switch (this.state_) {
|
|
441
|
+
case "live":
|
|
442
|
+
logger.debug(`Restarting live stream with new token`);
|
|
443
|
+
// Close current connection
|
|
444
|
+
if (this.stopClient) {
|
|
445
|
+
this.stopClient();
|
|
446
|
+
this.stopClient = undefined;
|
|
447
|
+
}
|
|
448
|
+
// Restart the updates stream with the new token
|
|
449
|
+
this.startUpdatesStream();
|
|
450
|
+
break;
|
|
451
|
+
case "init":
|
|
452
|
+
logger.debug(`Stream is in init state, will use new token when transitioning to live`);
|
|
453
|
+
// Stream is still in ACS phase, new token will be used when it transitions to updates
|
|
454
|
+
break;
|
|
455
|
+
case "start":
|
|
456
|
+
logger.debug(`Stream is in start state, token updated but no restart needed`);
|
|
457
|
+
break;
|
|
458
|
+
case "stop":
|
|
459
|
+
logger.debug(`Stream is stopped, token updated but no restart needed`);
|
|
460
|
+
break;
|
|
461
|
+
default:
|
|
462
|
+
// TypeScript exhaustiveness check
|
|
463
|
+
const _exhaustive = this.state_;
|
|
464
|
+
throw new Error(`Unknown stream state: ${_exhaustive}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
class InterfaceStreamImpl extends LedgerStream {
|
|
469
|
+
constructor(filters, parties, wsClient, startOffset,
|
|
470
|
+
// Required for interface streams
|
|
471
|
+
versionedRegistry, skipAcs = false, includeCreatedEventBlob = true, autoReconnect = false) {
|
|
472
|
+
super(filters, parties, wsClient, startOffset, skipAcs, includeCreatedEventBlob, versionedRegistry, autoReconnect);
|
|
473
|
+
}
|
|
474
|
+
on(type, listener) {
|
|
475
|
+
this.eventEmitter.on(type, listener);
|
|
476
|
+
}
|
|
477
|
+
off(type, listener) {
|
|
478
|
+
this.eventEmitter.off(type, listener);
|
|
479
|
+
}
|
|
480
|
+
handleCreatedEvent(createdEvent) {
|
|
481
|
+
this.eventEmitter.emit("create", createEventWithoutDecoder(createdEvent));
|
|
482
|
+
for (const interfaceView of createdEvent.interfaceViews ?? []) {
|
|
483
|
+
this.eventEmitter.emit("interfaceView", interfaceEvent_(createdEvent, interfaceView, this.versionedRegistry));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
handleArchivedEvent(archivedEvent) {
|
|
487
|
+
this.eventEmitter.emit("archive", archiveEvent_(archivedEvent));
|
|
488
|
+
}
|
|
403
489
|
}
|
|
404
490
|
/**
|
|
405
491
|
* Meant to be a simple replacement for Ledger from @daml/ledger
|
|
@@ -408,12 +494,9 @@ export class Ledger {
|
|
|
408
494
|
constructor(options) {
|
|
409
495
|
this.tokenUserInfo = null;
|
|
410
496
|
this.httpBaseUrl = options.httpBaseUrl;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
throw new Error(`Token payload missing 'sub' field`);
|
|
415
|
-
}
|
|
416
|
-
this.tokenUserId = payload.sub;
|
|
497
|
+
// Log token expiration info during ledger initialization
|
|
498
|
+
const tokenInfo = logTokenExpiration(options.token, `Ledger initialization`);
|
|
499
|
+
this.tokenUserId = tokenInfo.userId;
|
|
417
500
|
this.client = new TypedHttpClient({
|
|
418
501
|
token: options.token,
|
|
419
502
|
baseUrl: this.httpBaseUrl,
|
|
@@ -745,12 +828,22 @@ export class Ledger {
|
|
|
745
828
|
* @param offset Optional offset to start streaming from
|
|
746
829
|
* @param skipAcs Whether to skip archived contracts
|
|
747
830
|
* @param includeCreatedEventBlob Whether to include created event blobs
|
|
748
|
-
* @
|
|
831
|
+
* @param readAsParties Array of parties to stream for, if not specified default to
|
|
832
|
+
* the actAs parties of the user in the token.
|
|
833
|
+
* @returns A stream of events for the specified template or interface
|
|
749
834
|
*/
|
|
750
835
|
async streamQuery(template, offset = "end", skipAcs = false, includeCreatedEventBlob = false, readAsParties) {
|
|
751
836
|
const activeAtOffset = await this.resolveOffset(offset);
|
|
752
837
|
const parties_ = readAsParties || (await this.getTokenActAsParties());
|
|
753
|
-
return new LedgerStream([template.templateId], parties_, this.initClient(), activeAtOffset, skipAcs, includeCreatedEventBlob, this.options.versionedRegistry);
|
|
838
|
+
return new LedgerStream([{ type: "template", templateId: template.templateId }], parties_, this.initClient(), activeAtOffset, skipAcs, includeCreatedEventBlob, this.options.versionedRegistry, this.options.autoReconnect ?? true);
|
|
839
|
+
}
|
|
840
|
+
async streamQueryInterface(interface_, offset = "end", skipAcs = false, includeCreatedEventBlob = false, readAsParties) {
|
|
841
|
+
const activeAtOffset = await this.resolveOffset(offset);
|
|
842
|
+
const parties_ = readAsParties || (await this.getTokenActAsParties());
|
|
843
|
+
if (!this.options.versionedRegistry) {
|
|
844
|
+
throw new Error("VersionedRegistry expected for streamQueryInterface provided");
|
|
845
|
+
}
|
|
846
|
+
return new InterfaceStreamImpl([{ type: "interface", interfaceId: interface_.templateId }], parties_, this.initClient(), activeAtOffset, this.options.versionedRegistry, skipAcs, includeCreatedEventBlob, this.options.autoReconnect ?? true);
|
|
754
847
|
}
|
|
755
848
|
/**
|
|
756
849
|
* Create a type-safe MultiStream for working with multiple templates
|
|
@@ -783,8 +876,9 @@ export class Ledger {
|
|
|
783
876
|
* stream.start();
|
|
784
877
|
* ```
|
|
785
878
|
*
|
|
786
|
-
* @param
|
|
879
|
+
* @param tm Template mapping for the streams
|
|
787
880
|
* @param offset Optional offset to start streaming from
|
|
881
|
+
* @param skipAcs Whether to skip loading the initial active contract set
|
|
788
882
|
* @param includeCreatedEventBlob Whether to include created event blobs
|
|
789
883
|
* @param readAsParties Array of parties to stream for, if not specified default to
|
|
790
884
|
* the actAs parties of the user in the token.
|
|
@@ -792,11 +886,25 @@ export class Ledger {
|
|
|
792
886
|
*/
|
|
793
887
|
async createMultiStream(tm, offset = "end", skipAcs = false, includeCreatedEventBlob = false, readAsParties) {
|
|
794
888
|
const activeAtOffset = await this.resolveOffset(offset);
|
|
795
|
-
const
|
|
889
|
+
const filters = Object.keys(tm).map((id) => {
|
|
890
|
+
return { type: 'template', templateId: id };
|
|
891
|
+
});
|
|
796
892
|
const parties_ = readAsParties || (await this.getTokenActAsParties());
|
|
797
|
-
const stream = new LedgerStream(
|
|
893
|
+
const stream = new LedgerStream(filters, parties_, this.initClient(), activeAtOffset, skipAcs, includeCreatedEventBlob, this.options.versionedRegistry, this.options.autoReconnect ?? true);
|
|
798
894
|
return new MultiStreamAdapter(stream);
|
|
799
895
|
}
|
|
896
|
+
async createMultiInterfaceStream(im, offset = "end", skipAcs = false, includeCreatedEventBlob = false, readAsParties) {
|
|
897
|
+
const activeAtOffset = await this.resolveOffset(offset);
|
|
898
|
+
const filters = Object.keys(im).map((id) => {
|
|
899
|
+
return { type: 'interface', interfaceId: id };
|
|
900
|
+
});
|
|
901
|
+
const parties_ = readAsParties || (await this.getTokenActAsParties());
|
|
902
|
+
if (!this.options.versionedRegistry) {
|
|
903
|
+
throw new Error("VersionedRegistry expected for createMultiInterfaceStream");
|
|
904
|
+
}
|
|
905
|
+
const stream = new InterfaceStreamImpl(filters, parties_, this.initClient(), activeAtOffset, this.options.versionedRegistry, skipAcs, includeCreatedEventBlob, this.options.autoReconnect ?? true);
|
|
906
|
+
return new InterfaceMultiStreamImpl(stream);
|
|
907
|
+
}
|
|
800
908
|
// User information
|
|
801
909
|
async getUserInfo(userId) {
|
|
802
910
|
const response = await this.client.getUserInfo(userId);
|
package/lib/src/multistream.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EventEmitter } from "eventemitter3";
|
|
2
|
+
import { ArchiveEvent, CantonError, CreateEvent, Interface, InterfaceMapping, InterfaceMultiStream, InterfaceStream, MultiStream, Stream, StreamState, TemplateMapping } from "./types.js";
|
|
3
|
+
import { PackageIdString } from "./valueTypes.js";
|
|
4
|
+
import { PackageIdEmitterMap } from "./PackageIdEmitterMap.js";
|
|
2
5
|
/**
|
|
3
6
|
* Adapts a Stream instance to the MultiStream interface
|
|
4
7
|
* Provides template-specific event handlers with proper typing
|
|
5
8
|
*/
|
|
6
9
|
export declare class MultiStreamAdapter<TM extends TemplateMapping> implements MultiStream<TM> {
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
protected stream: Stream<object, unknown>;
|
|
11
|
+
protected packageIdEmitters: PackageIdEmitterMap;
|
|
9
12
|
private errorEmitter;
|
|
10
13
|
private stateEmitter;
|
|
11
14
|
/**
|
|
@@ -14,17 +17,17 @@ export declare class MultiStreamAdapter<TM extends TemplateMapping> implements M
|
|
|
14
17
|
*/
|
|
15
18
|
constructor(stream: Stream<object, unknown>);
|
|
16
19
|
/**
|
|
17
|
-
* Get or create an EventEmitter for a specific
|
|
20
|
+
* Get or create an EventEmitter for a specific package ID
|
|
18
21
|
*/
|
|
19
|
-
|
|
22
|
+
protected getEmitterForPackageId(packageId: PackageIdString): EventEmitter;
|
|
20
23
|
/**
|
|
21
24
|
* Handle create events from the underlying stream
|
|
22
|
-
* and route them to the appropriate
|
|
25
|
+
* and route them to the appropriate package ID emitter
|
|
23
26
|
*/
|
|
24
27
|
private handleCreateEvent;
|
|
25
28
|
/**
|
|
26
29
|
* Handle archive events from the underlying stream
|
|
27
|
-
* and route them to the appropriate
|
|
30
|
+
* and route them to the appropriate package ID emitter
|
|
28
31
|
*/
|
|
29
32
|
private handleArchiveEvent;
|
|
30
33
|
/**
|
|
@@ -44,4 +47,28 @@ export declare class MultiStreamAdapter<TM extends TemplateMapping> implements M
|
|
|
44
47
|
start(): void;
|
|
45
48
|
state(): StreamState;
|
|
46
49
|
close(): void;
|
|
50
|
+
updateToken(newToken: string): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extends MultiStreamAdapter to add interface-specific event handling
|
|
54
|
+
* Provides both template-specific and interface-specific event handlers with proper typing
|
|
55
|
+
*/
|
|
56
|
+
export declare class InterfaceMultiStreamImpl<IM extends InterfaceMapping> extends MultiStreamAdapter<{
|
|
57
|
+
[K in keyof IM]: {
|
|
58
|
+
contractType: object;
|
|
59
|
+
keyType: unknown;
|
|
60
|
+
};
|
|
61
|
+
}> implements InterfaceMultiStream<IM> {
|
|
62
|
+
private interfaceStream;
|
|
63
|
+
/**
|
|
64
|
+
* Create a new InterfaceMultiStream adapter
|
|
65
|
+
* @param stream The underlying InterfaceStream instance
|
|
66
|
+
*/
|
|
67
|
+
constructor(stream: InterfaceStream<object>);
|
|
68
|
+
/**
|
|
69
|
+
* Handle interfaceView events from the underlying stream
|
|
70
|
+
*/
|
|
71
|
+
private handleInterfaceViewEvent;
|
|
72
|
+
onInterfaceView<IID extends keyof IM>(interfaceId: IID, listener: (event: Interface<IM[IID]["contractType"]>) => void): void;
|
|
73
|
+
offInterfaceView<IID extends keyof IM>(interfaceId: IID, listener: (event: Interface<IM[IID]["contractType"]>) => void): void;
|
|
47
74
|
}
|