@distrohelena/canton-typescript-sdk 0.1.33 → 0.1.36

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.
Files changed (115) hide show
  1. package/README.md +211 -18
  2. package/dist/cjs/client/external-party-activation-client.js +20 -10
  3. package/dist/cjs/core/errors/active-contracts-traversal-error.js +12 -0
  4. package/dist/cjs/core/polling/poll-until-async.js +26 -0
  5. package/dist/cjs/core/types/active-contracts-traversal-options.js +26 -0
  6. package/dist/cjs/core/types/command-deduplication-period.js +2 -0
  7. package/dist/cjs/core/types/operation-deadline.js +42 -0
  8. package/dist/cjs/core/types/requests/allocate-party-request.js +2 -0
  9. package/dist/cjs/core/types/requests/finalize-decentralized-party-request.js +6 -0
  10. package/dist/cjs/core/types/requests/submit-command-request.js +43 -0
  11. package/dist/cjs/core/types/requests/wait-for-party-hosting-request.js +40 -0
  12. package/dist/cjs/index.js +13 -27
  13. package/dist/cjs/query/grpc/grpc-contract-query-client.js +33 -16
  14. package/dist/cjs/services/party-management/decentralized-party-lifecycle.js +12 -1
  15. package/dist/cjs/services/party-management/party-management-service-client.js +3 -0
  16. package/dist/cjs/services/state/state-service-client.js +57 -1
  17. package/dist/cjs/services/topology-aggregation/topology-aggregation-service-client.js +36 -0
  18. package/dist/cjs/transports/grpc/grpc-channel-factory.js +5 -7
  19. package/dist/cjs/transports/grpc/grpc-transport.js +31 -15
  20. package/dist/cjs/transports/grpc/mappers/command-deduplication-mapper.js +22 -0
  21. package/dist/cjs/transports/grpc/mappers/commands-mapper.js +3 -4
  22. package/dist/cjs/transports/grpc/mappers/interactive-command-mapper.js +5 -3
  23. package/dist/cjs/transports/grpc/mappers/parties-mapper.js +1 -12
  24. package/dist/cjs/transports/json/mappers/commands-mapper.js +5 -1
  25. package/dist/cjs/transports/json/mappers/parties-mapper.js +1 -0
  26. package/dist/client/external-party-activation-client.js +20 -10
  27. package/dist/client/external-party-activation-response.d.ts +1 -1
  28. package/dist/core/errors/active-contracts-traversal-error.d.ts +5 -0
  29. package/dist/core/errors/active-contracts-traversal-error.js +8 -0
  30. package/dist/core/polling/poll-until-async.d.ts +10 -0
  31. package/dist/core/polling/poll-until-async.js +23 -0
  32. package/dist/core/transports/transport.interface.d.ts +11 -3
  33. package/dist/core/types/active-contracts-traversal-options.d.ts +11 -0
  34. package/dist/core/types/active-contracts-traversal-options.js +22 -0
  35. package/dist/core/types/command-deduplication-period.d.ts +7 -0
  36. package/dist/core/types/command-deduplication-period.js +1 -0
  37. package/dist/core/types/operation-deadline.d.ts +13 -0
  38. package/dist/core/types/operation-deadline.js +38 -0
  39. package/dist/core/types/requests/allocate-party-request.d.ts +2 -0
  40. package/dist/core/types/requests/allocate-party-request.js +2 -0
  41. package/dist/core/types/requests/finalize-decentralized-party-request.d.ts +6 -0
  42. package/dist/core/types/requests/finalize-decentralized-party-request.js +6 -0
  43. package/dist/core/types/requests/submit-command-request.d.ts +5 -0
  44. package/dist/core/types/requests/submit-command-request.js +43 -0
  45. package/dist/core/types/requests/wait-for-party-hosting-request.d.ts +14 -0
  46. package/dist/core/types/requests/wait-for-party-hosting-request.js +36 -0
  47. package/dist/index.d.ts +5 -11
  48. package/dist/index.js +4 -11
  49. package/dist/query/grpc/grpc-contract-query-client.js +33 -16
  50. package/dist/services/party-management/decentralized-party-lifecycle.d.ts +1 -1
  51. package/dist/services/party-management/decentralized-party-lifecycle.js +12 -1
  52. package/dist/services/party-management/party-management-service-client.js +3 -0
  53. package/dist/services/state/state-service-client.d.ts +5 -1
  54. package/dist/services/state/state-service-client.js +57 -1
  55. package/dist/services/topology-aggregation/topology-aggregation-service-client.d.ts +4 -1
  56. package/dist/services/topology-aggregation/topology-aggregation-service-client.js +36 -0
  57. package/dist/services/topology-manager-read/topology-manager-read-service-client.d.ts +3 -1
  58. package/dist/services/user-management/user-management-service-client.d.ts +7 -1
  59. package/dist/transports/grpc/grpc-channel-factory.d.ts +0 -1
  60. package/dist/transports/grpc/grpc-channel-factory.js +5 -7
  61. package/dist/transports/grpc/grpc-transport.d.ts +10 -3
  62. package/dist/transports/grpc/grpc-transport.js +31 -15
  63. package/dist/transports/grpc/mappers/command-deduplication-mapper.d.ts +5 -0
  64. package/dist/transports/grpc/mappers/command-deduplication-mapper.js +19 -0
  65. package/dist/transports/grpc/mappers/commands-mapper.js +3 -4
  66. package/dist/transports/grpc/mappers/interactive-command-mapper.js +5 -3
  67. package/dist/transports/grpc/mappers/parties-mapper.d.ts +3 -7
  68. package/dist/transports/grpc/mappers/parties-mapper.js +1 -10
  69. package/dist/transports/json/json-transport.d.ts +10 -5
  70. package/dist/transports/json/mappers/commands-mapper.js +5 -1
  71. package/dist/transports/json/mappers/parties-mapper.d.ts +1 -0
  72. package/dist/transports/json/mappers/parties-mapper.js +1 -0
  73. package/node/participant-358-synchronizer.mjs +33 -1
  74. package/node/start-local-participant-358.sh +33 -3
  75. package/node/test-participant-358-sidecar.sh +31 -1
  76. package/package.json +15 -1
  77. package/dist/cjs/core/types/health-check-status.js +0 -10
  78. package/dist/cjs/core/types/requests/get-ledger-api-version-request.js +0 -6
  79. package/dist/cjs/core/types/requests/get-package-references-request.js +0 -10
  80. package/dist/cjs/core/types/requests/get-participant-id-request.js +0 -7
  81. package/dist/cjs/core/types/requests/get-participant-status-request.js +0 -6
  82. package/dist/cjs/core/types/requests/health-check-request.js +0 -10
  83. package/dist/cjs/core/types/responses/get-ledger-api-version-response.js +0 -12
  84. package/dist/cjs/core/types/responses/get-package-references-response.js +0 -10
  85. package/dist/cjs/core/types/responses/get-participant-id-response.js +0 -10
  86. package/dist/cjs/core/types/responses/get-participant-status-response.js +0 -12
  87. package/dist/cjs/core/types/responses/health-check-response.js +0 -10
  88. package/dist/cjs/transports/grpc/mappers/health-mapper.js +0 -24
  89. package/dist/cjs/transports/grpc/mappers/participant-status-mapper.js +0 -58
  90. package/dist/core/types/health-check-status.d.ts +0 -6
  91. package/dist/core/types/health-check-status.js +0 -7
  92. package/dist/core/types/requests/get-ledger-api-version-request.d.ts +0 -2
  93. package/dist/core/types/requests/get-ledger-api-version-request.js +0 -2
  94. package/dist/core/types/requests/get-package-references-request.d.ts +0 -6
  95. package/dist/core/types/requests/get-package-references-request.js +0 -6
  96. package/dist/core/types/requests/get-participant-id-request.d.ts +0 -3
  97. package/dist/core/types/requests/get-participant-id-request.js +0 -3
  98. package/dist/core/types/requests/get-participant-status-request.d.ts +0 -2
  99. package/dist/core/types/requests/get-participant-status-request.js +0 -2
  100. package/dist/core/types/requests/health-check-request.d.ts +0 -6
  101. package/dist/core/types/requests/health-check-request.js +0 -6
  102. package/dist/core/types/responses/get-ledger-api-version-response.d.ts +0 -8
  103. package/dist/core/types/responses/get-ledger-api-version-response.js +0 -8
  104. package/dist/core/types/responses/get-package-references-response.d.ts +0 -7
  105. package/dist/core/types/responses/get-package-references-response.js +0 -6
  106. package/dist/core/types/responses/get-participant-id-response.d.ts +0 -6
  107. package/dist/core/types/responses/get-participant-id-response.js +0 -6
  108. package/dist/core/types/responses/get-participant-status-response.d.ts +0 -10
  109. package/dist/core/types/responses/get-participant-status-response.js +0 -8
  110. package/dist/core/types/responses/health-check-response.d.ts +0 -7
  111. package/dist/core/types/responses/health-check-response.js +0 -6
  112. package/dist/transports/grpc/mappers/health-mapper.d.ts +0 -5
  113. package/dist/transports/grpc/mappers/health-mapper.js +0 -21
  114. package/dist/transports/grpc/mappers/participant-status-mapper.d.ts +0 -5
  115. package/dist/transports/grpc/mappers/participant-status-mapper.js +0 -54
