diff --git a/.cursor/skills/localnet-testing/SKILL.md b/.cursor/skills/localnet-testing/SKILL.md index a829c36d..a1c378b5 100644 --- a/.cursor/skills/localnet-testing/SKILL.md +++ b/.cursor/skills/localnet-testing/SKILL.md @@ -3,7 +3,7 @@ Read the public [LocalNet guide](https://github.com/Fairmint/canton-node-sdk/wiki/LocalNet-testing) first. -**ENG-1635:** `@fairmint/canton-dev-tools@0.1.7+` owns LocalNet lifecycle, pins, and shared test +**ENG-1635:** `@fairmint/canton-dev-tools@0.1.11+` owns LocalNet lifecycle, pins, and shared test helpers. This repository does not ship LocalNet scripts or a `canton-localnet` binary. - Commands: `npm run localnet:*` (wired to `canton-dev-tools`) or diff --git a/AGENTS.md b/AGENTS.md index d254bda9..944c096e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,14 +3,14 @@ See [CLAUDE.md](CLAUDE.md), [README.md](README.md), and `.cursor/skills/localnet-testing/SKILL.md`. `package.json` (`localnet:*` scripts) is the source of truth for LocalNet commands in this repo; lifecycle is owned by -`@fairmint/canton-dev-tools@0.1.7+`. +`@fairmint/canton-dev-tools@0.1.11+`. ## LocalNet ownership (ENG-1635) **`@fairmint/canton-dev-tools` owns LocalNet** (CLI, pins, and shared test helpers). This SDK does not ship a LocalNet engine or `canton-localnet` binary. -- Install pin: `devDependency` `@fairmint/canton-dev-tools@0.1.7` (exact). +- Install pin: `devDependency` `@fairmint/canton-dev-tools@0.1.11` (exact). - Repo scripts: `npm run localnet:*` → `canton-dev-tools `. - Integration helpers: import from `@fairmint/canton-dev-tools/testing`. - Pins / auth defaults: see Dev Tools diff --git a/README.md b/README.md index 86376dcc..7ec48212 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ npm run build ### LocalNet (owned by `@fairmint/canton-dev-tools`) LocalNet lifecycle and shared test helpers live in -[`@fairmint/canton-dev-tools@0.1.7`](https://www.npmjs.com/package/@fairmint/canton-dev-tools) +[`@fairmint/canton-dev-tools@0.1.11`](https://www.npmjs.com/package/@fairmint/canton-dev-tools) (see its [COMPATIBILITY.md](https://github.com/Fairmint/canton-dev-tools/blob/main/COMPATIBILITY.md)). This SDK does not publish a LocalNet CLI or `scripts/localnet-cloud.sh`. diff --git a/docs/package-boundary.md b/docs/package-boundary.md index d7678bc5..76346c79 100644 --- a/docs/package-boundary.md +++ b/docs/package-boundary.md @@ -13,7 +13,7 @@ repository / CI-only. Repeatable enforcement lives in `npm run check:package-art LocalNet CLI and shared integration-test helpers are **not** published here. They live in [`@fairmint/canton-dev-tools`](https://www.npmjs.com/package/@fairmint/canton-dev-tools) -(`0.1.7+`), including `scripts/localnet-cloud.sh` and `@fairmint/canton-dev-tools/testing`. +(`0.1.11+`), including `scripts/localnet-cloud.sh` and `@fairmint/canton-dev-tools/testing`. ## CI-only / must not publish diff --git a/eslint.config.mjs b/eslint.config.mjs index 71b8413b..c70e0782 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -201,6 +201,15 @@ const eslintConfig = [ '@typescript-eslint/no-redundant-type-constituents': 'off', }, }, + // Test-only override: jest mocks are invoked as bare functions and never rely on + // `this`, so unbound-method reports false positives there. Disabled for test files + // only; non-test code keeps full protection, with no per-assertion suppressions. + { + files: ['test/**/*'], + rules: { + '@typescript-eslint/unbound-method': 'off', + }, + }, // Prettier config last to override any conflicting rules eslintConfigPrettier, ]; diff --git a/package.json b/package.json index 5369bf53..82c1bfc2 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@hardlydifficult/websocket": "1.0.73", "@stellar/stellar-base": "15.0.0", "axios": "1.20.0", - "dotenv": "17.4.2", + "dotenv": "18.0.3", "glob": "13.0.6", "openapi-fetch": "0.17.0", "openapi-typescript": "7.13.0", @@ -75,27 +75,27 @@ "zod": "4.6.5" }, "devDependencies": { - "@fairmint/canton-dev-tools": "0.1.7", + "@fairmint/canton-dev-tools": "0.1.11", "@types/jest": "30.0.0", - "@types/node": "26.1.2", + "@types/node": "26.6.2", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.62.1", - "@typescript-eslint/parser": "8.62.1", + "@typescript-eslint/eslint-plugin": "8.70.1", + "@typescript-eslint/parser": "8.70.1", "eslint": "9.39.2", "eslint-config-prettier": "10.1.8", "eslint-import-resolver-typescript": "4.4.5", "eslint-plugin-import": "2.32.0", "eslint-plugin-markdown": "5.1.0", "eslint-plugin-unused-imports": "4.4.1", - "jest": "30.4.2", + "jest": "30.5.2", "markdownlint-cli": "0.49.1", - "npm-package-json-lint": "10.4.1", - "prettier": "3.9.6", + "npm-package-json-lint": "11.0.0", + "prettier": "3.9.8", "prettier-plugin-jsdoc": "1.8.1", "prettier-plugin-organize-imports": "4.3.0", "prettier-plugin-packagejson": "3.0.2", "ts-jest": "29.4.12", - "tsx": "4.23.7", + "tsx": "4.23.15", "typescript": "5.9.3", "typescript-7": "npm:typescript@7.0.2" }, diff --git a/src/utils/external-signing/external-party-onboarding.ts b/src/utils/external-signing/external-party-onboarding.ts index 825be1e6..5fe29b58 100644 --- a/src/utils/external-signing/external-party-onboarding.ts +++ b/src/utils/external-signing/external-party-onboarding.ts @@ -424,6 +424,7 @@ async function readExistingExternalPartyAfterAllocationConflict( const abortError = new ValidationError('Canton external-party conflict reconciliation was aborted', { partyId }); const createAbortError = (): ValidationError => abortError; try { + // eslint-disable-next-line @typescript-eslint/promise-function-async -- non-async arrow preserves rejection timing expected by race tests const partyDetailsResponse = await runWithAbortSignal(signal, createAbortError, () => signal === undefined ? ledgerClient.getPartyDetails({ diff --git a/test/typecheck/ledger-contract-by-id.typecheck.ts b/test/typecheck/ledger-contract-by-id.typecheck.ts index d1e3e87f..a47d1584 100644 --- a/test/typecheck/ledger-contract-by-id.typecheck.ts +++ b/test/typecheck/ledger-contract-by-id.typecheck.ts @@ -102,20 +102,20 @@ createdEvent.offset; void ledgerClient.getContractById(request, exactBodyRetry); void ledgerClient.getContractById(requestWithoutParties, derivedBodyRetry); -void requestWithUnknownField; -void requestWithUndefinedOptional; -void requestWithPlainString; -void requestWithPartyAsContract; -void requestWithContractAsParty; -void createArgument; -void optionalContractKey; -void responseContractId; -void responseWitness; -void responseTemplateId; -void responsePackageId; -void responsePackageName; -void responseInterfaceId; -void responseImplementationPackageId; -void unvalidatedTemplateId; -void nullContractKey; -void invalidCreateArgument; +requestWithUnknownField; +requestWithUndefinedOptional; +requestWithPlainString; +requestWithPartyAsContract; +requestWithContractAsParty; +createArgument; +optionalContractKey; +responseContractId; +responseWitness; +responseTemplateId; +responsePackageId; +responsePackageName; +responseInterfaceId; +responseImplementationPackageId; +unvalidatedTemplateId; +nullContractKey; +invalidCreateArgument; diff --git a/test/typecheck/ledger-dars.typecheck.ts b/test/typecheck/ledger-dars.typecheck.ts index 9abf7630..fdea0239 100644 --- a/test/typecheck/ledger-dars.typecheck.ts +++ b/test/typecheck/ledger-dars.typecheck.ts @@ -37,4 +37,4 @@ void client.validateDar({ darFile: Buffer.from('dar'), synchronizerId: 'sync::on // @ts-expect-error Successful DAR upload responses cannot expose arbitrary properties. const invalidResponse: UploadDarResponse = { packageId: 'unexpected' }; -void invalidResponse; +invalidResponse; diff --git a/test/typecheck/ledger-interactive-submission.typecheck.ts b/test/typecheck/ledger-interactive-submission.typecheck.ts index fdcbbaf5..d4f52201 100644 --- a/test/typecheck/ledger-interactive-submission.typecheck.ts +++ b/test/typecheck/ledger-interactive-submission.typecheck.ts @@ -142,14 +142,14 @@ const prepareResponse: PrepareResponse = { hashingSchemeVersion: 'HASHING_SCHEME_VERSION_V3', }; -void executeAndWaitForTransactionRequest; -void invalidHelperHashingScheme; -void unspecifiedTransactionShape; -void executeAndWaitResponse; -void prepareRequest; -void unspecifiedPrepareHashingScheme; -void unspecifiedCostHintSigningAlgorithm; -void prepareResponse; +executeAndWaitForTransactionRequest; +invalidHelperHashingScheme; +unspecifiedTransactionShape; +executeAndWaitResponse; +prepareRequest; +unspecifiedPrepareHashingScheme; +unspecifiedCostHintSigningAlgorithm; +prepareResponse; const invalidOffsetRequest: ExecuteAndWaitRequest = { ...executeAndWaitRequest, @@ -256,11 +256,11 @@ const invalidExerciseChoiceArgument: ExercisedTransactionEvent['choiceArgument'] // @ts-expect-error Exercise results are JSON values when present. const invalidExerciseResult: ExercisedTransactionEvent['exerciseResult'] = Symbol('invalid'); -void normalizedTraceContext; -void normalizedTraceState; -void nullTraceState; -void rawExternalHashString; -void unvalidatedExternalHash; +normalizedTraceContext; +normalizedTraceState; +nullTraceState; +rawExternalHashString; +unvalidatedExternalHash; type PrepareCommand = PrepareRequest['commands'][number]; type CreateArguments = Extract['CreateCommand']['createArguments']; @@ -329,43 +329,43 @@ const invalidMultiBranchEvent: InteractiveSubmissionEvent = { CreatedEvent: createdEvent, }; -void invalidOffsetRequest; -void invalidHashingSchemeRequest; -void unspecifiedHashingSchemeRequest; -void decodedProtoAny; -void invalidDecodedProtoAny; -void invalidSignatureFormat; -void unspecifiedSignatureFormat; -void invalidSigningAlgorithm; -void unspecifiedSigningAlgorithm; -void emptyTransactionEvents; -void nullContractKey; -void nullInterfaceViewValue; -void createdEventArgument; -void exerciseChoiceArgument; -void exerciseResult; -void invalidCreatedEventArgument; -void invalidExerciseChoiceArgument; -void invalidExerciseResult; -void createArguments; -void exerciseArguments; -void createAndExerciseCreateArguments; -void createAndExerciseChoiceArguments; -void exerciseByKeyContractKey; -void exerciseByKeyChoiceArgument; -void prefetchContractKey; -void invalidCreateArguments; -void invalidExerciseArguments; -void invalidCreateAndExerciseCreateArguments; -void invalidCreateAndExerciseChoiceArguments; -void invalidExerciseByKeyContractKey; -void invalidExerciseByKeyChoiceArgument; -void invalidPrefetchContractKey; -void invalidMultiBranchCommand; -void invalidMultiBranchDeduplication; -void invalidMultiBranchTime; -void invalidMultiBranchIdentifierFilter; -void invalidMultiBranchEvent; +invalidOffsetRequest; +invalidHashingSchemeRequest; +unspecifiedHashingSchemeRequest; +decodedProtoAny; +invalidDecodedProtoAny; +invalidSignatureFormat; +unspecifiedSignatureFormat; +invalidSigningAlgorithm; +unspecifiedSigningAlgorithm; +emptyTransactionEvents; +nullContractKey; +nullInterfaceViewValue; +createdEventArgument; +exerciseChoiceArgument; +exerciseResult; +invalidCreatedEventArgument; +invalidExerciseChoiceArgument; +invalidExerciseResult; +createArguments; +exerciseArguments; +createAndExerciseCreateArguments; +createAndExerciseChoiceArguments; +exerciseByKeyContractKey; +exerciseByKeyChoiceArgument; +prefetchContractKey; +invalidCreateArguments; +invalidExerciseArguments; +invalidCreateAndExerciseCreateArguments; +invalidCreateAndExerciseChoiceArguments; +invalidExerciseByKeyContractKey; +invalidExerciseByKeyChoiceArgument; +invalidPrefetchContractKey; +invalidMultiBranchCommand; +invalidMultiBranchDeduplication; +invalidMultiBranchTime; +invalidMultiBranchIdentifierFilter; +invalidMultiBranchEvent; const invalidPackagePreference: PrepareRequest = { ...prepareRequest, @@ -381,8 +381,8 @@ const incompletePrepareResponse: PrepareResponse = { hashingSchemeVersion: 'HASHING_SCHEME_VERSION_V3', }; -void invalidPackagePreference; -void incompletePrepareResponse; +invalidPackagePreference; +incompletePrepareResponse; type Assert = Condition; type IsRequired = {} extends Pick ? false : true; @@ -510,18 +510,18 @@ const absentPreferredPackage: PreferredPackageVersionResponse = {}; // @ts-expect-error Public responses normalize wire null into an absent optional property. const wireNullPreferredPackage: PreferredPackageVersionResponse = { packagePreference: null }; -void multipleRawPrepareCommands; -void multiplePrepareCommands; +multipleRawPrepareCommands; +multiplePrepareCommands; void ledgerClient.interactiveSubmissionGetPreferredPackageVersion( preferredPackageVersionRequest, preferredPackageVersionOptions ); void ledgerClient.interactiveSubmissionGetPreferredPackages(preferredPackagesRequest, preferredPackagesOptions); -void preferredPackagesRequest; -void preferredPackageVersionRequest; -void preferredPackagesResponse; -void absentPreferredPackage; -void wireNullPreferredPackage; +preferredPackagesRequest; +preferredPackageVersionRequest; +preferredPackagesResponse; +absentPreferredPackage; +wireNullPreferredPackage; // @ts-expect-error Every derived traffic-cost estimate includes its server estimation timestamp. const trafficEstimateWithoutTimestamp: TrafficCostEstimate = { @@ -533,15 +533,15 @@ const trafficEstimateWithoutTimestamp: TrafficCostEstimate = { costInDollars: 0.01, }; -void emptyPartySignatures; -void partyWithoutSignatures; -void emptyRawPartySignatureGroups; -void emptyRawSignatures; -void emptyRawPrepareCommands; -void emptyRawActAs; -void emptyPrepareCommands; -void emptyActAsParties; -void trafficEstimateWithoutTimestamp; -void invalidExecuteExactBodyOptions; -void invalidExecuteAndWaitExactBodyOptions; -void invalidExecuteAndWaitForTransactionExactBodyOptions; +emptyPartySignatures; +partyWithoutSignatures; +emptyRawPartySignatureGroups; +emptyRawSignatures; +emptyRawPrepareCommands; +emptyRawActAs; +emptyPrepareCommands; +emptyActAsParties; +trafficEstimateWithoutTimestamp; +invalidExecuteExactBodyOptions; +invalidExecuteAndWaitExactBodyOptions; +invalidExecuteAndWaitForTransactionExactBodyOptions; diff --git a/test/typecheck/operation-retry.typecheck.ts b/test/typecheck/operation-retry.typecheck.ts index 222a1124..e619efe9 100644 --- a/test/typecheck/operation-retry.typecheck.ts +++ b/test/typecheck/operation-retry.typecheck.ts @@ -39,8 +39,8 @@ const invalidTuplePayload: DeepReadonly = { // @ts-expect-error DeepReadonly must preserve non-empty tuple constraints. values: [], }; -void validTuplePayload; -void invalidTuplePayload; +validTuplePayload; +invalidTuplePayload; void parameterizedRequestCallback('value', 5); void client.allocateExternalParty(signedRequest, { @@ -52,7 +52,7 @@ void client.allocateExternalParty(signedRequest, { const [transaction] = params.onboardingTransactions; if (transaction && 'signatures' in transaction) { const signature: string = transaction.signatures[0]?.signature ?? ''; - void signature; + signature; } return signedRequest; }, @@ -78,7 +78,7 @@ void client.allocateExternalParty(requestWithCallerOnlyMetadata, { maxAttempts: 1, beforeAttempt: ({ params }) => { // @ts-expect-error Retry hooks expose validated declared params, not caller-only structural extensions. - void params.callerOnlyMetadata; + params.callerOnlyMetadata; }, }, }); @@ -130,7 +130,7 @@ const invalidGetOperationConfig = { requestSemantics: 'mutation', // @ts-expect-error Factory-created GET operations cannot use mutation semantics. } satisfies ApiOperationConfig; -void invalidGetOperationConfig; +invalidGetOperationConfig; const invalidDeleteOperationConfig = { paramsSchema: z.void(), @@ -139,7 +139,7 @@ const invalidDeleteOperationConfig = { requestSemantics: 'read', // @ts-expect-error Factory-created DELETE operations cannot use read semantics. } satisfies ApiOperationConfig; -void invalidDeleteOperationConfig; +invalidDeleteOperationConfig; const invalidPatchOperationConfig = { paramsSchema: z.void(), @@ -148,7 +148,7 @@ const invalidPatchOperationConfig = { requestSemantics: 'read', // @ts-expect-error Factory-created PATCH operations cannot use read semantics. } satisfies ApiOperationConfig; -void invalidPatchOperationConfig; +invalidPatchOperationConfig; // Custom ApiOperation subclasses retain their declared params while forwarding the same typed options. void client.getParties({}, { signal: new AbortController().signal }); diff --git a/test/typecheck/scan-registry-metadata.typecheck.ts b/test/typecheck/scan-registry-metadata.typecheck.ts index c2146681..332610d0 100644 --- a/test/typecheck/scan-registry-metadata.typecheck.ts +++ b/test/typecheck/scan-registry-metadata.typecheck.ts @@ -60,4 +60,4 @@ function assertClientSurface(client: ScanApiClient): void { void client.listInstruments({ pageSize: 25, unknown: true }); } -void assertClientSurface; +assertClientSurface; diff --git a/test/typecheck/scan-snapshot-after.typecheck.ts b/test/typecheck/scan-snapshot-after.typecheck.ts index 40896c06..83523c90 100644 --- a/test/typecheck/scan-snapshot-after.typecheck.ts +++ b/test/typecheck/scan-snapshot-after.typecheck.ts @@ -29,9 +29,9 @@ const missingRecordTime: ClientResponse = {}; // @ts-expect-error The generated response contract does not include additional envelope fields. const extraResponseField: ClientResponse = { record_time: '2026-07-10T12:00:01Z', unexpected: true }; -void clientParams; -void clientResponse; -void missingMigrationId; -void wireQueryName; -void missingRecordTime; -void extraResponseField; +clientParams; +clientResponse; +missingMigrationId; +wireQueryName; +missingRecordTime; +extraResponseField; diff --git a/test/typecheck/validator-global-domain-connection-config.typecheck.ts b/test/typecheck/validator-global-domain-connection-config.typecheck.ts index 78a7de25..eaf40072 100644 --- a/test/typecheck/validator-global-domain-connection-config.typecheck.ts +++ b/test/typecheck/validator-global-domain-connection-config.typecheck.ts @@ -49,6 +49,6 @@ const wrongCase: GetDecentralizedSynchronizerConnectionConfigResponse = { sequencerConnections: response.sequencer_connections, }; -void response; -void missingPatience; -void wrongCase; +response; +missingPatience; +wrongCase; diff --git a/test/typecheck/validator-wallet-list-transactions.typecheck.ts b/test/typecheck/validator-wallet-list-transactions.typecheck.ts index e8264a33..7f6f7286 100644 --- a/test/typecheck/validator-wallet-list-transactions.typecheck.ts +++ b/test/typecheck/validator-wallet-list-transactions.typecheck.ts @@ -68,11 +68,11 @@ const unsupportedBranch: ValidatorWalletTransaction = { date: '2026-07-10T02:00:00Z', }; -void firstPage; -void followingPage; -void response; -void publicEventIds; -void narrowTransaction; -void missingPageSize; -void numericCursor; -void unsupportedBranch; +firstPage; +followingPage; +response; +publicEventIds; +narrowTransaction; +missingPageSize; +numericCursor; +unsupportedBranch; diff --git a/test/typecheck/validator-wallet-tap.typecheck.ts b/test/typecheck/validator-wallet-tap.typecheck.ts index 64ef6cac..a29f609c 100644 --- a/test/typecheck/validator-wallet-tap.typecheck.ts +++ b/test/typecheck/validator-wallet-tap.typecheck.ts @@ -19,9 +19,9 @@ const missingAmount: TapRequest = { command_id: 'tap-command-123' }; // @ts-expect-error The generated tap response requires its created contract id. const missingContractId: TapResponse = {}; -void requestWithCommandId; -void requestWithoutCommandId; -void response; -void numericAmount; -void missingAmount; -void missingContractId; +requestWithCommandId; +requestWithoutCommandId; +response; +numericAmount; +missingAmount; +missingContractId; diff --git a/test/unit/amulet/offers.test.ts b/test/unit/amulet/offers.test.ts index e91fb161..208ec51f 100644 --- a/test/unit/amulet/offers.test.ts +++ b/test/unit/amulet/offers.test.ts @@ -171,7 +171,6 @@ describe('createTransferOffer', () => { }); const mockEnvLoaderInstance = EnvLoader.getInstance(); - // eslint-disable-next-line @typescript-eslint/unbound-method expect(mockEnvLoaderInstance.getValidatorWalletAppInstallContractId).toHaveBeenCalledWith('localnet'); const callArgs = mockClient.submitAndWaitForTransactionTree.mock.calls[0]?.[0]; diff --git a/test/unit/clients/scan-api.test.ts b/test/unit/clients/scan-api.test.ts index f6390019..e90ed337 100644 --- a/test/unit/clients/scan-api.test.ts +++ b/test/unit/clients/scan-api.test.ts @@ -60,7 +60,6 @@ function createClient( mockAxiosInstance: MockAxiosInstance; } { const client = new ScanApiClient(new CantonRuntime(config), options); - // eslint-disable-next-line @typescript-eslint/unbound-method -- axios.create is a mocked function in this test module const mockResults = jest.mocked(axios.create).mock.results; const latestResultIndex = mockResults.length - 1; const latestResult = latestResultIndex >= 0 ? mockResults[latestResultIndex] : undefined; diff --git a/test/unit/clients/validator-api-health.test.ts b/test/unit/clients/validator-api-health.test.ts index 4d5d03be..09ba1b24 100644 --- a/test/unit/clients/validator-api-health.test.ts +++ b/test/unit/clients/validator-api-health.test.ts @@ -39,7 +39,6 @@ function createClient(): { client: ValidatorApiClient; mockAxiosInstance: MockAx }; const client = new ValidatorApiClient(new CantonRuntime(config)); - // eslint-disable-next-line @typescript-eslint/unbound-method -- axios.create is a mocked function in this test module const mockResults = jest.mocked(axios.create).mock.results; const latestResult = mockResults[mockResults.length - 1]; if (!latestResult) { diff --git a/test/unit/external-signing/external-party-lifecycle.test.ts b/test/unit/external-signing/external-party-lifecycle.test.ts index 979337e0..49325fea 100644 --- a/test/unit/external-signing/external-party-lifecycle.test.ts +++ b/test/unit/external-signing/external-party-lifecycle.test.ts @@ -193,6 +193,7 @@ describe('external-party lifecycle reconciliation', (): void => { markPartyDetailsStarted = resolve; }); ledgerClient.getPartyDetails.mockImplementationOnce( + // eslint-disable-next-line @typescript-eslint/promise-function-async -- non-async arrow preserves rejection timing under abort () => new Promise((_resolve, reject) => { rejectPartyDetails = reject; diff --git a/test/unit/external-signing/external-party-onboarding.test.ts b/test/unit/external-signing/external-party-onboarding.test.ts index b24a2f8c..40d92066 100644 --- a/test/unit/external-signing/external-party-onboarding.test.ts +++ b/test/unit/external-signing/external-party-onboarding.test.ts @@ -12,7 +12,7 @@ import { CANTON_ED25519_SIGNATURE_ALGORITHM, CANTON_RAW_SIGNATURE_FORMAT, createExternalPartyWithSigner, - ExternalPartyConflictReconciliationError, + type ExternalPartyConflictReconciliationError, getExternalPartyIdForHintAndPublicKey, listExternalPartyIdsForPublicKey, prepareExternalPartyOnboarding, @@ -381,6 +381,7 @@ describe('external-party onboarding helpers', () => { }); ledgerClient.allocateExternalParty.mockRejectedValueOnce(allocationError); ledgerClient.getPartyDetails.mockImplementationOnce( + // eslint-disable-next-line @typescript-eslint/promise-function-async -- non-async arrow preserves rejection timing under abort () => new Promise((_resolve, reject) => { rejectConfirmation = reject;