@distrohelena/canton-typescript-sdk 0.1.36 → 0.1.37

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 (99) hide show
  1. package/README.md +104 -34
  2. package/dist/cjs/core/types/requests/{submit-command-request.js → submit-commands-request.js} +10 -6
  3. package/dist/cjs/index.js +10 -8
  4. package/dist/cjs/query/canonical/in-memory-query-evaluator.js +291 -0
  5. package/dist/cjs/query/canonical/public-identity.js +41 -0
  6. package/dist/cjs/query/canonical/query-ast.js +2 -0
  7. package/dist/cjs/query/canonical/query-dataset.js +351 -0
  8. package/dist/cjs/query/canonical/query-normalizer.js +655 -0
  9. package/dist/cjs/query/canonical/query-schema.js +86 -0
  10. package/dist/cjs/query/canton-manager.js +14 -5
  11. package/dist/cjs/query/errors/query-snapshot-incomplete-error.js +19 -0
  12. package/dist/cjs/query/grpc/grpc-contract-cache.js +484 -0
  13. package/dist/cjs/query/grpc/grpc-package-relation-reader.js +218 -0
  14. package/dist/cjs/query/grpc/grpc-query-client.js +249 -0
  15. package/dist/cjs/query/grpc/grpc-query-snapshot-reader.js +238 -0
  16. package/dist/cjs/query/grpc/grpc-query-value-mapper.js +204 -0
  17. package/dist/cjs/query/grpc/grpc-relation-mapper.js +794 -0
  18. package/dist/cjs/query/pqs/pqs-query-client.js +151 -582
  19. package/dist/cjs/query/pqs/pqs-relational-sql-compiler.js +138 -95
  20. package/dist/cjs/query/pqs/pqs-result-shape.js +28 -0
  21. package/dist/cjs/query/pqs/pqs-schema-profile.js +49 -48
  22. package/dist/cjs/query/pqs/pqs-sql-compiler.js +343 -289
  23. package/dist/cjs/query/pqs/pqs-sql-syntax.js +10 -0
  24. package/dist/cjs/services/command/command-service-client.js +16 -3
  25. package/dist/cjs/services/commands/command-payload-builder.js +1 -1
  26. package/dist/cjs/services/commands/command-submission-pipeline.js +21 -6
  27. package/dist/cjs/testing/runtime/declarative-action-executor.js +3 -3
  28. package/dist/cjs/transports/grpc/grpc-transport.js +2 -2
  29. package/dist/cjs/transports/grpc/mappers/commands-mapper.js +6 -6
  30. package/dist/cjs/transports/grpc/mappers/interactive-command-mapper.js +1 -1
  31. package/dist/cjs/transports/json/json-transport.js +1 -1
  32. package/dist/cjs/transports/json/mappers/commands-mapper.js +3 -3
  33. package/dist/core/transports/transport.interface.d.ts +4 -4
  34. package/dist/core/types/prepared-command-submission.d.ts +3 -3
  35. package/dist/core/types/requests/{submit-command-request.d.ts → submit-commands-request.d.ts} +8 -4
  36. package/dist/core/types/requests/{submit-command-request.js → submit-commands-request.js} +8 -4
  37. package/dist/index.d.ts +5 -2
  38. package/dist/index.js +2 -1
  39. package/dist/query/canonical/in-memory-query-evaluator.d.ts +22 -0
  40. package/dist/query/canonical/in-memory-query-evaluator.js +287 -0
  41. package/dist/query/canonical/public-identity.d.ts +14 -0
  42. package/dist/query/canonical/public-identity.js +35 -0
  43. package/dist/query/canonical/query-ast.d.ts +104 -0
  44. package/dist/query/canonical/query-ast.js +1 -0
  45. package/dist/query/canonical/query-dataset.d.ts +63 -0
  46. package/dist/query/canonical/query-dataset.js +343 -0
  47. package/dist/query/canonical/query-normalizer.d.ts +10 -0
  48. package/dist/query/canonical/query-normalizer.js +646 -0
  49. package/dist/query/canonical/query-schema.d.ts +25 -0
  50. package/dist/query/canonical/query-schema.js +83 -0
  51. package/dist/query/canton-manager.js +14 -5
  52. package/dist/query/errors/query-snapshot-incomplete-error.d.ts +14 -0
  53. package/dist/query/errors/query-snapshot-incomplete-error.js +15 -0
  54. package/dist/query/grpc/grpc-contract-cache.d.ts +27 -0
  55. package/dist/query/grpc/grpc-contract-cache.js +479 -0
  56. package/dist/query/grpc/grpc-package-relation-reader.d.ts +43 -0
  57. package/dist/query/grpc/grpc-package-relation-reader.js +213 -0
  58. package/dist/query/grpc/grpc-query-client.d.ts +42 -0
  59. package/dist/query/grpc/grpc-query-client.js +245 -0
  60. package/dist/query/grpc/grpc-query-snapshot-reader.d.ts +33 -0
  61. package/dist/query/grpc/grpc-query-snapshot-reader.js +233 -0
  62. package/dist/query/grpc/grpc-query-value-mapper.d.ts +15 -0
  63. package/dist/query/grpc/grpc-query-value-mapper.js +195 -0
  64. package/dist/query/grpc/grpc-relation-mapper.d.ts +67 -0
  65. package/dist/query/grpc/grpc-relation-mapper.js +789 -0
  66. package/dist/query/pqs/pqs-query-client.d.ts +10 -20
  67. package/dist/query/pqs/pqs-query-client.js +154 -585
  68. package/dist/query/pqs/pqs-relational-sql-compiler.d.ts +9 -17
  69. package/dist/query/pqs/pqs-relational-sql-compiler.js +136 -95
  70. package/dist/query/pqs/pqs-result-shape.d.ts +25 -0
  71. package/dist/query/pqs/pqs-result-shape.js +25 -0
  72. package/dist/query/pqs/pqs-schema-profile.js +49 -48
  73. package/dist/query/pqs/pqs-sql-compiler.d.ts +18 -3
  74. package/dist/query/pqs/pqs-sql-compiler.js +337 -288
  75. package/dist/query/pqs/pqs-sql-syntax.d.ts +2 -0
  76. package/dist/query/pqs/pqs-sql-syntax.js +6 -0
  77. package/dist/query/query-client.d.ts +15 -0
  78. package/dist/services/command/command-service-client.d.ts +9 -4
  79. package/dist/services/command/command-service-client.js +16 -3
  80. package/dist/services/command-submission/command-submission-service-client.d.ts +2 -2
  81. package/dist/services/commands/command-payload-builder.d.ts +2 -2
  82. package/dist/services/commands/command-payload-builder.js +1 -1
  83. package/dist/services/commands/command-submission-pipeline.d.ts +5 -4
  84. package/dist/services/commands/command-submission-pipeline.js +21 -6
  85. package/dist/testing/runtime/declarative-action-executor.js +3 -3
  86. package/dist/transports/grpc/grpc-transport.d.ts +4 -4
  87. package/dist/transports/grpc/grpc-transport.js +3 -3
  88. package/dist/transports/grpc/mappers/commands-mapper.d.ts +3 -3
  89. package/dist/transports/grpc/mappers/commands-mapper.js +4 -4
  90. package/dist/transports/grpc/mappers/interactive-command-mapper.d.ts +3 -3
  91. package/dist/transports/grpc/mappers/interactive-command-mapper.js +1 -1
  92. package/dist/transports/json/json-transport.d.ts +2 -2
  93. package/dist/transports/json/json-transport.js +2 -2
  94. package/dist/transports/json/mappers/commands-mapper.d.ts +2 -2
  95. package/dist/transports/json/mappers/commands-mapper.js +2 -2
  96. package/package.json +4 -3
  97. package/dist/cjs/query/grpc/grpc-contract-query-client.js +0 -178
  98. package/dist/query/grpc/grpc-contract-query-client.d.ts +0 -33
  99. package/dist/query/grpc/grpc-contract-query-client.js +0 -174