package/README.md CHANGED
@@ -92,6 +92,178 @@ topology shape: a serial-1 decentralized namespace definition, one self-root
92
92
  namespace certificate per owner, and a PartyToParticipant containing the party
93
93
  signing key. No deprecated PartyToKeyMapping is created.
94
94
 
95
+ The SDK preserves `identityProviderId`, `userId`, and `waitForAllocation`
96
+ through decentralized prepare/finalize. Canton ignores `waitForAllocation` for
97
+ decentralized parties, so the example explicitly calls
98
+ `topologyAggregationService.waitForPartyHostingAsync(...)` to prove that the
99
+ expected participant is hosting the party on the expected synchronizer. That
100
+ aggregate topology check is the same on Participant 3.5.7 and 3.5.8.
101
+
102
+ ## Standalone TypeScript examples
103
+
104
+ The repository includes runnable TypeScript examples for a local Canton
105
+ participant. Check them before running a lifecycle:
106
+
107
+ ```bash
108
+ npm run examples:check
109
+ ```
110
+
111
+ The setup and party examples are available individually:
112
+
113
+ ```bash
114
+ npm run example:init
115
+ npm run example:tls
116
+ npm run example:jwt
117
+ npm run example:party:hosted
118
+ npm run example:party:external
119
+ npm run example:party:decentralized
120
+ ```
121
+
122
+ For the application lifecycle, run these in order:
123
+
124
+ ```bash
125
+ npm run example:dar:upload
126
+ npm run example:contract:create-exercise
127
+ npm run example:contract:query
128
+ npm run example:updates:stream
129
+ npm run example:user:rights
130
+ npm run example:topology:party-hosting
131
+ ```
132
+
133
+ ### Workflow examples
134
+
135
+ The seven workflow examples are standalone proofs, not a sequence:
136
+ each uploads or verifies the fixture DAR, resolves a party, reads the
137
+ participant status, and creates its own run-scoped data. First make a Canton
138
+ participant available and verify the source with `npm run examples:check`; the
139
+ default endpoints and authentication environment variables are documented below.
140
+ The pruning preflight is a separate read-only operator check and does none of
141
+ those setup or mutation steps. Run any example independently:
142
+
143
+ ```bash
144
+ npm run example:workflow:atomic
145
+ npm run example:workflow:retry
146
+ npm run example:workflow:resume
147
+ npm run example:workflow:stale-contract
148
+ npm run example:workflow:command-completion
149
+ npm run example:workflow:contract-lifecycle-audit
150
+ npm run example:workflow:update-lookup-reconciliation
151
+ npm run example:workflow:pruning-preflight
152
+ ```
153
+
154
+ Those seven established stateful workflows intentionally leave durable state behind. A missing `SDK_EXAMPLE_PARTY`
155
+ causes fallback party allocation, which creates durable topology state; every
156
+ one also creates durable contracts. Set `SDK_EXAMPLE_PARTY` to an existing
157
+ party to rerun the established workflows against that party and avoid fallback
158
+ allocation. The fixture DAR remains installed after a run.
159
+
160
+ The completion-correlation workflow (`npm run example:workflow:command-completion`)
161
+ is a standalone successful proof that creates durable Message state. It uses the
162
+ same normal `SDK_EXAMPLE_*` configuration: `SDK_EXAMPLE_LEDGER_ENDPOINT`,
163
+ `SDK_EXAMPLE_LEDGER_ADMIN_ENDPOINT`, `SDK_EXAMPLE_PARTICIPANT_ADMIN_ENDPOINT`,
164
+ `SDK_EXAMPLE_BEARER_TOKEN`, `SDK_EXAMPLE_LEDGER_BEARER_TOKEN`,
165
+ `SDK_EXAMPLE_LEDGER_ADMIN_BEARER_TOKEN`,
166
+ `SDK_EXAMPLE_PARTICIPANT_ADMIN_BEARER_TOKEN`,
167
+ `SDK_EXAMPLE_TLS_ROOT_CERTIFICATE`, `SDK_EXAMPLE_PARTY`,
168
+ `SDK_EXAMPLE_PARTY_PREFIX`, and `SDK_EXAMPLE_TIMEOUT_MS`.
169
+ `SDK_EXAMPLE_USER_ID` is mandatory: absent or blank input is rejected, while
170
+ every nonblank value is preserved untrimmed and exactly submitted and exactly
171
+ matched in the completion. With bearer authentication, the configured declared
172
+ user must equal the token's Ledger API user/subject; the example does not inspect
173
+ the token. It keeps the ledger end as its saved exclusive offset and begins the
174
+ first stream read before submission.
175
+ No public wait-for-command-completion helper or API is introduced; the
176
+ correlation helper remains example-only.
177
+ The completion-correlation example asserts successful correlation only; it does
178
+ not assert rejected-command correlation. On both participant observations, the
179
+ stream-first rejected-command probes observed no exact completion before their
180
+ bounded stream transport errors.
181
+
182
+ The contract-lifecycle audit workflow (`npm run example:workflow:contract-lifecycle-audit`) is a standalone gRPC-only proof. It uses the normal `SDK_EXAMPLE_*` endpoint, authentication, party, and timeout configuration: `SDK_EXAMPLE_LEDGER_ENDPOINT`, `SDK_EXAMPLE_LEDGER_ADMIN_ENDPOINT`, `SDK_EXAMPLE_PARTICIPANT_ADMIN_ENDPOINT`, `SDK_EXAMPLE_BEARER_TOKEN`, `SDK_EXAMPLE_LEDGER_BEARER_TOKEN`, `SDK_EXAMPLE_LEDGER_ADMIN_BEARER_TOKEN`, `SDK_EXAMPLE_PARTICIPANT_ADMIN_BEARER_TOKEN`, `SDK_EXAMPLE_PARTY`, `SDK_EXAMPLE_PARTY_PREFIX`, and `SDK_EXAMPLE_TIMEOUT_MS`. An explicit `SDK_EXAMPLE_PARTY` is reused; otherwise fallback allocation creates durable topology. The fixture upload leaves a durable DAR, and the workflow leaves durable contracts. It uses the alpha ContractService to prove the original active Message before replacement and the replacement active Message afterward, then uses EventQuery for the original contract's create/archive history. It makes no post-archive ContractService claim for the original.
183
+
184
+ The update-lookup reconciliation workflow (`npm run example:workflow:update-lookup-reconciliation`) is a standalone gRPC-only proof that observes one exact self-party Message transaction from `UpdateService.GetUpdates`, then immediately reconciles it through `getUpdateById` and `getUpdateByOffset`. It uses the normal `SDK_EXAMPLE_*` endpoint, authentication, party, and timeout configuration, including `SDK_EXAMPLE_PARTY` and `SDK_EXAMPLE_TIMEOUT_MS`; first run `npm run examples:check` and make an authenticated participant available. An explicit party is reused, while fallback allocation creates durable topology; the fixture upload leaves a durable DAR and the workflow leaves durable contracts. The same unchanged implementation is tested against authenticated Participant 3.5.7 and the isolated Participant 3.5.8 sidecar, in default-party and explicit-party modes.
185
+
186
+ The pruning-preflight workflow (`npm run example:workflow:pruning-preflight`) is a standalone gRPC-only, read-only operator check. It requires `SDK_EXAMPLE_OFFSET` to be a canonical positive decimal integer and uses the normal endpoint, authentication, and timeout variables: `SDK_EXAMPLE_LEDGER_ENDPOINT`, `SDK_EXAMPLE_LEDGER_ADMIN_ENDPOINT`, `SDK_EXAMPLE_PARTICIPANT_ADMIN_ENDPOINT`, `SDK_EXAMPLE_BEARER_TOKEN`, `SDK_EXAMPLE_LEDGER_BEARER_TOKEN`, `SDK_EXAMPLE_LEDGER_ADMIN_BEARER_TOKEN`, `SDK_EXAMPLE_PARTICIPANT_ADMIN_BEARER_TOKEN`, and `SDK_EXAMPLE_TIMEOUT_MS`. The participant-admin credential is required for the schedule and safe-pruning context reads. Its safe-pruning context request sends the saved ledger end and a validated current timestamp encoded in `beforeOrAt`; it leaves commitment-state absent. It does not mutate the participant or create durable state: no party, DAR, command, update query, schedule change, or pruning request is made. The later participant watermark classifies the supplied offset as `alreadyPruned`, `beyondLedgerEnd`, or `notObservedPruned`; all-divulged watermarks and schedule/safe-pruning context are reported separately and do not alter that result. `notObservedPruned` is not proven queryable, because pruning can race after the later observation and other query preconditions can still fail. The unchanged implementation is supported on Participant 3.5.7 and the isolated Participant 3.5.8 sidecar.
187
+
188
+ Each of the seven established stateful workflows prints its actor plus the full participant version returned by the
189
+ authenticated status API, its parsed release core, and its selected path:
190
+ `Participant version:`, `Release core:`, and `Compatibility path:`. The current
191
+ stateful-workflow implementation uses one common-code path for release cores 3.5.7 and 3.5.8. It
192
+ adds a version-specific behavioral difference only after live evidence proves
193
+ one; it does not infer compatibility from a container tag or endpoint.
194
+
195
+ - `example:workflow:atomic` first proves that an invalid choice is rejected,
196
+ then proves an atomic create-and-exercise by reading the created replacement
197
+ and its exact text.
198
+ - `example:workflow:retry` submits a caller-controlled command ID with a
199
+ deduplication duration, retries the exact same request, classifies the
200
+ duplicate outcome, and proves that exactly one matching contract is active.
201
+ - `example:workflow:resume` saves the ledger end before its post-offset create,
202
+ proves the intentionally idle stream timeout, then resumes exclusively after
203
+ that saved offset and rejects a pre-offset contract if it appears.
204
+ - `example:workflow:stale-contract` proves archive/replacement state and then
205
+ proves that exercising the archived contract is rejected.
206
+
207
+ Expected failures are accepted only through structured error classification:
208
+ gRPC status code, decoded status, operation, and the selected compatibility
209
+ path—not prose matching. The retry example uses one explicit command ID and
210
+ deduplication period for the exact same request; changing either value is a new
211
+ request and is not a retry proof. The resume example treats the saved offset as
212
+ exclusive, so its resumed stream must observe only updates after the saved
213
+ ledger end.
214
+
215
+ The seven established stateful workflow sources and unit contracts were developed
216
+ and live-tested against authenticated Participant 3.5.7 and the isolated
217
+ Participant 3.5.8. Both final-tree matrices selected the same unchanged implementation and common
218
+ compatibility path. The normalized outcome comparison is identical: atomic
219
+ reports `invalidChoice` before its replacement proof, retry reports
220
+ `duplicateCommand` with one active contract, resume reports `idle-timeout` and
221
+ a post-offset update, and stale-contract reports `staleContract`.
222
+
223
+ For those seven established stateful workflows, the best multi-version path reads the authenticated full version, parses its
224
+ release core, and uses data-only structured compatibility for observed
225
+ outcomes. A behavioral difference is introduced only after live proof; it is
226
+ never inferred from a container tag, endpoint, or prose error message. For the
227
+ isolated 3.5.8 sidecar, use the SDK sidecar launcher's protected documented
228
+ credential refresh flow in a local child shell and refresh before expiry. Never
229
+ log its output or expose refreshed credentials beyond that child. Use the
230
+ documented `SDK_EXAMPLE_*` environment variables when running the workflow
231
+ commands.
232
+
233
+ By default, Ledger and Ledger Admin use `localhost:3901`; Participant Admin
234
+ uses `localhost:3902`. Override them with
235
+ `SDK_EXAMPLE_LEDGER_ENDPOINT`, `SDK_EXAMPLE_LEDGER_ADMIN_ENDPOINT`, and
236
+ `SDK_EXAMPLE_PARTICIPANT_ADMIN_ENDPOINT`. Use `SDK_EXAMPLE_BEARER_TOKEN` for a
237
+ shared bearer token, or set `SDK_EXAMPLE_LEDGER_BEARER_TOKEN`,
238
+ `SDK_EXAMPLE_LEDGER_ADMIN_BEARER_TOKEN`, and
239
+ `SDK_EXAMPLE_PARTICIPANT_ADMIN_BEARER_TOKEN` per surface. The examples also
240
+ accept `SDK_EXAMPLE_PARTY`, `SDK_EXAMPLE_USER_ID`, `SDK_EXAMPLE_SYNCHRONIZER`,
241
+ and `SDK_EXAMPLE_TIMEOUT_MS`; generated fallback party names use
242
+ `SDK_EXAMPLE_PARTY_PREFIX` when supplied. For TLS, set
243
+ `SDK_EXAMPLE_TLS_ROOT_CERTIFICATE` to a custom CA certificate path.
244
+
245
+ Some commands intentionally create durable localnet state: DAR upload installs
246
+ a package; create/exercise, query, and stream may allocate a fallback party and
247
+ create contracts; topology inspection may allocate a fallback party. User-rights
248
+ is read-only. These examples do not clean up durable state.
249
+
250
+ The uploaded asset is
251
+ `canton-explorer-debug-playground-0.1.0.dar`, the normal (not debug) Canton
252
+ Explorer Debug Playground DAR from
253
+ `/home/helena/dev/daml/canton-explorer/debug-playground/.daml/dist/canton-explorer-debug-playground-0.1.0.dar`.
254
+ Its Canton Explorer checkout commit is `750b28dd0ce4674e4368c12a6da1b5b5cbb00f88`,
255
+ its package-introduction commit is `abde077`, it is Apache-2.0 licensed, and
256
+ its SHA-256 is
257
+ `307cf7c52ac2770d1d1a2c5e1ec56a78ab7c70e7809c0cfb419abadb93cc6e29`.
258
+
259
+ The examples and their DAR are repository-only and excluded from the npm
260
+ tarball. The existing setup and decentralized-party examples were developed
261
+ and live-tested against Participant 3.5.7, then live-tested unchanged against
262
+ the isolated [Participant 3.5.8 sidecar](#optional-canton-358-participant-sidecar).
263
+ The workflow examples have completed 3.5.7 and 3.5.8 workflow matrices with
264
+ authenticated status evidence, the same unchanged implementation, and the
265
+ common compatibility path.
266
+
95
267
  ## Localnet launchers
96
268
 
97
269
  The published package includes launchers for an existing CN Quickstart checkout.
@@ -133,13 +305,21 @@ Compose project, its Postgres container, and host ports `8901` (Ledger), `8902`
133
305
  registered synchronizer via its Admin API, stores the exported connection
134
306
  configuration in the sidecar runtime directory, and connects the sidecar with
135
307
  its own Admin API. It generates a five-minute 3.5.8-compatible development JWT
136
- at `.generated/participant-358/ledger-api-user.token` and prints both live-test
137
- and example endpoint/token exports when the connection is healthy.
308
+ at `.generated/participant-358/ledger-api-user.token`. When the connection is
309
+ healthy, use the protected child-shell credential flow below to make the
310
+ live-test and example endpoint/token variables available without logging them.
311
+ Canton 3.5.8 enforces this short lifetime for the unsafe development JWT.
138
312
  `cn-quickstart` is strictly read-only: this launcher never writes to, starts,
139
313
  or stops the normal Quickstart stack.
140
314
 
141
- After copying the printed `SDK_EXAMPLE_*` exports into the current shell, run
142
- the standalone decentralized-party comparison example with:
315
+ Refresh short-lived credentials before expiry without restarting or otherwise
316
+ touching the running containers. Use the launcher's protected documented
317
+ credential refresh flow only in a local child shell; do not log its output or
318
+ expose refreshed credentials beyond that child. The runtime credential file
319
+ remains protected with mode `0600`.
320
+
321
+ After the protected credential flow completes, run the example inside that same
322
+ short-lived credential-scoped child shell:
143
323
 
144
324
  ```bash
145
325
  npm run example:party:decentralized
@@ -151,11 +331,14 @@ localnet differs:
151
331
 
152
332
  ```bash
153
333
  PARTICIPANT_358_SOURCE_ADMIN_ENDPOINT=localhost:3902 \
154
- PARTICIPANT_358_SOURCE_ADMIN_BEARER_TOKEN="$TOKEN" \
155
334
  PARTICIPANT_358_NETWORK=quickstart \
156
335
  canton-localnet-participant-358-start
157
336
  ```
158
337
 
338
+ Provide any required source-admin credential only through the protected
339
+ child-scoped credential mechanism described above; do not place credentials in
340
+ command lines or logs.
341
+
159
342
  Use `PARTICIPANT_358_CANTON_IMAGE`, `PARTICIPANT_358_PROJECT_NAME`,
160
343
  `PARTICIPANT_358_RUNTIME_DIR`, `PARTICIPANT_358_LEDGER_PORT`,
161
344
  `PARTICIPANT_358_ADMIN_PORT`, and `PARTICIPANT_358_JSON_PORT` to make an
@@ -179,11 +362,12 @@ a self-signed certificate in `.generated/localnet-es256` at the package root. Se
179
362
  use your own matching PEM private key and certificate.
180
363
 
181
364
  The launcher writes a short-lived (ten-minute) token for `ledger-api-user` to
182
- `ledger-api-user.token` in that runtime directory and prints its path. Use it
183
- with the live SDK suite:
365
+ `ledger-api-user.token` in that runtime directory and prints its path. In a
366
+ short-lived child shell, use a protected local credential mechanism to make the
367
+ token available without placing its value or a token-file read in a command
368
+ line or log. From within that same child shell, run the live SDK suite:
184
369
 
185
370
  ```bash
186
- SDK_TEST_LEDGER_BEARER_TOKEN="$(cat .generated/localnet-es256/ledger-api-user.token)" \
187
371
  npm run test:live
188
372
  ```
189
373
 
@@ -356,11 +540,11 @@ import {
356
540
  BearerTokenAuthProvider,
357
541
  CantonClient,
358
542
  CantonClientOptions,
359
- GetActiveContractsPageRequest,
360
543
  HealthCheckRequest,
361
544
  GetLedgerApiVersionRequest,
362
545
  TransportKind,
363
546
  } from "@distrohelena/canton-typescript-sdk";
547
+ import { ledgerApiV2 } from "@distrohelena/canton-typescript-sdk/protobuf";
364
548
 
365
549
  const client = new CantonClient(
366
550
  new CantonClientOptions({
@@ -393,14 +577,22 @@ const party = await client.partyManagementService.allocatePartyAsync(
393
577
  }),
394
578
  );
395
579
  const contracts = await client.stateService.getActiveContractsPageAsync(
396
- new GetActiveContractsPageRequest({
397
- party: "Alice",
398
- templateId: "Main:Iou",
580
+ ledgerApiV2.GetActiveContractsPageRequest.create({
581
+ eventFormat: {
582
+ filtersByParty: {
583
+ Alice: { cumulative: [] },
584
+ },
585
+ verbose: true,
586
+ },
399
587
  }),
400
588
  );
401
589
  ```
402
590
 
403
- `stateService.getActiveContractsPageAsync(...)` keeps `templateId` as the simple helper path, and on gRPC also supports interface-based ACS reads with `interfaceId`, `includeInterfaceView`, `includeCreatedEventBlob`, `activeAtOffset`, `maxPageSize`, and `pageToken`. JSON remains template-query only.
591
+ `stateService.getActiveContractsPageAsync(...)` is gRPC-only and accepts the generated Ledger API request, including its `eventFormat`, `activeAtOffset`, `maxPageSize`, and `pageToken` fields. Build party, template, and interface filters in `eventFormat`.
592
+
593
+ `stateService.getActiveContractsPagesAsync(...)` is the gRPC-only lazy, raw, bounded traversal API. It starts from the same generated request and yields raw `GetActiveContractsPageResponse` values one page at a time. The caller selects the shared `OperationDeadline`, maximum pages, and maximum contracts with `ActiveContractsTraversalOptions`; there is no collect-all wrapper. Transport errors from dispatched RPCs propagate unchanged. Traversal safety, invariant, and bound failures use `ActiveContractsTraversalError` codes, such as an inconsistent offset, repeated page token, or exceeded bound.
594
+
595
+ JSON does not implement either paginated gRPC API. Its existing `stateService.getActiveContractsAsync(...)` behavior remains the distinct JSON streaming read.
404
596
 
405
597
  For interface views, do not use `contractService.getContractAsync(...)`. That contract lookup surface cannot return interface views; use `stateService` or `updateService` instead.
406
598
 
@@ -547,12 +739,13 @@ unsupported query features reject with `QueryCapabilityError`.
547
739
  - `packageService.listVettedPackagesAsync(...)`: `grpc` only
548
740
  - `commandService.submitAndWaitAsync(...)`: `json`, `grpc`
549
741
  - `commandSubmissionService.submitAsync(...)`: reserved, currently unsupported
550
- - `stateService.getActiveContractsPageAsync(...)`: `json`, `grpc`
551
- - `stateService.getActiveContractsAsync(...)`: `json` only
742
+ - `stateService.getActiveContractsPageAsync(...)`: `grpc` only
743
+ - `stateService.getActiveContractsPagesAsync(...)`: `grpc` only, lazy raw bounded traversal
744
+ - `stateService.getActiveContractsAsync(...)`: `json` only, existing distinct streaming read
552
745
  - `updateService.getUpdatesAsync(...)`: `grpc` only
553
- - `commandCompletionService`: placeholder, no methods yet
554
- - `eventQueryService`: placeholder, no methods yet
555
- - `contractService`: placeholder, no methods yet
746
+ - `commandCompletionService.getCompletionsAsync(...)`: `grpc` only, existing streaming API
747
+ - `eventQueryService.getEventsByContractIdAsync(...)`: `grpc` only; JSON rejects this request
748
+ - `contractService.getContractAsync(...)`: `grpc` only; JSON rejects this request
556
749
 
557
750
  - Ledger Admin endpoint:
558
751
  - `partyManagementService.allocatePartyAsync(...)`: `json`, `grpc`
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExternalPartyActivationClient = void 0;
4
+ const list_party_to_participant_request_js_1 = require("../core/types/requests/list-party-to-participant-request.js");
5
+ const topology_base_query_js_1 = require("../core/types/topology/topology-base-query.js");
4
6
  const topology_store_id_js_1 = require("../core/types/topology/topology-store-id.js");
5
7
  const topology_js_1 = require("../transports/grpc/generated/canton/com/digitalasset/canton/protocol/v30/topology.js");
6
8
  const external_party_activation_response_js_1 = require("./external-party-activation-response.js");
@@ -66,27 +68,35 @@ class ExternalPartyActivationClient {
66
68
  }
67
69
  async readStateAsync(request, options) {
68
70
  const [activeResponse, proposalResponse] = await Promise.all([
69
- this.sourceClient.topologyManagerReadService.listPartyToParticipantAsync({
70
- baseQuery: createSynchronizerQuery(request.synchronizerId, false),
71
+ this.sourceClient.topologyManagerReadService.listPartyToParticipantAsync(new list_party_to_participant_request_js_1.ListPartyToParticipantRequest({
72
+ baseQuery: new topology_base_query_js_1.TopologyBaseQuery({
73
+ storeId: createSynchronizerStoreId(request.synchronizerId),
74
+ headState: true,
75
+ includeProposals: false,
76
+ }),
71
77
  filterParty: request.partyId,
72
78
  filterParticipant: "",
73
- }, options),
74
- this.sourceClient.topologyManagerReadService.listPartyToParticipantAsync({
75
- baseQuery: createSynchronizerQuery(request.synchronizerId, true),
79
+ }), options),
80
+ this.sourceClient.topologyManagerReadService.listPartyToParticipantAsync(new list_party_to_participant_request_js_1.ListPartyToParticipantRequest({
81
+ baseQuery: new topology_base_query_js_1.TopologyBaseQuery({
82
+ storeId: createSynchronizerStoreId(request.synchronizerId),
83
+ headState: true,
84
+ includeProposals: true,
85
+ }),
76
86
  filterParty: request.partyId,
77
87
  filterParticipant: "",
78
- }, options),
88
+ }), options),
79
89
  ]);
80
- const active = activeResponse.results.find((item) => item.item?.party === request.partyId);
81
- const proposal = proposalResponse.results.find((item) => item.item?.party === request.partyId);
90
+ const active = activeResponse.results.find((item) => item.item.party === request.partyId);
91
+ const proposal = proposalResponse.results.find((item) => item.item.party === request.partyId);
82
92
  return {
83
- active: active === undefined || active.context === undefined || active.item === undefined
93
+ active: active === undefined || active.context === undefined
84
94
  ? undefined
85
95
  : {
86
96
  context: active.context,
87
97
  mapping: active.item,
88
98
  },
89
- proposal: proposal === undefined || proposal.context === undefined || proposal.item === undefined
99
+ proposal: proposal === undefined || proposal.context === undefined
90
100
  ? undefined
91
101
  : {
92
102
  context: proposal.context,
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ActiveContractsTraversalError = void 0;
4
+ const canton_error_js_1 = require("./canton-error.js");
5
+ class ActiveContractsTraversalError extends canton_error_js_1.CantonError {
6
+ code;
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.code = code;
10
+ }
11
+ }
12
+ exports.ActiveContractsTraversalError = ActiveContractsTraversalError;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pollUntilAsync = pollUntilAsync;
4
+ async function pollUntilAsync(init) {
5
+ const now = init.now ?? Date.now;
6
+ const sleepAsync = init.sleepAsync ?? defaultSleepAsync;
7
+ const deadline = now() + init.timeoutMs;
8
+ let lastObserved;
9
+ let firstRead = true;
10
+ while (firstRead || now() < deadline) {
11
+ firstRead = false;
12
+ lastObserved = await init.readAsync();
13
+ if (init.match(lastObserved)) {
14
+ return lastObserved;
15
+ }
16
+ const remainingMs = deadline - now();
17
+ if (remainingMs <= 0) {
18
+ throw init.createTimeoutError(lastObserved);
19
+ }
20
+ await sleepAsync(Math.min(init.pollIntervalMs, remainingMs));
21
+ }
22
+ throw init.createTimeoutError(lastObserved);
23
+ }
24
+ async function defaultSleepAsync(milliseconds) {
25
+ await new Promise(resolve => setTimeout(resolve, milliseconds));
26
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ActiveContractsTraversalOptions = void 0;
4
+ const validation_error_js_1 = require("../errors/validation-error.js");
5
+ const operation_deadline_js_1 = require("./operation-deadline.js");
6
+ class ActiveContractsTraversalOptions {
7
+ deadline;
8
+ maxPages;
9
+ maxContracts;
10
+ constructor(init) {
11
+ if (!(init.deadline instanceof operation_deadline_js_1.OperationDeadline)) {
12
+ throw new validation_error_js_1.ValidationError("active contracts traversal deadline must be an OperationDeadline");
13
+ }
14
+ else if (!Number.isSafeInteger(init.maxPages) || init.maxPages <= 0) {
15
+ throw new validation_error_js_1.ValidationError("active contracts traversal maxPages must be a positive safe integer");
16
+ }
17
+ else if (!Number.isSafeInteger(init.maxContracts) || init.maxContracts <= 0) {
18
+ throw new validation_error_js_1.ValidationError("active contracts traversal maxContracts must be a positive safe integer");
19
+ }
20
+ this.deadline = init.deadline;
21
+ this.maxPages = init.maxPages;
22
+ this.maxContracts = init.maxContracts;
23
+ Object.freeze(this);
24
+ }
25
+ }
26
+ exports.ActiveContractsTraversalOptions = ActiveContractsTraversalOptions;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OperationDeadline = void 0;
4
+ const timeout_error_js_1 = require("../errors/timeout-error.js");
5
+ const validation_error_js_1 = require("../errors/validation-error.js");
6
+ const request_options_js_1 = require("./request-options.js");
7
+ class OperationDeadline {
8
+ endsAtMs;
9
+ now;
10
+ remainingMs;
11
+ constructor(init) {
12
+ if (!Number.isSafeInteger(init.timeoutMs) || init.timeoutMs <= 0) {
13
+ throw new validation_error_js_1.ValidationError("operation deadline timeout must be a positive safe integer");
14
+ }
15
+ this.now = init.now ?? Date.now;
16
+ const startedAtMs = this.safeNow();
17
+ this.endsAtMs = startedAtMs + init.timeoutMs;
18
+ if (!Number.isSafeInteger(this.endsAtMs)) {
19
+ throw new validation_error_js_1.ValidationError("operation deadline end time must be a safe integer");
20
+ }
21
+ this.remainingMs = init.timeoutMs;
22
+ }
23
+ remainingTimeoutMs() {
24
+ const computedRemainingMs = Math.max(0, this.endsAtMs - this.safeNow());
25
+ this.remainingMs = Math.min(this.remainingMs, computedRemainingMs);
26
+ if (this.remainingMs === 0) {
27
+ throw new timeout_error_js_1.TimeoutError("operation deadline has expired");
28
+ }
29
+ return this.remainingMs;
30
+ }
31
+ createRequestOptions() {
32
+ return new request_options_js_1.RequestOptions({ timeoutMs: this.remainingTimeoutMs() });
33
+ }
34
+ safeNow() {
35
+ const now = this.now();
36
+ if (!Number.isSafeInteger(now)) {
37
+ throw new validation_error_js_1.ValidationError("operation deadline clock must return a safe integer");
38
+ }
39
+ return now;
40
+ }
41
+ }
42
+ exports.OperationDeadline = OperationDeadline;
@@ -4,9 +4,11 @@ exports.AllocatePartyRequest = void 0;
4
4
  class AllocatePartyRequest {
5
5
  partyIdHint;
6
6
  displayName;
7
+ userId;
7
8
  constructor(init = {}) {
8
9
  this.partyIdHint = init.partyIdHint;
9
10
  this.displayName = init.displayName;
11
+ this.userId = init.userId;
10
12
  }
11
13
  }
12
14
  exports.AllocatePartyRequest = AllocatePartyRequest;
@@ -8,6 +8,9 @@ class PreparedDecentralizedParty {
8
8
  decentralizedNamespace;
9
9
  ownerThreshold;
10
10
  partySigningThreshold;
11
+ identityProviderId;
12
+ waitForAllocation;
13
+ userId;
11
14
  transactions;
12
15
  signingRequests;
13
16
  constructor(init) {
@@ -16,6 +19,9 @@ class PreparedDecentralizedParty {
16
19
  this.decentralizedNamespace = init.decentralizedNamespace;
17
20
  this.ownerThreshold = init.ownerThreshold;
18
21
  this.partySigningThreshold = init.partySigningThreshold;
22
+ this.identityProviderId = init.identityProviderId;
23
+ this.waitForAllocation = init.waitForAllocation;
24
+ this.userId = init.userId;
19
25
  this.transactions = [...(init.transactions ?? [])].map((transaction) => new prepared_topology_transaction_js_1.PreparedTopologyTransaction({
20
26
  serializedTransaction: transaction.serializedTransaction,
21
27
  transactionHash: transaction.transactionHash,
@@ -2,25 +2,68 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SubmitCommandRequest = void 0;
4
4
  const validation_error_js_1 = require("../../errors/validation-error.js");
5
+ const ledgerStringPattern = /^[A-Za-z0-9#:\-_/ ]+$/;
6
+ const canonicalOffsetPattern = /^(0|[1-9][0-9]*)$/;
7
+ const maximumInt64Offset = "9223372036854775807";
5
8
  class SubmitCommandRequest {
6
9
  applicationId;
7
10
  userId;
8
11
  actAs;
9
12
  readAs;
10
13
  command;
14
+ commandId;
15
+ deduplicationPeriod;
11
16
  disclosedContracts;
12
17
  synchronizerId;
13
18
  constructor(init) {
14
19
  if (init.actAs.length === 0) {
15
20
  throw new validation_error_js_1.ValidationError("submit requests require at least one actAs party");
16
21
  }
22
+ else if (init.commandId !== undefined &&
23
+ (typeof init.commandId !== "string" ||
24
+ init.commandId.length > 255 ||
25
+ !ledgerStringPattern.test(init.commandId))) {
26
+ throw new validation_error_js_1.ValidationError("submit request command ID must be a LedgerString");
27
+ }
28
+ const deduplicationPeriod = freezeDeduplicationPeriod(init.deduplicationPeriod);
17
29
  this.applicationId = init.applicationId;
18
30
  this.userId = init.userId;
19
31
  this.actAs = init.actAs;
20
32
  this.readAs = init.readAs ?? [];
21
33
  this.command = init.command;
34
+ this.commandId = init.commandId;
35
+ this.deduplicationPeriod = deduplicationPeriod;
22
36
  this.disclosedContracts = init.disclosedContracts ?? [];
23
37
  this.synchronizerId = init.synchronizerId;
24
38
  }
25
39
  }
26
40
  exports.SubmitCommandRequest = SubmitCommandRequest;
41
+ function freezeDeduplicationPeriod(value) {
42
+ if (value === undefined) {
43
+ return undefined;
44
+ }
45
+ else if (typeof value !== "object" ||
46
+ value === null ||
47
+ Array.isArray(value)) {
48
+ throw new validation_error_js_1.ValidationError("submit request deduplication period is invalid");
49
+ }
50
+ else if (value.kind === "duration") {
51
+ if (typeof value.seconds !== "number" ||
52
+ !Number.isSafeInteger(value.seconds) ||
53
+ value.seconds <= 0) {
54
+ throw new validation_error_js_1.ValidationError("submit request duration deduplication seconds must be a positive safe integer");
55
+ }
56
+ return Object.freeze({ kind: "duration", seconds: value.seconds });
57
+ }
58
+ else if (value.kind !== "offset") {
59
+ throw new validation_error_js_1.ValidationError("submit request deduplication period is invalid");
60
+ }
61
+ if (typeof value.offset !== "string" ||
62
+ !canonicalOffsetPattern.test(value.offset) ||
63
+ value.offset.length > maximumInt64Offset.length ||
64
+ (value.offset.length === maximumInt64Offset.length &&
65
+ value.offset > maximumInt64Offset)) {
66
+ throw new validation_error_js_1.ValidationError("submit request offset deduplication period must be a canonical unsigned int64");
67
+ }
68
+ return Object.freeze({ kind: "offset", offset: value.offset });
69
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WaitForPartyHostingRequest = void 0;
4
+ const validation_error_js_1 = require("../../errors/validation-error.js");
5
+ class WaitForPartyHostingRequest {
6
+ partyId;
7
+ participantId;
8
+ synchronizerId;
9
+ pollIntervalMs;
10
+ timeoutMs;
11
+ constructor(init) {
12
+ const partyId = init.partyId.trim();
13
+ const participantId = init.participantId.trim();
14
+ const synchronizerId = init.synchronizerId.trim();
15
+ const pollIntervalMs = init.pollIntervalMs ?? 500;
16
+ const timeoutMs = init.timeoutMs ?? 30_000;
17
+ if (!partyId) {
18
+ throw new validation_error_js_1.ValidationError("party hosting wait requires a party ID");
19
+ }
20
+ else if (!participantId) {
21
+ throw new validation_error_js_1.ValidationError("party hosting wait requires a participant ID");
22
+ }
23
+ else if (!synchronizerId) {
24
+ throw new validation_error_js_1.ValidationError("party hosting wait requires a synchronizer ID");
25
+ }
26
+ else if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
27
+ throw new validation_error_js_1.ValidationError("party hosting wait timeout must be a positive safe integer");
28
+ }
29
+ else if (!Number.isSafeInteger(pollIntervalMs) ||
30
+ pollIntervalMs < 0) {
31
+ throw new validation_error_js_1.ValidationError("party hosting wait poll interval must be a non-negative safe integer");
32
+ }
33
+ this.partyId = partyId;
34
+ this.participantId = participantId;
35
+ this.synchronizerId = synchronizerId;
36
+ this.pollIntervalMs = pollIntervalMs;
37
+ this.timeoutMs = timeoutMs;
38
+ }
39
+ }
40
+ exports.WaitForPartyHostingRequest = WaitForPartyHostingRequest;