@distrohelena/canton-typescript-sdk 0.1.38 → 0.1.40

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.
@@ -80,6 +80,10 @@ class DefaultGrpcQueryDataProvider {
80
80
  return cachedContractsDataset(cached.contracts, cached.activeAtOffset, this.options.endpointScope ?? "ledger");
81
81
  }
82
82
  else if (needsHistory) {
83
+ console.warn(`[GrpcQueryClient] Falling back to a full ledger replay from offset 0 for a "${query.relation}" query. `
84
+ + "This is expensive and should be an extreme edge case. It is usually triggered by a \"contracts\" query "
85
+ + "that does not explicitly prove `active: true` (so archived contracts may be in scope), or by querying "
86
+ + "\"transactions\"/\"events\"/\"exercises\" directly. Add an explicit active:true filter if only current state is needed.");
83
87
  const endInclusive = cached?.activeAtOffset ?? (await this.options.stateService.getLedgerEndAsync({})).offset;
84
88
  const history = await this.snapshots.readHistoryAsync(endInclusive);
85
89
  const transactions = history.updates.flatMap((response) => response.update.oneofKind === "transaction" ? [response.update.transaction] : []);
@@ -95,7 +99,13 @@ class DefaultGrpcQueryDataProvider {
95
99
  }
96
100
  const endInclusive = cached?.activeAtOffset ?? (await this.options.stateService.getLedgerEndAsync({})).offset;
97
101
  if (query.relation === "packages" || query.relation === "contractTypes" || query.relation === "exerciseTypes") {
98
- return (0, grpc_relation_mapper_js_1.createGrpcQueryDataset)((0, grpc_relation_mapper_js_1.mapGrpcQueryRelationFragment)([]), await this.packages.readAllAsync(), endInclusive, this.options.endpointScope ?? "ledger");
102
+ // A proven-active where/include on "contracts" (see predicateRequiresHistory/includesRequireHistory)
103
+ // can put "contracts" in the closure here without needsHistory being true, so the ACS still has to
104
+ // be read — otherwise that where/include would silently resolve against an empty row set.
105
+ const contractsFragment = closure.has("contracts")
106
+ ? (0, grpc_relation_mapper_js_1.mapGrpcQueryRelationFragment)([], (await this.snapshots.readActiveContractsAsync(endInclusive, partiesFor(query))).activeContracts)
107
+ : (0, grpc_relation_mapper_js_1.mapGrpcQueryRelationFragment)([]);
108
+ return (0, grpc_relation_mapper_js_1.createGrpcQueryDataset)(contractsFragment, await this.packages.readAllAsync(), endInclusive, this.options.endpointScope ?? "ledger");
99
109
  }
100
110
  else if (query.relation === "watermark") {
101
111
  return (0, grpc_relation_mapper_js_1.createGrpcQueryDataset)((0, grpc_relation_mapper_js_1.mapGrpcQueryRelationFragment)([]), [], endInclusive, this.options.endpointScope ?? "ledger");
@@ -160,10 +170,22 @@ function predicateRequiresHistory(relation, predicate) {
160
170
  return false;
161
171
  }
162
172
  const target = query_schema_js_1.queryRelationEdges[relation]?.[predicate.edge]?.target;
163
- return target === "contracts" || target === "transactions" || target === "events" || target === "exercises" || target !== undefined && predicateRequiresHistory(target, predicate.predicate);
173
+ if (target === "contracts") {
174
+ // "every"/"none" must see archived rows too (an archived row can silently violate "every",
175
+ // or an unseen one could exist for "none"); only "some"/"one" ask "does an active row match?",
176
+ // which the active contract set alone already answers completely.
177
+ const provenActiveOnly = (predicate.quantifier === "some" || predicate.quantifier === "one") && predicateProvesActive(predicate.predicate);
178
+ return !provenActiveOnly;
179
+ }
180
+ return target === "transactions" || target === "events" || target === "exercises" || target !== undefined && predicateRequiresHistory(target, predicate.predicate);
164
181
  }
165
182
  function includesRequireHistory(relation, includes) {
166
- 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));
183
+ return includes.some((include) => {
184
+ if (include.relation === "contracts") {
185
+ return !predicateProvesActive(include.predicate);
186
+ }
187
+ return include.relation === "transactions" || include.relation === "events" || include.relation === "exercises" || predicateRequiresHistory(include.relation, include.predicate) || includesRequireHistory(include.relation, include.includes);
188
+ });
167
189
  }
