@c7-digital/ledger 0.0.3 → 0.0.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- # Ledger Build
1
+ # Ledger Build
2
2
 
3
3
  A standalone, repeatable build system for generating TypeScript types from OpenAPI and AsyncAPI specifications with value.proto branding.
4
4
 
@@ -30,7 +30,7 @@ pnpm exec ledger-build --sdk-version=3.4.0 # Target specific SDK version
30
30
  ### Programmatic Usage
31
31
 
32
32
  ```typescript
33
- import { build } from "@c7-digital/ledger/scripts/build";
33
+ import { build } from "@c7/ledger/scripts/build";
34
34
 
35
35
  await build();
36
36
  ```
@@ -89,7 +89,7 @@ To use this build system in other projects:
89
89
  1. **Install the package**:
90
90
 
91
91
  ```bash
92
- pnpm install @c7-digital/ledger
92
+ pnpm install @c7/ledger
93
93
  ```
94
94
 
95
95
  2. **Add to package.json**:
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @c7-digital/ledger
1
+ # @c7/ledger
2
2
 
3
3
  OpenAPI v2 compatible Daml ledger client (that could replace `@daml/ledger`) for the new Canton JSON API v2.
4
4
 
@@ -27,7 +27,7 @@ This package follows the Canton SDK versioning scheme. The package version match
27
27
  ## Installation
28
28
 
29
29
  ```bash
30
- pnpm install @c7-digital/ledger
30
+ pnpm install @c7/ledger
31
31
  ```
32
32
 
33
33
  Or build from source:
@@ -40,7 +40,7 @@ pnpm build
40
40
  ## Usage
41
41
 
42
42
  ```typescript
43
- import { Ledger } from "@c7-digital/ledger";
43
+ import { Ledger } from "@c7/ledger";
44
44
 
45
45
  const ledger = new Ledger({
46
46
  token: "your-jwt-token",
@@ -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/publish_to_npm/c7_ledger/ledger/specs/asyncapi_3.4.7.yaml
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/publish_to_npm/c7_ledger/ledger/specs/openapi_3.4.7.yaml
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
@@ -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, TemplateOrInterface } from "@daml/types";
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
- * @returns A stream of events for the specified template
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: TemplateOrInterface<T, K>, offset?: LedgerOffset, skipAcs?: boolean, includeCreatedEventBlob?: boolean, readAsParties?: Party[]): Promise<Stream<T, K>>;
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 templates Array of templates to stream
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 interview that we are interested in,
80
- // but the underlying contract has multiple interview implementations that we are NOT
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 template The template to stream contracts for
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(templateIds, parties, wsClient, startOffset, skipAcs = false, includeCreatedEventBlob = true, versionedRegistry) {
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
- let cumulative = templateIds.map(templateId => {
221
+ this.autoReconnect = autoReconnect;
222
+ let cumulative = filters.map(filter => {
209
223
  return {
210
- identifierFilter: templateFilter(templateId, includeCreatedEventBlob),
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.eventEmitter.emit("create", createEvent_(activeContract.createdEvent, this.versionedRegistry));
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.eventEmitter.emit("create", createEvent_(event.CreatedEvent, this.versionedRegistry));
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.eventEmitter.emit("archive", archiveEvent_(event.ArchivedEvent));
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
- const payload = decodeJwt(options.token);
412
- logger.debug(`Token payload: ${JSON.stringify(payload)}`);
413
- if (payload.sub === undefined) {
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
- * @returns A stream of events for the specified template
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 templates Array of templates to stream
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 templateIds = Object.keys(tm);
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(templateIds, parties_, this.initClient(), activeAtOffset, skipAcs, includeCreatedEventBlob, this.options.versionedRegistry);
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);
@@ -1,11 +1,14 @@
1
- import { ArchiveEvent, CantonError, CreateEvent, MultiStream, Stream, StreamState, TemplateMapping } from "./types.js";
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
- private stream;
8
- private templateEmitters;
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 template
20
+ * Get or create an EventEmitter for a specific package ID
18
21
  */
19
- private getEmitterForTemplate;
22
+ protected getEmitterForPackageId(packageId: PackageIdString): EventEmitter;
20
23
  /**
21
24
  * Handle create events from the underlying stream
22
- * and route them to the appropriate template emitter
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 template emitter
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
  }