@@ -0,0 +1,43 @@
1
+ import { CantonError } from "../../core/errors/canton-error.js";
2
+ import { DamlLfPackageLoader } from "../../daml-lf/daml-lf-package-loader.js";
3
+ import { type GetPackageResponse, type ListPackagesResponse } from "../../transports/grpc/generated/canton/com/daml/ledger/api/v2/package_service.js";
4
+ export interface GrpcPackageService {
5
+ listPackagesAsync(request: {}): Promise<ListPackagesResponse>;
6
+ getPackageAsync(request: {
7
+ packageId: string;
8
+ }): Promise<GetPackageResponse>;
9
+ }
10
+ export interface GrpcPackageChoiceMetadata {
11
+ readonly choice: string;
12
+ readonly consuming: boolean;
13
+ readonly aliases: readonly string[];
14
+ readonly choiceFqn: string;
15
+ }
16
+ export interface GrpcPackageTemplateMetadata {
17
+ readonly moduleName: string;
18
+ readonly entityName: string;
19
+ readonly payloadType: "template" | "interface";
20
+ readonly aliases: readonly string[];
21
+ readonly templateFqn: string;
22
+ readonly choices: readonly GrpcPackageChoiceMetadata[];
23
+ }
24
+ export interface GrpcPackageMetadata {
25
+ readonly id: string;
26
+ readonly name: string;
27
+ readonly version: string;
28
+ readonly templates: readonly GrpcPackageTemplateMetadata[];
29
+ }
30
+ /** An explicit package-service/LF decoding failure; callers never receive partial package rows. */
31
+ export declare class GrpcPackageRelationError extends CantonError {
32
+ readonly packageId: string;
33
+ constructor(packageId: string, message: string);
34
+ }
35
+ /** Loads LF package metadata. Each call owns its promise map, so no package data enters QueryCacheStore. */
36
+ export declare class GrpcPackageRelationReader {
37
+ private readonly packageService;
38
+ private readonly packageLoader;
39
+ constructor(packageService: GrpcPackageService, packageLoader?: DamlLfPackageLoader);
40
+ readAllAsync(): Promise<readonly GrpcPackageMetadata[]>;
41
+ readPackagesAsync(packageIds: readonly string[]): Promise<readonly GrpcPackageMetadata[]>;
42
+ private readPackageAsync;
43
+ }
@@ -0,0 +1,213 @@
1
+ import { createHash } from "node:crypto";
2
+ import { CantonError } from "../../core/errors/canton-error.js";
3
+ import { DamlLfPackageLoader } from "../../daml-lf/daml-lf-package-loader.js";
4
+ import { DamlLfTemplate } from "../../daml-lf/model/daml-lf-template.js";
5
+ import { Lf2ModelMapper } from "../../daml-lf/model/lf-2-model-mapper.js";
6
+ import { Archive, HashFunction as ArchiveHashFunction } from "../../transports/grpc/generated/canton/com/digitalasset/daml/lf/archive/daml_lf.js";
7
+ import { HashFunction as PackageServiceHashFunction } from "../../transports/grpc/generated/canton/com/daml/ledger/api/v2/package_service.js";
8
+ import { validDottedNameString, validNameString, validPackageIdString } from "./grpc-query-value-mapper.js";
9
+ /** An explicit package-service/LF decoding failure; callers never receive partial package rows. */
10
+ export class GrpcPackageRelationError extends CantonError {
11
+ packageId;
12
+ constructor(packageId, message) {
13
+ super(`gRPC query package ${packageId} could not be materialized: ${message}`);
14
+ this.packageId = packageId;
15
+ Object.freeze(this);
16
+ }
17
+ }
18
+ /** Loads LF package metadata. Each call owns its promise map, so no package data enters QueryCacheStore. */
19
+ export class GrpcPackageRelationReader {
20
+ packageService;
21
+ packageLoader;
22
+ constructor(packageService, packageLoader = new DamlLfPackageLoader()) {
23
+ this.packageService = packageService;
24
+ this.packageLoader = packageLoader;
25
+ }
26
+ async readAllAsync() {
27
+ let packageIds;
28
+ try {
29
+ packageIds = packageIdsSnapshot(await this.packageService.listPackagesAsync({}));
30
+ }
31
+ catch (error) {
32
+ throw new GrpcPackageRelationError("<list>", errorMessage(error));
33
+ }
34
+ const packages = await this.readPackagesAsync(packageIds);
35
+ return Object.freeze(packages.filter((pkg) => pkg.templates.length > 0));
36
+ }
37
+ async readPackagesAsync(packageIds) {
38
+ const uniquePackageIds = new Set();
39
+ for (const packageId of packageIds) {
40
+ try {
41
+ validPackageDigest(packageId);
42
+ }
43
+ catch (error) {
44
+ throw new GrpcPackageRelationError(typeof packageId === "string" ? packageId : "<invalid>", errorMessage(error));
45
+ }
46
+ uniquePackageIds.add(packageId);
47
+ }
48
+ const packages = await Promise.all([...uniquePackageIds].map((packageId) => this.readPackageAsync(packageId)));
49
+ return Object.freeze(packages.slice().sort((left, right) => left.id.localeCompare(right.id)));
50
+ }
51
+ async readPackageAsync(packageId) {
52
+ let response;
53
+ try {
54
+ response = await this.packageService.getPackageAsync({ packageId });
55
+ }
56
+ catch (error) {
57
+ throw new GrpcPackageRelationError(packageId, errorMessage(error));
58
+ }
59
+ try {
60
+ if (response.hash !== packageId) {
61
+ throw new Error("Package Service response hash does not match its request");
62
+ }
63
+ else if (!(response.archivePayload instanceof Uint8Array) || response.archivePayload.length === 0) {
64
+ throw new Error("Package Service response archive payload is missing");
65
+ }
66
+ else if (response.hashFunction !== PackageServiceHashFunction.SHA256) {
67
+ throw new Error("Package Service response uses an unsupported hash function");
68
+ }
69
+ const digest = createHash("sha256").update(response.archivePayload).digest("hex");
70
+ if (digest !== packageId || digest !== response.hash) {
71
+ throw new Error("Package Service response archive payload does not match its SHA-256 hash");
72
+ }
73
+ const archive = Archive.toBinary({
74
+ hashFunction: ArchiveHashFunction.SHA256,
75
+ payload: response.archivePayload,
76
+ hash: response.hash,
77
+ });
78
+ const result = this.packageLoader.loadRawPackageOrThrow(archive);
79
+ const pkg = Lf2ModelMapper.mapPackage(result.packageId, result.rawPackage, result.languageVersion);
80
+ const packageName = requiredPackageText(pkg.packageName, "name");
81
+ const templates = [
82
+ ...pkg.modules.flatMap((module) => module.definitions
83
+ .filter((definition) => definition instanceof DamlLfTemplate)
84
+ .map((template) => templateMetadata(packageId, packageName, module.name, template))),
85
+ ...interfaceMetadata(packageId, packageName, result.rawPackage),
86
+ ];
87
+ return Object.freeze({
88
+ id: packageId,
89
+ name: packageName,
90
+ version: requiredPackageText(pkg.packageVersion, "version"),
91
+ templates: Object.freeze(templates),
92
+ });
93
+ }
94
+ catch (error) {
95
+ throw isGrpcPackageRelationError(error) ? error : new GrpcPackageRelationError(packageId, errorMessage(error));
96
+ }
97
+ }
98
+ }
99
+ function packageIdsSnapshot(response) {
100
+ if (response === null || typeof response !== "object") {
101
+ throw new Error("Package Service returned no package IDs");
102
+ }
103
+ const descriptor = Object.getOwnPropertyDescriptor(response, "packageIds");
104
+ if (descriptor === undefined || !("value" in descriptor) || !Array.isArray(descriptor.value)) {
105
+ throw new Error("Package Service returned no package IDs");
106
+ }
107
+ const packageIds = Array.from(descriptor.value, validPackageDigest);
108
+ if (packageIds.length === 0) {
109
+ throw new Error("Package Service returned no package IDs");
110
+ }
111
+ return Object.freeze(packageIds);
112
+ }
113
+ function validPackageDigest(value) {
114
+ if (typeof value !== "string") {
115
+ throw new Error("Package Service package ID is invalid");
116
+ }
117
+ validPackageIdString(value, "package id");
118
+ if (!/^[0-9a-f]{64}$/.test(value)) {
119
+ throw new Error("Package Service package ID is not a lowercase SHA-256 digest");
120
+ }
121
+ return value;
122
+ }
123
+ function templateMetadata(packageId, packageName, moduleName, template) {
124
+ validDottedNameString(moduleName, "DAML-LF module name");
125
+ validDottedNameString(template.templateId.templateName, "DAML-LF template name");
126
+ const entityName = template.templateId.templateName;
127
+ const templateFqn = `${packageName}:${moduleName}:${entityName}`;
128
+ return Object.freeze({
129
+ moduleName,
130
+ entityName,
131
+ payloadType: "template",
132
+ aliases: contractTypeAliases(packageName, moduleName, entityName),
133
+ templateFqn,
134
+ choices: Object.freeze(template.choices.map((choice) => {
135
+ validNameString(choice.name, "DAML-LF choice name");
136
+ return Object.freeze({ choice: choice.name, consuming: choice.consuming, aliases: exerciseTypeAliases(packageName, moduleName, entityName, choice.name), choiceFqn: `${templateFqn}:${choice.name}` });
137
+ }).sort((left, right) => left.choice.localeCompare(right.choice))),
138
+ });
139
+ }
140
+ function interfaceMetadata(packageId, packageName, rawPackage) {
141
+ return rawPackage.modules.flatMap((module) => {
142
+ const moduleName = resolveInternedDottedName(rawPackage, module.nameInternedDname);
143
+ return module.interfaces.map((definition) => rawInterfaceMetadata(packageId, packageName, moduleName, definition, rawPackage));
144
+ });
145
+ }
146
+ function rawInterfaceMetadata(packageId, packageName, moduleName, definition, rawPackage) {
147
+ validDottedNameString(moduleName, "DAML-LF module name");
148
+ const entityName = resolveInternedDottedName(rawPackage, definition.tyconInternedDname);
149
+ validDottedNameString(entityName, "DAML-LF interface name");
150
+ const templateFqn = `${packageName}:${moduleName}:${entityName}`;
151
+ return Object.freeze({
152
+ moduleName,
153
+ entityName,
154
+ payloadType: "interface",
155
+ aliases: contractTypeAliases(packageName, moduleName, entityName),
156
+ templateFqn,
157
+ choices: Object.freeze(definition.choices.map((choice) => rawInterfaceChoiceMetadata(packageId, packageName, moduleName, entityName, choice, rawPackage))
158
+ .sort((left, right) => left.choice.localeCompare(right.choice))),
159
+ });
160
+ }
161
+ function rawInterfaceChoiceMetadata(packageId, packageName, moduleName, entityName, choice, rawPackage) {
162
+ const choiceName = resolveInternedString(rawPackage, choice.nameInternedStr);
163
+ validNameString(choiceName, `DAML-LF package ${packageId} interface choice name`);
164
+ return Object.freeze({
165
+ choice: choiceName,
166
+ consuming: choice.consuming,
167
+ aliases: exerciseTypeAliases(packageName, moduleName, entityName, choiceName),
168
+ choiceFqn: `${packageName}:${moduleName}:${entityName}:${choiceName}`,
169
+ });
170
+ }
171
+ function contractTypeAliases(packageName, moduleName, entityName) {
172
+ return Object.freeze([`${packageName}:${moduleName}:${entityName}`, `${moduleName}:${entityName}`, entityName]);
173
+ }
174
+ function exerciseTypeAliases(packageName, moduleName, entityName, choiceName) {
175
+ return Object.freeze([`${packageName}:${moduleName}:${entityName}:${choiceName}`, `${moduleName}:${entityName}:${choiceName}`, `${entityName}:${choiceName}`, choiceName]);
176
+ }
177
+ function resolveInternedDottedName(rawPackage, index) {
178
+ const dottedName = rawPackage.internedDottedNames[index];
179
+ if (dottedName === undefined) {
180
+ throw new Error("DAML-LF interface references a missing interned dotted name");
181
+ }
182
+ return dottedName.segmentsInternedStr.map((segment) => resolveInternedString(rawPackage, segment)).join(".");
183
+ }
184
+ function resolveInternedString(rawPackage, index) {
185
+ const value = rawPackage.internedStrings[index];
186
+ if (value === undefined) {
187
+ throw new Error("DAML-LF interface references a missing interned string");
188
+ }
189
+ return value;
190
+ }
191
+ function requiredPackageText(value, name) {
192
+ if (value.length === 0) {
193
+ throw new Error(`DAML-LF package ${name} is missing`);
194
+ }
195
+ return value;
196
+ }
197
+ function errorMessage(error) {
198
+ try {
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ return typeof message === "string" ? message : "Unknown package service error";
201
+ }
202
+ catch {
203
+ return "Unknown package service error";
204
+ }
205
+ }
206
+ function isGrpcPackageRelationError(error) {
207
+ try {
208
+ return error instanceof GrpcPackageRelationError;
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ }
@@ -0,0 +1,42 @@
1
+ import type { PackageServiceClient } from "../../services/package/package-service-client.js";
2
+ import type { StateServiceClient } from "../../services/state/state-service-client.js";
3
+ import type { UpdateServiceClient } from "../../services/update/update-service-client.js";
4
+ import { type QueryDataset } from "../canonical/query-dataset.js";
5
+ import type { NormalizedAggregateQuery, NormalizedCountQuery, NormalizedFindManyQuery, NormalizedFindUniqueQuery, NormalizedGroupByQuery } from "../canonical/query-ast.js";
6
+ import type { ContractCacheArgs, ContractCacheResult, QueryClient } from "../query-client.js";
7
+ import { QuerySource } from "../query-source.js";
8
+ import { GrpcContractCache } from "./grpc-contract-cache.js";
9
+ type NormalizedQuery = NormalizedFindManyQuery | NormalizedFindUniqueQuery | NormalizedCountQuery | NormalizedAggregateQuery | NormalizedGroupByQuery;
10
+ /** The sole transport boundary used by every typed gRPC query delegate. */
11
+ interface GrpcQueryDataProvider {
12
+ readDatasetAsync(query: NormalizedQuery): Promise<QueryDataset>;
13
+ }
14
+ export interface GrpcQueryClientOptions {
15
+ readonly stateService: Pick<StateServiceClient, "getLedgerEndAsync" | "getLatestPrunedOffsetsAsync" | "getActiveContractsPageAsync">;
16
+ readonly updateService: Pick<UpdateServiceClient, "getUpdatesPageAsync">;
17
+ readonly packageService: Pick<PackageServiceClient, "listPackagesAsync" | "getPackageAsync">;
18
+ readonly contractCache?: GrpcContractCache;
19
+ readonly endpointScope?: string;
20
+ }
21
+ export declare class GrpcQueryClient implements QueryClient {
22
+ private readonly options;
23
+ readonly source = QuerySource.grpc;
24
+ private readonly evaluator;
25
+ private readonly dataProvider;
26
+ readonly contracts: QueryClient["contracts"];
27
+ readonly contractTypes: QueryClient["contractTypes"];
28
+ readonly events: QueryClient["events"];
29
+ readonly exercises: QueryClient["exercises"];
30
+ readonly exerciseTypes: QueryClient["exerciseTypes"];
31
+ readonly packages: QueryClient["packages"];
32
+ readonly transactions: QueryClient["transactions"];
33
+ readonly watermark: QueryClient["watermark"];
34
+ constructor(options: GrpcQueryClientOptions, dataProvider?: GrpcQueryDataProvider);
35
+ $queryRaw<TRow>(_sql: string, _values?: readonly unknown[]): Promise<readonly TRow[]>;
36
+ cacheContracts(args?: ContractCacheArgs): Promise<ContractCacheResult>;
37
+ invalidateContractsCache(args?: ContractCacheArgs): Promise<void>;
38
+ private delegate;
39
+ private collectionDelegate;
40
+ private execute;
41
+ }
42
+ export {};
@@ -0,0 +1,245 @@
1
+ import { ValidationError } from "../../core/errors/validation-error.js";
2
+ import { QueryCapabilityError } from "../errors/query-capability-error.js";
3
+ import { InMemoryQueryEvaluator } from "../canonical/in-memory-query-evaluator.js";
4
+ import { createQueryDataset } from "../canonical/query-dataset.js";
5
+ import { normalizeAggregate, normalizeCount, normalizeFindMany, normalizeFindUnique, normalizeGroupBy } from "../canonical/query-normalizer.js";
6
+ import { queryRelationEdges, queryRelations } from "../canonical/query-schema.js";
7
+ import { QuerySource } from "../query-source.js";
8
+ import { createGrpcQueryDataset, mapGrpcQueryRelationFragment, referencedGrpcPackageIds } from "./grpc-relation-mapper.js";
9
+ import { GrpcPackageRelationReader } from "./grpc-package-relation-reader.js";
10
+ import { GrpcQuerySnapshotReader } from "./grpc-query-snapshot-reader.js";
11
+ export class GrpcQueryClient {
12
+ options;
13
+ source = QuerySource.grpc;
14
+ evaluator = new InMemoryQueryEvaluator();
15
+ dataProvider;
16
+ contracts = this.delegate("contracts");
17
+ contractTypes = this.delegate("contractTypes");
18
+ events = this.delegate("events");
19
+ exercises = this.collectionDelegate("exercises");
20
+ exerciseTypes = this.delegate("exerciseTypes");
21
+ packages = this.delegate("packages");
22
+ transactions = this.delegate("transactions");
23
+ watermark = this.delegate("watermark");
24
+ constructor(options, dataProvider) {
25
+ this.options = options;
26
+ this.dataProvider = dataProvider ?? new DefaultGrpcQueryDataProvider(options);
27
+ }
28
+ async $queryRaw(_sql, _values = []) {
29
+ throw new QueryCapabilityError(QuerySource.grpc, "query.$queryRaw");
30
+ }
31
+ async cacheContracts(args) {
32
+ if (this.options.contractCache === undefined) {
33
+ throw new ValidationError("gRPC contract caching requires configured cache storage and ttlMs.");
34
+ }
35
+ return this.options.contractCache.cacheContracts(args);
36
+ }
37
+ async invalidateContractsCache(args) {
38
+ await this.options.contractCache?.invalidateContractsCache(args);
39
+ }
40
+ delegate(relation) {
41
+ return {
42
+ findMany: async (args = {}) => this.execute(normalizeFindMany(relation, args)),
43
+ findUnique: async (args) => this.execute(normalizeFindUnique(relation, args)),
44
+ count: async (args = {}) => this.execute(normalizeCount(relation, args)),
45
+ aggregate: async (args) => this.execute(normalizeAggregate(relation, args)),
46
+ groupBy: async (args) => this.execute(normalizeGroupBy(relation, args)),
47
+ };
48
+ }
49
+ collectionDelegate(relation) {
50
+ const delegate = this.delegate(relation);
51
+ delete delegate.findUnique;
52
+ return delegate;
53
+ }
54
+ async execute(query) {
55
+ const dataset = await this.dataProvider.readDatasetAsync(query);
56
+ return this.evaluator.execute(dataset, query);
57
+ }
58
+ }
59
+ class DefaultGrpcQueryDataProvider {
60
+ options;
61
+ snapshots;
62
+ packages;
63
+ constructor(options) {
64
+ this.options = options;
65
+ this.snapshots = new GrpcQuerySnapshotReader(options.stateService, options.updateService);
66
+ this.packages = new GrpcPackageRelationReader(options.packageService);
67
+ }
68
+ async readDatasetAsync(query) {
69
+ const closure = relationClosure(query);
70
+ const needsHistory = requiresHistory(query);
71
+ const activeOnly = query.relation === "contracts" && isActiveOnly(query);
72
+ const cached = activeOnly && this.options.contractCache !== undefined
73
+ ? await this.options.contractCache.readSnapshotAsync({ parties: partiesFor(query) })
74
+ : undefined;
75
+ if (cached !== undefined && !needsHistory && !requiresPackageMetadata(closure)) {
76
+ return cachedContractsDataset(cached.contracts, cached.activeAtOffset, this.options.endpointScope ?? "ledger");
77
+ }
78
+ else if (needsHistory) {
79
+ const endInclusive = cached?.activeAtOffset ?? (await this.options.stateService.getLedgerEndAsync({})).offset;
80
+ const history = await this.snapshots.readHistoryAsync(endInclusive);
81
+ const transactions = history.updates.flatMap((response) => response.update.oneofKind === "transaction" ? [response.update.transaction] : []);
82
+ const fragment = mapGrpcQueryRelationFragment(transactions);
83
+ const packageMetadata = !requiresPackageMetadata(closure)
84
+ ? []
85
+ : query.relation === "packages" || query.relation === "contractTypes" || query.relation === "exerciseTypes"
86
+ ? await this.packages.readAllAsync()
87
+ : await this.packages.readPackagesAsync(referencedGrpcPackageIds(fragment));
88
+ return packageMetadata.length === 0
89
+ ? fragmentDataset(fragment, endInclusive, this.options.endpointScope ?? "ledger")
90
+ : createGrpcQueryDataset(fragment, packageMetadata, endInclusive, this.options.endpointScope ?? "ledger");
91
+ }
92
+ const endInclusive = cached?.activeAtOffset ?? (await this.options.stateService.getLedgerEndAsync({})).offset;
93
+ if (query.relation === "packages" || query.relation === "contractTypes" || query.relation === "exerciseTypes") {
94
+ return createGrpcQueryDataset(mapGrpcQueryRelationFragment([]), await this.packages.readAllAsync(), endInclusive, this.options.endpointScope ?? "ledger");
95
+ }
96
+ else if (query.relation === "watermark") {
97
+ return createGrpcQueryDataset(mapGrpcQueryRelationFragment([]), [], endInclusive, this.options.endpointScope ?? "ledger");
98
+ }
99
+ const active = await this.snapshots.readActiveContractsAsync(endInclusive, partiesFor(query));
100
+ const fragment = mapGrpcQueryRelationFragment([], active.activeContracts);
101
+ return requiresPackageMetadata(closure)
102
+ ? createGrpcQueryDataset(fragment, await this.packages.readPackagesAsync(referencedGrpcPackageIds(fragment)), endInclusive, this.options.endpointScope ?? "ledger")
103
+ : fragmentDataset(fragment, endInclusive, this.options.endpointScope ?? "ledger", false);
104
+ }
105
+ }
106
+ function requiresHistory(query) {
107
+ if (query.relation === "transactions" || query.relation === "events" || query.relation === "exercises") {
108
+ return true;
109
+ }
110
+ else if (query.relation === "contracts" && !isActiveOnly(query)) {
111
+ return true;
112
+ }
113
+ return predicateRequiresHistory(query.relation, query.predicate) || includesRequireHistory(query.relation, includesFor(query));
114
+ }
115
+ function partiesFor(query) {
116
+ return query.kind === "findMany" || query.kind === "count" ? query.parties : undefined;
117
+ }
118
+ function includesFor(query) {
119
+ return query.kind === "findMany" || query.kind === "findUnique" ? query.includes : [];
120
+ }
121
+ function isActiveOnly(query) {
122
+ if (query.kind === "findMany" || query.kind === "count") {
123
+ return query.activeOnly;
124
+ }
125
+ return predicateProvesActive(query.predicate);
126
+ }
127
+ function predicateProvesActive(predicate) {
128
+ if (predicate === undefined) {
129
+ return false;
130
+ }
131
+ else if (predicate.kind === "scalar") {
132
+ return predicate.path.length === 1 && predicate.path[0] === "active" && predicate.operator === "equals" && predicate.value === true;
133
+ }
134
+ else if (predicate.kind === "and") {
135
+ return predicate.children.some(predicateProvesActive);
136
+ }
137
+ else if (predicate.kind === "or") {
138
+ return predicate.children.length > 0 && predicate.children.every(predicateProvesActive);
139
+ }
140
+ return false;
141
+ }
142
+ function requiresPackageMetadata(closure) {
143
+ return closure.has("packages") || closure.has("contractTypes") || closure.has("exercises") || closure.has("exerciseTypes");
144
+ }
145
+ function predicateRequiresHistory(relation, predicate) {
146
+ if (predicate === undefined || predicate.kind === "scalar") {
147
+ return false;
148
+ }
149
+ else if (predicate.kind === "not") {
150
+ return predicateRequiresHistory(relation, predicate.child);
151
+ }
152
+ else if (predicate.kind === "and" || predicate.kind === "or") {
153
+ return predicate.children.some((child) => predicateRequiresHistory(relation, child));
154
+ }
155
+ else if (predicate.kind !== "relation") {
156
+ return false;
157
+ }
158
+ const target = queryRelationEdges[relation]?.[predicate.edge]?.target;
159
+ return target === "contracts" || target === "transactions" || target === "events" || target === "exercises" || target !== undefined && predicateRequiresHistory(target, predicate.predicate);
160
+ }
161
+ function includesRequireHistory(relation, includes) {
162
+ return includes.some((include) => include.relation === "contracts" || include.relation === "transactions" || include.relation === "events" || include.relation === "exercises" || predicateRequiresHistory(include.relation, include.predicate) || includesRequireHistory(include.relation, include.includes));
163
+ }
164
+ function relationClosure(query) {
165
+ const relations = new Set([query.relation]);
166
+ const visitPredicate = (relation, predicate) => {
167
+ if (predicate === undefined || predicate.kind === "scalar") {
168
+ return;
169
+ }
170
+ else if (predicate.kind === "relation") {
171
+ const target = queryRelationEdges[relation]?.[predicate.edge]?.target;
172
+ if (target !== undefined) {
173
+ relations.add(target);
174
+ visitPredicate(target, predicate.predicate);
175
+ }
176
+ }
177
+ else if (predicate.kind === "not") {
178
+ visitPredicate(relation, predicate.child);
179
+ }
180
+ else {
181
+ predicate.children.forEach((child) => visitPredicate(relation, child));
182
+ }
183
+ };
184
+ const visitIncludes = (relation, includes) => {
185
+ for (const include of includes) {
186
+ relations.add(include.relation);
187
+ visitPredicate(include.relation, include.predicate);
188
+ visitIncludes(include.relation, include.includes);
189
+ }
190
+ };
191
+ visitPredicate(query.relation, query.predicate);
192
+ visitIncludes(query.relation, includesFor(query));
193
+ if (query.kind === "groupBy") {
194
+ for (const group of query.by) {
195
+ addRelationPath(query.relation, group.path, relations);
196
+ }
197
+ }
198
+ else if (query.kind === "findMany") {
199
+ for (const order of query.orderBy) {
200
+ addRelationPath(query.relation, order.path, relations);
201
+ }
202
+ }
203
+ return relations;
204
+ }
205
+ function addRelationPath(relation, path, relations) {
206
+ if (path.length > 1) {
207
+ const target = queryRelationEdges[relation]?.[path[0]]?.target;
208
+ if (target !== undefined) {
209
+ relations.add(target);
210
+ }
211
+ }
212
+ }
213
+ function cachedContractsDataset(contracts, offset, instanceId) {
214
+ const empty = [];
215
+ return basicDataset({ contracts: contracts, transactions: empty, events: empty, exercises: empty }, offset, instanceId, false);
216
+ }
217
+ function fragmentDataset(fragment, offset, instanceId, completeHistoryEdges = true) {
218
+ return basicDataset(fragment, offset, instanceId, completeHistoryEdges);
219
+ }
220
+ function basicDataset(rows, offset, instanceId, completeHistoryEdges = true) {
221
+ const empty = [];
222
+ return createQueryDataset({
223
+ rows: { contracts: rows.contracts, contractTypes: empty, events: rows.events, exercises: rows.exercises, exerciseTypes: empty, packages: empty, transactions: rows.transactions, watermark: [{ singleton: true, ix: offset, offset, instanceId }] },
224
+ uniqueKeys: { contracts: [["contractId"]], contractTypes: [["pk"]], events: [["pk"]], exercises: [["tpePk", "contractTpePk", "exerciseEventPk", "contractId"]], exerciseTypes: [["pk"]], packages: [["pk"], ["id"]], transactions: [["ix"], ["offset"]], watermark: [["singleton"]] },
225
+ edges: Object.fromEntries(queryRelations.map((relation) => [relation, Object.fromEntries(Object.keys(queryRelationEdges[relation] ?? {}).map((edge) => [edge, { ...cachedEdgePaths(relation, edge), ...(completeHistoryEdges ? completeFragmentEdge(relation, edge) : { complete: false }) }]))])),
226
+ });
227
+ }
228
+ function completeFragmentEdge(relation, edge) {
229
+ const complete = new Set([
230
+ "contracts.createdTransaction", "contracts.archivedTransaction", "contracts.exercises",
231
+ "events.transaction", "events.exercises",
232
+ "exercises.event", "exercises.transaction", "exercises.contract",
233
+ "transactions.events", "transactions.createdContracts", "transactions.archivedContracts", "transactions.exercises",
234
+ ]);
235
+ return complete.has(`${relation}.${edge}`) ? {} : { complete: false };
236
+ }
237
+ function cachedEdgePaths(relation, edge) {
238
+ const paths = {
239
+ "contracts.contractType": { from: ["contractId"], to: ["pk"] }, "contracts.createdTransaction": { from: ["createdEventOffset"], to: ["ix"] }, "contracts.archivedTransaction": { from: ["archivedEventOffset"], to: ["ix"] }, "contracts.exercises": { from: ["contractId"], to: ["contractId"] },
240
+ "contractTypes.contracts": { from: ["pk"], to: ["contractId"] }, "contractTypes.exercises": { from: ["pk"], to: ["contractTpePk"] }, "events.transaction": { from: ["txIx"], to: ["ix"] }, "events.exercises": { from: ["pk"], to: ["exerciseEventPk"] },
241
+ "exercises.exerciseType": { from: ["tpePk"], to: ["pk"] }, "exercises.contractType": { from: ["contractTpePk"], to: ["pk"] }, "exercises.event": { from: ["exerciseEventPk"], to: ["pk"] }, "exercises.transaction": { from: ["exercisedAtIx"], to: ["ix"] }, "exercises.package": { from: ["packagePk"], to: ["pk"] }, "exercises.contract": { from: ["contractId"], to: ["contractId"] },
242
+ "exerciseTypes.exercises": { from: ["pk"], to: ["tpePk"] }, "packages.exercises": { from: ["pk"], to: ["packagePk"] }, "transactions.events": { from: ["ix"], to: ["txIx"] }, "transactions.createdContracts": { from: ["ix"], to: ["createdEventOffset"] }, "transactions.archivedContracts": { from: ["ix"], to: ["archivedEventOffset"] }, "transactions.exercises": { from: ["ix"], to: ["exercisedAtIx"] },
243
+ };
244
+ return paths[`${relation}.${edge}`];
245
+ }
@@ -0,0 +1,33 @@
1
+ import { StateServiceClient } from "../../services/state/state-service-client.js";
2
+ import { UpdateServiceClient } from "../../services/update/update-service-client.js";
3
+ import { type GetActiveContractsResponse as GetActiveContractsResponseType } from "../../transports/grpc/generated/canton/com/daml/ledger/api/v2/state_service.js";
4
+ import { type GetUpdateResponse as GetUpdateResponseType } from "../../transports/grpc/generated/canton/com/daml/ledger/api/v2/update_service.js";
5
+ type StateSnapshotReader = Pick<StateServiceClient, "getLedgerEndAsync" | "getLatestPrunedOffsetsAsync" | "getActiveContractsPageAsync">;
6
+ type UpdateSnapshotReader = Pick<UpdateServiceClient, "getUpdatesPageAsync">;
7
+ export interface GrpcHistorySnapshot {
8
+ readonly endInclusive: string;
9
+ readonly updates: readonly GetUpdateResponseType[];
10
+ }
11
+ export interface GrpcActiveContractSnapshot {
12
+ readonly activeAtOffset: string;
13
+ readonly activeContracts: readonly GetActiveContractsResponseType[];
14
+ }
15
+ export interface GrpcQuerySnapshotReaderOptions {
16
+ readonly maxHistoryPages?: number;
17
+ readonly maxHistoryUpdates?: number;
18
+ readonly maxActiveContractPages?: number;
19
+ readonly maxActiveContracts?: number;
20
+ }
21
+ export declare class GrpcQuerySnapshotReader {
22
+ private readonly stateService;
23
+ private readonly updateService;
24
+ private readonly options;
25
+ constructor(stateService: StateSnapshotReader, updateService: UpdateSnapshotReader, options?: GrpcQuerySnapshotReaderOptions);
26
+ readCurrentHistoryAsync(): Promise<GrpcHistorySnapshot>;
27
+ readHistoryAsync(endInclusive: string): Promise<GrpcHistorySnapshot>;
28
+ readActiveContractsAsync(activeAtOffset: string, parties?: readonly string[]): Promise<GrpcActiveContractSnapshot>;
29
+ private historyError;
30
+ private activeError;
31
+ }
32
+ export declare function isCanonicalGrpcOffset(value: unknown): value is string;
33
+ export {};