168
190
  function relationClosure(query) {
169
191
  const relations = new Set([query.relation]);
@@ -76,6 +76,10 @@ class DefaultGrpcQueryDataProvider {
76
76
  return cachedContractsDataset(cached.contracts, cached.activeAtOffset, this.options.endpointScope ?? "ledger");
77
77
  }
78
78
  else if (needsHistory) {
79
+ console.warn(`[GrpcQueryClient] Falling back to a full ledger replay from offset 0 for a "${query.relation}" query. `
80
+ + "This is expensive and should be an extreme edge case. It is usually triggered by a \"contracts\" query "
81
+ + "that does not explicitly prove `active: true` (so archived contracts may be in scope), or by querying "
82
+ + "\"transactions\"/\"events\"/\"exercises\" directly. Add an explicit active:true filter if only current state is needed.");
79
83
  const endInclusive = cached?.activeAtOffset ?? (await this.options.stateService.getLedgerEndAsync({})).offset;
80
84
  const history = await this.snapshots.readHistoryAsync(endInclusive);
81
85
  const transactions = history.updates.flatMap((response) => response.update.oneofKind === "transaction" ? [response.update.transaction] : []);
@@ -91,7 +95,13 @@ class DefaultGrpcQueryDataProvider {
91
95
  }
92
96
  const endInclusive = cached?.activeAtOffset ?? (await this.options.stateService.getLedgerEndAsync({})).offset;
93
97
  if (query.relation === "packages" || query.relation === "contractTypes" || query.relation === "exerciseTypes") {
94
- return createGrpcQueryDataset(mapGrpcQueryRelationFragment([]), await this.packages.readAllAsync(), endInclusive, this.options.endpointScope ?? "ledger");
98
+ // A proven-active where/include on "contracts" (see predicateRequiresHistory/includesRequireHistory)
99
+ // can put "contracts" in the closure here without needsHistory being true, so the ACS still has to
100
+ // be read — otherwise that where/include would silently resolve against an empty row set.
101
+ const contractsFragment = closure.has("contracts")
102
+ ? mapGrpcQueryRelationFragment([], (await this.snapshots.readActiveContractsAsync(endInclusive, partiesFor(query))).activeContracts)
103
+ : mapGrpcQueryRelationFragment([]);
104
+ return createGrpcQueryDataset(contractsFragment, await this.packages.readAllAsync(), endInclusive, this.options.endpointScope ?? "ledger");
95
105
  }
96
106
  else if (query.relation === "watermark") {
97
107
  return createGrpcQueryDataset(mapGrpcQueryRelationFragment([]), [], endInclusive, this.options.endpointScope ?? "ledger");
@@ -156,10 +166,22 @@ function predicateRequiresHistory(relation, predicate) {
156
166
  return false;
157
167
  }
158
168
  const target = queryRelationEdges[relation]?.[predicate.edge]?.target;
159
- return target === "contracts" || target === "transactions" || target === "events" || target === "exercises" || target !== undefined && predicateRequiresHistory(target, predicate.predicate);
169
+ if (target === "contracts") {
170
+ // "every"/"none" must see archived rows too (an archived row can silently violate "every",
171
+ // or an unseen one could exist for "none"); only "some"/"one" ask "does an active row match?",
172
+ // which the active contract set alone already answers completely.
173
+ const provenActiveOnly = (predicate.quantifier === "some" || predicate.quantifier === "one") && predicateProvesActive(predicate.predicate);
174
+ return !provenActiveOnly;
175
+ }
176
+ return target === "transactions" || target === "events" || target === "exercises" || target !== undefined && predicateRequiresHistory(target, predicate.predicate);
160
177
  }
161
178
  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));
179
+ return includes.some((include) => {
180
+ if (include.relation === "contracts") {
181
+ return !predicateProvesActive(include.predicate);
182
+ }
183
+ return include.relation === "transactions" || include.relation === "events" || include.relation === "exercises" || predicateRequiresHistory(include.relation, include.predicate) || includesRequireHistory(include.relation, include.includes);
184
+ });
163
185
  }
164
186
  function relationClosure(query) {
165
187
  const relations = new Set([query.relation]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@distrohelena/canton-typescript-sdk",
3
- "version": "0.1.38",
3
+ "version": "0.1.40",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",