@distrohelena/canton-typescript-sdk 0.1.7 → 0.1.9
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/dist/core/transports/transport.interface.d.ts +4 -0
- package/dist/core/types/prepared-command-submission.d.ts +9 -0
- package/dist/core/types/prepared-command-submission.js +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/query/grpc/grpc-contract-query-client.js +29 -3
- package/dist/query/model-types.d.ts +7 -2
- package/dist/query/pqs/pqs-sql-compiler.js +20 -8
- package/dist/services/command/command-service-client.d.ts +4 -0
- package/dist/services/command/command-service-client.js +2 -0
- package/dist/services/commands/command-submission-pipeline.d.ts +4 -0
- package/dist/services/commands/command-submission-pipeline.js +4 -0
- package/dist/transports/grpc/grpc-transport.d.ts +4 -0
- package/dist/transports/grpc/grpc-transport.js +17 -0
- package/package.json +1 -1
|
@@ -186,6 +186,8 @@ import { CommitmentChunkObserver } from "../../services/participant-inspection/c
|
|
|
186
186
|
import { ContractObserver } from "../../services/contracts/contract-observer.interface.js";
|
|
187
187
|
import { TransactionObserver } from "../../services/events/transaction-observer.interface.js";
|
|
188
188
|
import { CommandSigners, ICommandSigner } from "../signing/command-signer.interface.js";
|
|
189
|
+
import { SignCommandResult } from "../signing/sign-command-result.js";
|
|
190
|
+
import { PreparedCommandSubmission } from "../types/prepared-command-submission.js";
|
|
189
191
|
import { RequestOptions } from "../types/request-options.js";
|
|
190
192
|
import { SubmitCommandRequest } from "../types/requests/submit-command-request.js";
|
|
191
193
|
export interface ITransport {
|
|
@@ -381,4 +383,6 @@ export interface ITransport {
|
|
|
381
383
|
* Supported on JSON and gRPC. External signing is gRPC-only.
|
|
382
384
|
*/
|
|
383
385
|
submitCommandAsync(request: SubmitCommandRequest, signer?: ICommandSigner | CommandSigners, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
386
|
+
prepareCommandAsync?(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
|
|
387
|
+
executePreparedCommandAndWaitAsync?(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
384
388
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SubmitCommandRequest } from "./requests/submit-command-request.js";
|
|
2
|
+
/** Opaque result of interactive command preparation, suitable for detached signing. */
|
|
3
|
+
export declare class PreparedCommandSubmission {
|
|
4
|
+
readonly request: SubmitCommandRequest;
|
|
5
|
+
readonly transaction: unknown;
|
|
6
|
+
readonly transactionHash: Uint8Array;
|
|
7
|
+
readonly hashingSchemeVersion: number;
|
|
8
|
+
constructor(request: SubmitCommandRequest, transaction: unknown, transactionHash: Uint8Array, hashingSchemeVersion: number);
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Opaque result of interactive command preparation, suitable for detached signing. */
|
|
2
|
+
export class PreparedCommandSubmission {
|
|
3
|
+
request;
|
|
4
|
+
transaction;
|
|
5
|
+
transactionHash;
|
|
6
|
+
hashingSchemeVersion;
|
|
7
|
+
constructor(request, transaction, transactionHash, hashingSchemeVersion) {
|
|
8
|
+
this.request = request;
|
|
9
|
+
this.transaction = transaction;
|
|
10
|
+
this.transactionHash = transactionHash;
|
|
11
|
+
this.hashingSchemeVersion = hashingSchemeVersion;
|
|
12
|
+
}
|
|
13
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -207,6 +207,7 @@ export { CreateExternalPartyRequest, } from "./core/types/requests/create-extern
|
|
|
207
207
|
export type { ExternalPartySigner, ExternalPartySigningRequest, ExternalPartySigningResult, ExternalPartySigningPayloadKind, } from "./core/types/requests/create-external-party-request.js";
|
|
208
208
|
export { AllocatePartyRequest } from "./core/types/requests/allocate-party-request.js";
|
|
209
209
|
export { DisclosedContract } from "./core/types/disclosed-contract.js";
|
|
210
|
+
export { PreparedCommandSubmission } from "./core/types/prepared-command-submission.js";
|
|
210
211
|
export { AddPartyAsyncArguments } from "./core/types/requests/add-party-async-request.js";
|
|
211
212
|
export { AddPartyAsyncRequest } from "./core/types/requests/add-party-async-request.js";
|
|
212
213
|
export { AssembleSignedTopologyTransactionsRequest } from "./core/types/requests/assemble-signed-topology-transactions-request.js";
|
package/dist/index.js
CHANGED
|
@@ -194,6 +194,7 @@ export { AllocateExternalPartyRequest } from "./core/types/requests/allocate-ext
|
|
|
194
194
|
export { CreateExternalPartyRequest, } from "./core/types/requests/create-external-party-request.js";
|
|
195
195
|
export { AllocatePartyRequest } from "./core/types/requests/allocate-party-request.js";
|
|
196
196
|
export { DisclosedContract } from "./core/types/disclosed-contract.js";
|
|
197
|
+
export { PreparedCommandSubmission } from "./core/types/prepared-command-submission.js";
|
|
197
198
|
export { AddPartyAsyncArguments } from "./core/types/requests/add-party-async-request.js";
|
|
198
199
|
export { AddPartyAsyncRequest } from "./core/types/requests/add-party-async-request.js";
|
|
199
200
|
export { AssembleSignedTopologyTransactionsRequest } from "./core/types/requests/assemble-signed-topology-transactions-request.js";
|
|
@@ -44,7 +44,7 @@ export class GrpcContractQueryClient {
|
|
|
44
44
|
args.where?.contractId?.in !== undefined ||
|
|
45
45
|
args.where?.contractId?.is !== undefined ||
|
|
46
46
|
args.where?.contractId?.isNot !== undefined ||
|
|
47
|
-
args.where?.templateId !== undefined) {
|
|
47
|
+
(args.where?.templateId !== undefined && typeof args.where.templateId.equals !== "string")) {
|
|
48
48
|
throw new QueryCapabilityError(QuerySource.grpc, "contracts.findMany");
|
|
49
49
|
}
|
|
50
50
|
const findArgs = args;
|
|
@@ -55,6 +55,11 @@ export class GrpcContractQueryClient {
|
|
|
55
55
|
let rows = snapshot.filter((row) => args.where?.contractId?.equals === undefined
|
|
56
56
|
? true
|
|
57
57
|
: row.contractId === args.where.contractId.equals);
|
|
58
|
+
if (args.where?.payload !== undefined)
|
|
59
|
+
rows = rows.filter((row) => matchesPayload(row.payload, args.where.payload));
|
|
60
|
+
const legacyTemplate = args.where?.templateId;
|
|
61
|
+
if (legacyTemplate?.equals !== undefined)
|
|
62
|
+
rows = rows.filter((row) => `${row.templateId.packageId}:${row.templateId.moduleName}:${row.templateId.entityName}` === legacyTemplate.equals);
|
|
58
63
|
return rows;
|
|
59
64
|
}
|
|
60
65
|
unsupported(operation) {
|
|
@@ -99,7 +104,7 @@ export class GrpcContractQueryClient {
|
|
|
99
104
|
function hasUnsupportedFilter(where) {
|
|
100
105
|
if (where === undefined)
|
|
101
106
|
return false;
|
|
102
|
-
if ("and" in where || "or" in where || "not" in where || "
|
|
107
|
+
if ("and" in where || "or" in where || "not" in where || "createdEventOffset" in where || "createdAt" in where || "archivedEventOffset" in where || "archivedAt" in where)
|
|
103
108
|
return true;
|
|
104
109
|
for (const field of ["contractId", "templateId"]) {
|
|
105
110
|
const filter = where[field];
|
|
@@ -115,7 +120,7 @@ function mapGrpcContract(value) {
|
|
|
115
120
|
contractId: row.contractId ?? "",
|
|
116
121
|
templateId: { packageId: template?.packageId ?? "", moduleName: template?.moduleName ?? "", entityName: template?.entityName ?? "" },
|
|
117
122
|
packageId: null,
|
|
118
|
-
payload:
|
|
123
|
+
payload: row.payload,
|
|
119
124
|
witnesses: [],
|
|
120
125
|
createdEventOffset: "",
|
|
121
126
|
createdAt: null,
|
|
@@ -124,3 +129,24 @@ function mapGrpcContract(value) {
|
|
|
124
129
|
active: true,
|
|
125
130
|
};
|
|
126
131
|
}
|
|
132
|
+
function matchesPayload(value, filter) {
|
|
133
|
+
const match = filter.match;
|
|
134
|
+
if (match === undefined)
|
|
135
|
+
return false;
|
|
136
|
+
const visit = (current, node) => Object.entries(node).every(([key, child]) => {
|
|
137
|
+
const next = current !== null && typeof current === "object" ? current[key] : undefined;
|
|
138
|
+
const predicate = child;
|
|
139
|
+
if (Object.keys(predicate).some((name) => ["equals", "lt", "lte", "gt", "gte", "like", "ilike"].includes(name)))
|
|
140
|
+
return compare(String(next ?? ""), predicate);
|
|
141
|
+
return visit(next, predicate);
|
|
142
|
+
});
|
|
143
|
+
return visit(value, match);
|
|
144
|
+
}
|
|
145
|
+
function compare(value, filter) { if (filter.equals !== undefined)
|
|
146
|
+
return value === filter.equals; if (filter.like !== undefined || filter.ilike !== undefined) {
|
|
147
|
+
const pattern = String(filter.like ?? filter.ilike).replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replaceAll("%", ".*").replaceAll("_", ".");
|
|
148
|
+
return new RegExp(`^${pattern}$`, filter.ilike === undefined ? "" : "i").test(value);
|
|
149
|
+
} if (filter.lt !== undefined)
|
|
150
|
+
return value < String(filter.lt); if (filter.lte !== undefined)
|
|
151
|
+
return value <= String(filter.lte); if (filter.gt !== undefined)
|
|
152
|
+
return value > String(filter.gt); return value >= String(filter.gte); }
|
|
@@ -106,9 +106,14 @@ type PayloadValueFilter = {
|
|
|
106
106
|
readonly gte?: never;
|
|
107
107
|
readonly like?: never;
|
|
108
108
|
};
|
|
109
|
-
export type
|
|
109
|
+
export type PayloadMatch = {
|
|
110
|
+
readonly [field: string]: PayloadMatch | PayloadValueFilter;
|
|
111
|
+
};
|
|
112
|
+
export type ContractPayloadFilter = ({
|
|
110
113
|
readonly path: string;
|
|
111
|
-
} & PayloadValueFilter
|
|
114
|
+
} & PayloadValueFilter) | {
|
|
115
|
+
readonly match: PayloadMatch;
|
|
116
|
+
};
|
|
112
117
|
type ContractWhereFields = {
|
|
113
118
|
readonly contractId?: StringFilter;
|
|
114
119
|
readonly templateId?: Partial<{
|
|
@@ -72,18 +72,30 @@ function compileWhere(where, addValue) {
|
|
|
72
72
|
}
|
|
73
73
|
if (key === "payload") {
|
|
74
74
|
const payload = value;
|
|
75
|
-
const path =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
const compilePayload = (path, filter) => { const ops = ["equals", "lt", "lte", "gt", "gte", "like", "ilike"].filter((op) => filter[op] !== undefined); if (ops.length === 1) {
|
|
76
|
+
const op = { equals: "=", lt: "<", lte: "<=", gt: ">", gte: ">=", like: "like", ilike: "ilike" }[ops[0]];
|
|
77
|
+
parts.push(`contract_row.payload #>> ${addValue(path)}::text[] ${op} ${addValue(filter[ops[0]])}`);
|
|
78
|
+
return;
|
|
79
|
+
} for (const [name, child] of Object.entries(filter))
|
|
80
|
+
compilePayload([...path, name], child); };
|
|
81
|
+
if (payload.match !== undefined) {
|
|
82
|
+
compilePayload([], payload.match);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const path = payload.path;
|
|
86
|
+
if (typeof path !== "string" || path.split(".").some((x) => x.length === 0))
|
|
87
|
+
throw new Error("payload path must contain non-empty segments");
|
|
88
|
+
compilePayload(path.split("."), payload);
|
|
89
|
+
}
|
|
83
90
|
continue;
|
|
84
91
|
}
|
|
85
92
|
if (key === "templateId") {
|
|
86
93
|
const fields = { packageId: "contract_row.creation_package_id", moduleName: "contract_tpe_row.module_name", entityName: "contract_tpe_row.entity_name" };
|
|
94
|
+
const legacy = value;
|
|
95
|
+
if (typeof legacy.equals === "string") {
|
|
96
|
+
parts.push(`(contract_row.creation_package_id || ':' || contract_tpe_row.module_name || ':' || contract_tpe_row.entity_name) = ${addValue(legacy.equals)}`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
87
99
|
for (const [name, filter] of Object.entries(value))
|
|
88
100
|
for (const [op, operand] of Object.entries(filter)) {
|
|
89
101
|
const sql = { equals: "=", lt: "<", lte: "<=", gt: ">", gte: ">=", like: "like", ilike: "ilike" }[op];
|
|
@@ -3,9 +3,13 @@ import { RequestOptions } from "../../core/types/request-options.js";
|
|
|
3
3
|
import { ITransport } from "../../core/transports/transport.interface.js";
|
|
4
4
|
import { SubmitCommandRequest } from "../../core/types/requests/submit-command-request.js";
|
|
5
5
|
import { SubmitCommandResponse } from "../../core/types/responses/submit-command-response.js";
|
|
6
|
+
import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
|
|
7
|
+
import { SignCommandResult } from "../../core/signing/sign-command-result.js";
|
|
6
8
|
export declare class CommandServiceClient {
|
|
7
9
|
private readonly pipeline;
|
|
8
10
|
constructor(transport: ITransport, signer?: ICommandSigner | CommandSigners);
|
|
9
11
|
/** Submits a command and waits for the result. Supported on JSON and gRPC. */
|
|
10
12
|
submitAndWaitAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
13
|
+
prepareAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
|
|
14
|
+
executeAndWaitAsync(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
11
15
|
}
|
|
@@ -11,4 +11,6 @@ export class CommandServiceClient {
|
|
|
11
11
|
submitAndWaitAsync(request, options) {
|
|
12
12
|
return this.pipeline.submitAsync(request, options);
|
|
13
13
|
}
|
|
14
|
+
prepareAsync(request, options) { return this.pipeline.prepareAsync(request, options); }
|
|
15
|
+
executeAndWaitAsync(prepared, signatures, options) { return this.pipeline.executeAsync(prepared, signatures, options); }
|
|
14
16
|
}
|
|
@@ -3,6 +3,8 @@ import { ITransport } from "../../core/transports/transport.interface.js";
|
|
|
3
3
|
import { RequestOptions } from "../../core/types/request-options.js";
|
|
4
4
|
import { SubmitCommandRequest } from "../../core/types/requests/submit-command-request.js";
|
|
5
5
|
import { SubmitCommandResponse } from "../../core/types/responses/submit-command-response.js";
|
|
6
|
+
import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
|
|
7
|
+
import { SignCommandResult } from "../../core/signing/sign-command-result.js";
|
|
6
8
|
export declare class CommandSubmissionPipeline {
|
|
7
9
|
private readonly dependencies;
|
|
8
10
|
constructor(dependencies: {
|
|
@@ -10,4 +12,6 @@ export declare class CommandSubmissionPipeline {
|
|
|
10
12
|
signer?: ICommandSigner | CommandSigners;
|
|
11
13
|
});
|
|
12
14
|
submitAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
15
|
+
prepareAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
|
|
16
|
+
executeAsync(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
13
17
|
}
|
|
@@ -12,4 +12,8 @@ export class CommandSubmissionPipeline {
|
|
|
12
12
|
}
|
|
13
13
|
return this.dependencies.transport.submitCommandAsync(request, this.dependencies.signer, options);
|
|
14
14
|
}
|
|
15
|
+
prepareAsync(request, options) { if (!this.dependencies.transport.prepareCommandAsync)
|
|
16
|
+
throw new NotSupportedError("interactive command preparation is not supported by the selected transport"); return this.dependencies.transport.prepareCommandAsync(request, options); }
|
|
17
|
+
executeAsync(prepared, signatures, options) { if (!this.dependencies.transport.executePreparedCommandAndWaitAsync)
|
|
18
|
+
throw new NotSupportedError("interactive command execution is not supported by the selected transport"); return this.dependencies.transport.executePreparedCommandAndWaitAsync(prepared, signatures, options); }
|
|
15
19
|
}
|
|
@@ -85,6 +85,8 @@ import { TopologyListVettedPackagesRequest } from "../../core/types/requests/top
|
|
|
85
85
|
import { TrafficControlStateRequest } from "../../core/types/requests/traffic-control-state-request.js";
|
|
86
86
|
import { UploadDarFileRequest } from "../../core/types/requests/upload-dar-file-request.js";
|
|
87
87
|
import { CommandSigners, ICommandSigner } from "../../core/signing/command-signer.interface.js";
|
|
88
|
+
import { SignCommandResult } from "../../core/signing/sign-command-result.js";
|
|
89
|
+
import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
|
|
88
90
|
import { AllocatePartyResponse as SdkAllocatePartyResponse } from "../../core/types/responses/allocate-party-response.js";
|
|
89
91
|
import { AllocateExternalPartyResponse } from "../../core/types/responses/allocate-external-party-response.js";
|
|
90
92
|
import { AddPartyAsyncResponse } from "../../core/types/responses/add-party-async-response.js";
|
|
@@ -276,6 +278,8 @@ export declare class GrpcTransport implements ITransport {
|
|
|
276
278
|
getUpdatesPageAsync(request: GetUpdatesPageRequest, options?: RequestOptions): Promise<GetUpdatesPageResponse>;
|
|
277
279
|
getCompletionsAsync(request: GetCompletionsRequest, observer: CompletionObserver, options?: RequestOptions): Promise<void>;
|
|
278
280
|
submitCommandAsync(request: SubmitCommandRequest, signer?: ICommandSigner | CommandSigners, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
281
|
+
prepareCommandAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
|
|
282
|
+
executePreparedCommandAndWaitAsync(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
|
|
279
283
|
private throwIfDisposed;
|
|
280
284
|
private throwPartyTopologyReadCompatibilityError;
|
|
281
285
|
private isProtobufDeserializationFailure;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { ValidationError } from "../../core/errors/validation-error.js";
|
|
3
3
|
import { SignCommandRequest } from "../../core/signing/sign-command-request.js";
|
|
4
|
+
import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
|
|
4
5
|
import { AllocatePartyResponse as SdkAllocatePartyResponse } from "../../core/types/responses/allocate-party-response.js";
|
|
5
6
|
import { GetActiveContractsPageResponse } from "../../core/types/responses/get-active-contracts-page-response.js";
|
|
6
7
|
import { GetLedgerApiVersionResponse as SdkGetLedgerApiVersionResponse } from "../../core/types/responses/get-ledger-api-version-response.js";
|
|
@@ -616,6 +617,22 @@ export class GrpcTransport {
|
|
|
616
617
|
}), options);
|
|
617
618
|
return mapGrpcInteractiveSubmitCommand(executed);
|
|
618
619
|
}
|
|
620
|
+
async prepareCommandAsync(request, options) {
|
|
621
|
+
if (!this.operations.prepareSubmissionAsync)
|
|
622
|
+
throw new NotSupportedError("interactive gRPC command signing is not available on this transport");
|
|
623
|
+
const prepared = await this.operations.prepareSubmissionAsync(mapGrpcPrepareSubmissionRequest(request, randomUUID()), options);
|
|
624
|
+
if (!prepared.preparedTransaction || prepared.preparedTransactionHash.length === 0)
|
|
625
|
+
throw new ValidationError("interactive prepare submission returned an incomplete result");
|
|
626
|
+
return new PreparedCommandSubmission(request, prepared.preparedTransaction, prepared.preparedTransactionHash, prepared.hashingSchemeVersion);
|
|
627
|
+
}
|
|
628
|
+
async executePreparedCommandAndWaitAsync(prepared, signatures, options) {
|
|
629
|
+
if (!this.operations.executeSubmissionAndWaitAsync)
|
|
630
|
+
throw new NotSupportedError("interactive gRPC command signing is not available on this transport");
|
|
631
|
+
const signerResults = prepared.request.actAs.map((party) => { const result = signatures[party]; if (!result)
|
|
632
|
+
throw new ValidationError(`interactive command signing requires a signature for ${party}`); return { party, result }; });
|
|
633
|
+
const result = await this.operations.executeSubmissionAndWaitAsync(mapGrpcExecuteSubmissionAndWaitRequest({ request: prepared.request, preparedTransaction: prepared.transaction, hashingSchemeVersion: prepared.hashingSchemeVersion, submissionId: randomUUID(), signerResults }), options);
|
|
634
|
+
return mapGrpcInteractiveSubmitCommand(result);
|
|
635
|
+
}
|
|
619
636
|
throwIfDisposed() {
|
|
620
637
|
if (this.disposed) {
|
|
621
638
|
throw new ObjectDisposedError("The client or transport has been disposed.");
|