@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.
- package/README.md +104 -34
- package/dist/cjs/core/types/requests/{submit-command-request.js → submit-commands-request.js} +10 -6
- package/dist/cjs/index.js +10 -8
- package/dist/cjs/query/canonical/in-memory-query-evaluator.js +291 -0
- package/dist/cjs/query/canonical/public-identity.js +41 -0
- package/dist/cjs/query/canonical/query-ast.js +2 -0
- package/dist/cjs/query/canonical/query-dataset.js +351 -0
- package/dist/cjs/query/canonical/query-normalizer.js +655 -0
- package/dist/cjs/query/canonical/query-schema.js +86 -0
- package/dist/cjs/query/canton-manager.js +14 -5
- package/dist/cjs/query/errors/query-snapshot-incomplete-error.js +19 -0
- package/dist/cjs/query/grpc/grpc-contract-cache.js +484 -0
- package/dist/cjs/query/grpc/grpc-package-relation-reader.js +218 -0
- package/dist/cjs/query/grpc/grpc-query-client.js +249 -0
- package/dist/cjs/query/grpc/grpc-query-snapshot-reader.js +238 -0
- package/dist/cjs/query/grpc/grpc-query-value-mapper.js +204 -0
- package/dist/cjs/query/grpc/grpc-relation-mapper.js +794 -0
- package/dist/cjs/query/pqs/pqs-query-client.js +151 -582
- package/dist/cjs/query/pqs/pqs-relational-sql-compiler.js +138 -95
- package/dist/cjs/query/pqs/pqs-result-shape.js +28 -0
- package/dist/cjs/query/pqs/pqs-schema-profile.js +49 -48
- package/dist/cjs/query/pqs/pqs-sql-compiler.js +343 -289
- package/dist/cjs/query/pqs/pqs-sql-syntax.js +10 -0
- package/dist/cjs/services/command/command-service-client.js +16 -3
- package/dist/cjs/services/commands/command-payload-builder.js +1 -1
- package/dist/cjs/services/commands/command-submission-pipeline.js +21 -6
- package/dist/cjs/testing/runtime/declarative-action-executor.js +3 -3
- package/dist/cjs/transports/grpc/grpc-transport.js +2 -2
- package/dist/cjs/transports/grpc/mappers/commands-mapper.js +6 -6
- package/dist/cjs/transports/grpc/mappers/interactive-command-mapper.js +1 -1
- package/dist/cjs/transports/json/json-transport.js +1 -1
- package/dist/cjs/transports/json/mappers/commands-mapper.js +3 -3
- package/dist/core/transports/transport.interface.d.ts +4 -4
- package/dist/core/types/prepared-command-submission.d.ts +3 -3
- package/dist/core/types/requests/{submit-command-request.d.ts → submit-commands-request.d.ts} +8 -4
- package/dist/core/types/requests/{submit-command-request.js → submit-commands-request.js} +8 -4
- package/dist/index.d.ts +5 -2
- package/dist/index.js +2 -1
- package/dist/query/canonical/in-memory-query-evaluator.d.ts +22 -0
- package/dist/query/canonical/in-memory-query-evaluator.js +287 -0
- package/dist/query/canonical/public-identity.d.ts +14 -0
- package/dist/query/canonical/public-identity.js +35 -0
- package/dist/query/canonical/query-ast.d.ts +104 -0
- package/dist/query/canonical/query-ast.js +1 -0
- package/dist/query/canonical/query-dataset.d.ts +63 -0
- package/dist/query/canonical/query-dataset.js +343 -0
- package/dist/query/canonical/query-normalizer.d.ts +10 -0
- package/dist/query/canonical/query-normalizer.js +646 -0
- package/dist/query/canonical/query-schema.d.ts +25 -0
- package/dist/query/canonical/query-schema.js +83 -0
- package/dist/query/canton-manager.js +14 -5
- package/dist/query/errors/query-snapshot-incomplete-error.d.ts +14 -0
- package/dist/query/errors/query-snapshot-incomplete-error.js +15 -0
- package/dist/query/grpc/grpc-contract-cache.d.ts +27 -0
- package/dist/query/grpc/grpc-contract-cache.js +479 -0
- package/dist/query/grpc/grpc-package-relation-reader.d.ts +43 -0
- package/dist/query/grpc/grpc-package-relation-reader.js +213 -0
- package/dist/query/grpc/grpc-query-client.d.ts +42 -0
- package/dist/query/grpc/grpc-query-client.js +245 -0
- package/dist/query/grpc/grpc-query-snapshot-reader.d.ts +33 -0
- package/dist/query/grpc/grpc-query-snapshot-reader.js +233 -0
- package/dist/query/grpc/grpc-query-value-mapper.d.ts +15 -0
- package/dist/query/grpc/grpc-query-value-mapper.js +195 -0
- package/dist/query/grpc/grpc-relation-mapper.d.ts +67 -0
- package/dist/query/grpc/grpc-relation-mapper.js +789 -0
- package/dist/query/pqs/pqs-query-client.d.ts +10 -20
- package/dist/query/pqs/pqs-query-client.js +154 -585
- package/dist/query/pqs/pqs-relational-sql-compiler.d.ts +9 -17
- package/dist/query/pqs/pqs-relational-sql-compiler.js +136 -95
- package/dist/query/pqs/pqs-result-shape.d.ts +25 -0
- package/dist/query/pqs/pqs-result-shape.js +25 -0
- package/dist/query/pqs/pqs-schema-profile.js +49 -48
- package/dist/query/pqs/pqs-sql-compiler.d.ts +18 -3
- package/dist/query/pqs/pqs-sql-compiler.js +337 -288
- package/dist/query/pqs/pqs-sql-syntax.d.ts +2 -0
- package/dist/query/pqs/pqs-sql-syntax.js +6 -0
- package/dist/query/query-client.d.ts +15 -0
- package/dist/services/command/command-service-client.d.ts +9 -4
- package/dist/services/command/command-service-client.js +16 -3
- package/dist/services/command-submission/command-submission-service-client.d.ts +2 -2
- package/dist/services/commands/command-payload-builder.d.ts +2 -2
- package/dist/services/commands/command-payload-builder.js +1 -1
- package/dist/services/commands/command-submission-pipeline.d.ts +5 -4
- package/dist/services/commands/command-submission-pipeline.js +21 -6
- package/dist/testing/runtime/declarative-action-executor.js +3 -3
- package/dist/transports/grpc/grpc-transport.d.ts +4 -4
- package/dist/transports/grpc/grpc-transport.js +3 -3
- package/dist/transports/grpc/mappers/commands-mapper.d.ts +3 -3
- package/dist/transports/grpc/mappers/commands-mapper.js +4 -4
- package/dist/transports/grpc/mappers/interactive-command-mapper.d.ts +3 -3
- package/dist/transports/grpc/mappers/interactive-command-mapper.js +1 -1
- package/dist/transports/json/json-transport.d.ts +2 -2
- package/dist/transports/json/json-transport.js +2 -2
- package/dist/transports/json/mappers/commands-mapper.d.ts +2 -2
- package/dist/transports/json/mappers/commands-mapper.js +2 -2
- package/package.json +4 -3
- package/dist/cjs/query/grpc/grpc-contract-query-client.js +0 -178
- package/dist/query/grpc/grpc-contract-query-client.d.ts +0 -33
- package/dist/query/grpc/grpc-contract-query-client.js +0 -174
|
@@ -1,37 +1,33 @@
|
|
|
1
1
|
import { QuerySource } from "../query-source.js";
|
|
2
2
|
import { PqsQueryError } from "../errors/pqs-query-error.js";
|
|
3
|
-
import { compileContractFindMany, compileContractGroupBy } from "./pqs-sql-compiler.js";
|
|
4
|
-
import { compilePqsRelationGroupBy } from "./pqs-relational-sql-compiler.js";
|
|
5
|
-
import {
|
|
3
|
+
import { compileContractAggregate, compileContractCount, compileContractFindMany, compileContractGroupBy } from "./pqs-sql-compiler.js";
|
|
4
|
+
import { compilePqsRelationAggregate, compilePqsRelationCount, compilePqsRelationFindMany, compilePqsRelationGroupBy, } from "./pqs-relational-sql-compiler.js";
|
|
5
|
+
import { normalizeAggregate, normalizeCount, normalizeFindMany, normalizeFindUnique, normalizeGroupBy } from "../canonical/query-normalizer.js";
|
|
6
|
+
import { pqsRelationMetadata } from "./pqs-schema-profile.js";
|
|
6
7
|
import { assertReadOnlySql } from "./read-only-sql.js";
|
|
7
|
-
const
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
const queryRelationForPqs = {
|
|
9
|
+
__contracts: "contracts", __contract_tpe: "contractTypes", __events: "events", __exercises: "exercises",
|
|
10
|
+
__exercise_tpe: "exerciseTypes", __packages: "packages", __transactions: "transactions", __watermark: "watermark",
|
|
11
|
+
};
|
|
12
|
+
function normalizedUniqueAsFindMany(query) {
|
|
13
|
+
return {
|
|
14
|
+
kind: "findMany", relation: query.relation, predicate: query.predicate,
|
|
15
|
+
select: query.select, includes: query.includes,
|
|
16
|
+
orderBy: query.relation === "contracts" ? [{ path: ["contractId"], direction: "asc" }] : [],
|
|
17
|
+
skip: 0, take: 1, activeOnly: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
19
20
|
export class PqsQueryClient {
|
|
20
21
|
executor;
|
|
21
22
|
profile;
|
|
22
23
|
ready;
|
|
23
24
|
source = QuerySource.pqs;
|
|
24
25
|
contracts = {
|
|
25
|
-
findMany: (args = {}) => this.findContractsAsync(args),
|
|
26
|
-
findUnique: (args) => this.findContractsAsync(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
take: 1,
|
|
31
|
-
}).then((rows) => rows[0]),
|
|
32
|
-
count: async (args = {}) => (await this.findContractsAsync(args)).length,
|
|
33
|
-
aggregate: async (args) => this.aggregateContractsAsync(args),
|
|
34
|
-
groupBy: async (args) => this.groupContractsAsync(args),
|
|
26
|
+
findMany: async (args = {}) => await this.findContractsAsync(normalizeFindMany("contracts", args)),
|
|
27
|
+
findUnique: async (args) => (await this.findContractsAsync(normalizedUniqueAsFindMany(normalizeFindUnique("contracts", args))))[0],
|
|
28
|
+
count: async (args = {}) => this.countContractsAsync(normalizeCount("contracts", args)),
|
|
29
|
+
aggregate: async (args) => this.aggregateContractsAsync(normalizeAggregate("contracts", args)),
|
|
30
|
+
groupBy: async (args) => this.groupContractsAsync(normalizeGroupBy("contracts", args)),
|
|
35
31
|
};
|
|
36
32
|
contractTypes = this.createPhysicalDelegate("__contract_tpe");
|
|
37
33
|
events = this.createPhysicalDelegate("__events");
|
|
@@ -55,105 +51,54 @@ export class PqsQueryClient {
|
|
|
55
51
|
throw this.wrap("$queryRaw", cause);
|
|
56
52
|
}
|
|
57
53
|
}
|
|
54
|
+
async cacheContracts(_args) {
|
|
55
|
+
return { source: QuerySource.pqs, cached: false };
|
|
56
|
+
}
|
|
57
|
+
async invalidateContractsCache(_args) { }
|
|
58
58
|
createPhysicalDelegate(relation, hasUnique = true) {
|
|
59
|
-
const
|
|
60
|
-
const findMany = async (args = {}) => this.readPhysicalAsync(relation, metadata, args);
|
|
59
|
+
const queryRelation = queryRelationForPqs[relation];
|
|
61
60
|
const delegate = {
|
|
62
|
-
findMany,
|
|
63
|
-
count: async (args = {}) =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await this.ready;
|
|
67
|
-
const result = await this.executor.query(`select count(*)::text as count from ${this.profile.relation(relation)}${where}`, values);
|
|
68
|
-
return Number(result.rows[0]?.count ?? 0);
|
|
69
|
-
}
|
|
70
|
-
catch (cause) {
|
|
71
|
-
throw this.wrap(`${relation}.count`, cause);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
aggregate: async (args) => this.aggregatePhysicalAsync(relation, metadata, args),
|
|
75
|
-
groupBy: async (args) => this.groupPhysicalAsync(relation, args),
|
|
61
|
+
findMany: async (args = {}) => this.readPhysicalAsync(relation, normalizeFindMany(queryRelation, args)),
|
|
62
|
+
count: async (args = {}) => this.countPhysicalAsync(relation, normalizeCount(queryRelation, args)),
|
|
63
|
+
aggregate: async (args) => this.aggregatePhysicalAsync(relation, normalizeAggregate(queryRelation, args)),
|
|
64
|
+
groupBy: async (args) => this.groupPhysicalAsync(relation, normalizeGroupBy(queryRelation, args)),
|
|
76
65
|
};
|
|
77
|
-
|
|
78
|
-
return delegate;
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
66
|
+
return hasUnique ? {
|
|
81
67
|
...delegate,
|
|
82
|
-
findUnique: async (args) =>
|
|
83
|
-
|
|
84
|
-
return findMany({
|
|
85
|
-
where: Object.fromEntries(Object.entries(args.where).map(([field, value]) => [field, { equals: value }])),
|
|
86
|
-
select: args.select,
|
|
87
|
-
include: args.include,
|
|
88
|
-
take: 1,
|
|
89
|
-
}).then((rows) => rows[0]);
|
|
90
|
-
},
|
|
91
|
-
};
|
|
68
|
+
findUnique: async (args) => (await this.readPhysicalAsync(relation, normalizedUniqueAsFindMany(normalizeFindUnique(queryRelation, args))))[0],
|
|
69
|
+
} : delegate;
|
|
92
70
|
}
|
|
93
|
-
async
|
|
94
|
-
const
|
|
95
|
-
const filter = this.compileWhere(relation, pqsRelationMetadata[relation], args.where, undefined, root);
|
|
96
|
-
const compiled = compilePqsRelationGroupBy(relation, args, this.profile, filter.values.length);
|
|
97
|
-
const text = filter.where.length === 0 ? compiled.text : compiled.text.replace(" group by ", `${filter.where} group by `);
|
|
71
|
+
async readPhysicalAsync(relation, query) {
|
|
72
|
+
const compiled = compilePqsRelationFindMany(relation, query, this.profile);
|
|
98
73
|
try {
|
|
99
74
|
await this.ready;
|
|
100
|
-
return (await this.executor.query(text,
|
|
75
|
+
return (await this.executor.query(compiled.text, compiled.values)).rows.map((row) => this.mapPhysicalRow(row, compiled.resultShape));
|
|
101
76
|
}
|
|
102
77
|
catch (cause) {
|
|
103
|
-
throw this.wrap(`${relation}.
|
|
78
|
+
throw this.wrap(`${relation}.findMany`, cause);
|
|
104
79
|
}
|
|
105
80
|
}
|
|
106
|
-
async
|
|
107
|
-
this.
|
|
108
|
-
const selected = this.selectedFields(relation, metadata, args.select);
|
|
109
|
-
const { where, values } = this.compileWhere(relation, metadata, args.where);
|
|
110
|
-
const orderBy = this.compileOrderBy(relation, metadata, args.orderBy);
|
|
111
|
-
const parameters = [...values];
|
|
112
|
-
const add = (value) => {
|
|
113
|
-
parameters.push(value);
|
|
114
|
-
return `$${parameters.length}`;
|
|
115
|
-
};
|
|
116
|
-
const limit = args.take === undefined ? "" : ` limit ${add(args.take)}`;
|
|
117
|
-
const offset = args.skip === undefined ? "" : ` offset ${add(args.skip)}`;
|
|
118
|
-
const selection = selected.map(([field, column]) => `"${column}" as "${field}"`).join(", ");
|
|
119
|
-
const includes = this.compileIncludes(relation, args.include, add);
|
|
120
|
-
const json = this.compileJsonSelections(relation, args.select?.json, add);
|
|
121
|
-
const fullSelection = [selection, ...json.selection, ...includes.selection].filter((value) => value.length > 0).join(", ");
|
|
81
|
+
async countPhysicalAsync(relation, query) {
|
|
82
|
+
const compiled = compilePqsRelationCount(relation, query, this.profile);
|
|
122
83
|
try {
|
|
123
84
|
await this.ready;
|
|
124
|
-
|
|
125
|
-
return result.rows.map((row) => this.mapPhysicalRow(row, metadata, selected, includes.values, json.fields));
|
|
85
|
+
return Number((await this.executor.query(compiled.text, compiled.values)).rows[0]?.count ?? 0);
|
|
126
86
|
}
|
|
127
87
|
catch (cause) {
|
|
128
|
-
throw this.wrap(`${relation}.
|
|
88
|
+
throw this.wrap(`${relation}.count`, cause);
|
|
129
89
|
}
|
|
130
90
|
}
|
|
131
|
-
async aggregatePhysicalAsync(relation,
|
|
132
|
-
const
|
|
133
|
-
const selected = [];
|
|
134
|
-
if (args.count) {
|
|
135
|
-
selected.push("count(*)::text as count");
|
|
136
|
-
}
|
|
137
|
-
for (const [operation, fields] of [["min", args.min], ["max", args.max], ["sum", args.sum]]) {
|
|
138
|
-
for (const field of fields ?? []) {
|
|
139
|
-
this.assertNumericField(relation, metadata, field);
|
|
140
|
-
selected.push(`${operation}("${metadata.fields[field]}")::text as "${operation}_${field}"`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
if (selected.length === 0) {
|
|
144
|
-
throw new Error("aggregate must request at least one result");
|
|
145
|
-
}
|
|
91
|
+
async aggregatePhysicalAsync(relation, query) {
|
|
92
|
+
const compiled = compilePqsRelationAggregate(relation, query, this.profile);
|
|
146
93
|
try {
|
|
147
94
|
await this.ready;
|
|
148
|
-
const row = (await this.executor.query(
|
|
95
|
+
const row = (await this.executor.query(compiled.text, compiled.values)).rows[0] ?? {};
|
|
149
96
|
const result = {};
|
|
150
|
-
if (
|
|
97
|
+
if (query.aggregates.count)
|
|
151
98
|
result.count = Number(row.count ?? 0);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (fields !== undefined) {
|
|
99
|
+
for (const [operation, fields] of [["min", query.aggregates.min], ["max", query.aggregates.max], ["sum", query.aggregates.sum]]) {
|
|
100
|
+
if (fields.length > 0)
|
|
155
101
|
result[operation] = Object.fromEntries(fields.map((field) => [field, nullableString(row[`${operation}_${field}`])]));
|
|
156
|
-
}
|
|
157
102
|
}
|
|
158
103
|
return result;
|
|
159
104
|
}
|
|
@@ -161,403 +106,79 @@ export class PqsQueryClient {
|
|
|
161
106
|
throw this.wrap(`${relation}.aggregate`, cause);
|
|
162
107
|
}
|
|
163
108
|
}
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
169
|
-
});
|
|
170
|
-
if (relation === "__contracts") {
|
|
171
|
-
const where = this.compileLogicalContractWhere(filters, add, parentExpression);
|
|
172
|
-
return { where: where === "true" ? "" : ` where ${where}`, values };
|
|
173
|
-
}
|
|
174
|
-
const compile = (expression) => {
|
|
175
|
-
const conditions = [];
|
|
176
|
-
for (const [field, filter] of Object.entries(expression ?? {})) {
|
|
177
|
-
if (field === "and" || field === "or") {
|
|
178
|
-
if (!Array.isArray(filter))
|
|
179
|
-
throw new Error(`${field} must be an array`);
|
|
180
|
-
conditions.push(filter.length ? `(${filter.map((child) => compile(child)).join(` ${field} `)})` : field === "and" ? "true" : "false");
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (field === "not") {
|
|
184
|
-
if (filter === null || Array.isArray(filter) || typeof filter !== "object")
|
|
185
|
-
throw new Error("not must be an expression");
|
|
186
|
-
conditions.push(`not (${compile(filter)})`);
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
const edge = pqsRelationEdges[relation]?.[field];
|
|
190
|
-
if (edge !== undefined) {
|
|
191
|
-
if (filter === null || Array.isArray(filter) || typeof filter !== "object")
|
|
192
|
-
throw new Error(`${field} must be a relation filter`);
|
|
193
|
-
const related = filter;
|
|
194
|
-
const alias = `"${field}"`;
|
|
195
|
-
const relatedCondition = (child) => {
|
|
196
|
-
const where = this.compileWhere(edge.target, pqsRelationMetadata[edge.target], child, add, alias).where;
|
|
197
|
-
return where.length === 0 ? "true" : where.slice(" where ".length);
|
|
198
|
-
};
|
|
199
|
-
const join = `${alias}."${edge.targetColumn}" = ${parentExpression}."${edge.sourceColumn}"`;
|
|
200
|
-
if (edge.cardinality === "one") {
|
|
201
|
-
if (Object.keys(related).some((name) => ["some", "none", "every"].includes(name)))
|
|
202
|
-
throw new Error(`${field} is a to-one relation`);
|
|
203
|
-
conditions.push(`exists (select 1 from ${this.profile.relation(edge.target)} ${alias} where ${join} and (${relatedCondition(related)}))`);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
const operators = ["some", "none", "every"].filter((name) => related[name] !== undefined);
|
|
207
|
-
if (operators.length !== 1)
|
|
208
|
-
throw new Error(`${field} requires exactly one of some, none, or every`);
|
|
209
|
-
const operator = operators[0];
|
|
210
|
-
const predicate = related[operator];
|
|
211
|
-
const condition = relatedCondition(predicate);
|
|
212
|
-
const subquery = `select 1 from ${this.profile.relation(edge.target)} ${alias} where ${join} and (${condition})`;
|
|
213
|
-
if (operator === "some")
|
|
214
|
-
conditions.push(`exists (${subquery})`);
|
|
215
|
-
else if (operator === "none")
|
|
216
|
-
conditions.push(`not exists (${subquery})`);
|
|
217
|
-
else
|
|
218
|
-
conditions.push(`not exists (select 1 from ${this.profile.relation(edge.target)} ${alias} where ${join} and not (${condition}))`);
|
|
219
|
-
}
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
const column = this.field(relation, metadata, field);
|
|
223
|
-
if (filter === null || Array.isArray(filter) || typeof filter !== "object")
|
|
224
|
-
throw new Error(`${field} must be a filter`);
|
|
225
|
-
const scalar = filter;
|
|
226
|
-
if (PqsSchemaProfileV1.jsonField(relation, field) && scalar.path !== undefined) {
|
|
227
|
-
if (scalar.path.length === 0 || scalar.path.some((segment) => segment.length === 0))
|
|
228
|
-
throw new Error(`${field}.path must be a non-empty JSON path`);
|
|
229
|
-
const expression = `"${column}" #>> ${add(scalar.path)}::text[]`;
|
|
230
|
-
if (scalar.is === null)
|
|
231
|
-
conditions.push(`${expression} is null`);
|
|
232
|
-
if (scalar.isNot === null)
|
|
233
|
-
conditions.push(`${expression} is not null`);
|
|
234
|
-
if (scalar.equals !== undefined)
|
|
235
|
-
conditions.push(`${expression} = ${add(scalar.equals)}`);
|
|
236
|
-
if (scalar.in !== undefined)
|
|
237
|
-
conditions.push(scalar.in.length === 0 ? "false" : `${expression} = any(${add(scalar.in)})`);
|
|
238
|
-
for (const [operator, value] of [["lt", scalar.lt], ["lte", scalar.lte], ["gt", scalar.gt], ["gte", scalar.gte], ["like", scalar.like], ["ilike", scalar.ilike]]) {
|
|
239
|
-
if (value !== undefined)
|
|
240
|
-
conditions.push(`${expression} ${operator === "lte" ? "<=" : operator === "gte" ? ">=" : operator} ${add(value)}`);
|
|
241
|
-
}
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
if (scalar.is === null) {
|
|
245
|
-
conditions.push(`"${column}" is null`);
|
|
246
|
-
}
|
|
247
|
-
if (scalar.isNot === null) {
|
|
248
|
-
conditions.push(`"${column}" is not null`);
|
|
249
|
-
}
|
|
250
|
-
if (scalar.equals !== undefined) {
|
|
251
|
-
conditions.push(`"${column}" = ${add(scalar.equals)}`);
|
|
252
|
-
}
|
|
253
|
-
if (scalar.in !== undefined) {
|
|
254
|
-
conditions.push(scalar.in.length === 0 ? "false" : `"${column}" = any(${add(scalar.in)})`);
|
|
255
|
-
}
|
|
256
|
-
if (scalar.has !== undefined) {
|
|
257
|
-
if (!metadata.arrayFields.includes(field)) {
|
|
258
|
-
throw new Error(`${field} is not an array field of ${relation}`);
|
|
259
|
-
}
|
|
260
|
-
conditions.push(`${add(scalar.has)} = any("${column}")`);
|
|
261
|
-
}
|
|
262
|
-
for (const [operator, value] of [["lt", scalar.lt], ["lte", scalar.lte], ["gt", scalar.gt], ["gte", scalar.gte], ["like", scalar.like], ["ilike", scalar.ilike]]) {
|
|
263
|
-
if (value === undefined)
|
|
264
|
-
continue;
|
|
265
|
-
if ((operator === "like" || operator === "ilike") && !metadata.stringFields?.includes(field))
|
|
266
|
-
throw new Error(`${field} does not support ${operator}`);
|
|
267
|
-
if ((operator !== "like" && operator !== "ilike") && !metadata.numericFields.includes(field) && !metadata.dateFields.includes(field) && !metadata.stringFields?.includes(field))
|
|
268
|
-
throw new Error(`${field} does not support ${operator}`);
|
|
269
|
-
conditions.push(`"${column}" ${operator === "lte" ? "<=" : operator === "gte" ? ">=" : operator} ${add(value)}`);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return conditions.length ? conditions.join(" and ") : "true";
|
|
273
|
-
};
|
|
274
|
-
const where = compile(filters);
|
|
275
|
-
return { where: where === "true" ? "" : ` where ${where}`, values };
|
|
276
|
-
}
|
|
277
|
-
compileLogicalContractWhere(filters, add, alias) {
|
|
278
|
-
const compile = (expression) => {
|
|
279
|
-
const conditions = [];
|
|
280
|
-
for (const [field, filter] of Object.entries(expression ?? {})) {
|
|
281
|
-
if (field === "and" || field === "or") {
|
|
282
|
-
if (!Array.isArray(filter))
|
|
283
|
-
throw new Error(`${field} must be an array`);
|
|
284
|
-
conditions.push(filter.length === 0 ? field === "and" ? "true" : "false" : `(${filter.map((child) => compile(child)).join(` ${field} `)})`);
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
if (field === "not") {
|
|
288
|
-
conditions.push(`not (${compile(filter)})`);
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
if (field === "active") {
|
|
292
|
-
const value = typeof filter === "boolean" ? filter : filter.equals;
|
|
293
|
-
if (typeof value !== "boolean")
|
|
294
|
-
throw new Error("active supports only equals");
|
|
295
|
-
conditions.push(`${alias}."archived_at_ix" is ${value ? "null" : "not null"}`);
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
if (field === "witnesses") {
|
|
299
|
-
const value = filter.has;
|
|
300
|
-
if (typeof value !== "string")
|
|
301
|
-
throw new Error("witnesses supports only has");
|
|
302
|
-
conditions.push(`${add(value)} = any(${alias}."witnesses")`);
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
if (field === "payload") {
|
|
306
|
-
const match = filter.match;
|
|
307
|
-
if (match === undefined || typeof match !== "object" || match === null)
|
|
308
|
-
throw new Error("payload requires match");
|
|
309
|
-
for (const [key, value] of Object.entries(match))
|
|
310
|
-
if (value.equals !== undefined)
|
|
311
|
-
conditions.push(`${alias}."payload" #>> ${add([key])}::text[] = ${add(value.equals)}`);
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
const columns = { contractId: "contract_id", packageId: "creation_package_id", createdEventOffset: "created_at_ix", archivedEventOffset: "archived_at_ix" };
|
|
315
|
-
const column = columns[field];
|
|
316
|
-
if (column === undefined || filter === null || typeof filter !== "object")
|
|
317
|
-
throw new Error(`${field} is not a supported contract filter`);
|
|
318
|
-
const value = filter;
|
|
319
|
-
if (value.equals !== undefined)
|
|
320
|
-
conditions.push(`${alias}."${column}" = ${add(value.equals)}`);
|
|
321
|
-
else if (value.in !== undefined)
|
|
322
|
-
conditions.push(value.in.length === 0 ? "false" : `${alias}."${column}" = any(${add(value.in)})`);
|
|
323
|
-
else
|
|
324
|
-
throw new Error(`${field} requires equals or in`);
|
|
325
|
-
}
|
|
326
|
-
return conditions.length === 0 ? "true" : conditions.join(" and ");
|
|
327
|
-
};
|
|
328
|
-
return compile(filters);
|
|
329
|
-
}
|
|
330
|
-
selectedFields(relation, metadata, select) {
|
|
331
|
-
if (select === undefined) {
|
|
332
|
-
return Object.entries(metadata.fields);
|
|
333
|
-
}
|
|
334
|
-
const selected = Object.entries(select).filter(([field, enabled]) => field !== "json" && enabled === true).map(([field]) => [field, this.field(relation, metadata, field)]);
|
|
335
|
-
if (selected.length === 0 && Object.keys(select.json ?? {}).length === 0) {
|
|
336
|
-
throw new Error("select must include at least one field");
|
|
337
|
-
}
|
|
338
|
-
return selected;
|
|
339
|
-
}
|
|
340
|
-
compileOrderBy(relation, metadata, orderBy) {
|
|
341
|
-
if (orderBy === undefined) {
|
|
342
|
-
return "";
|
|
343
|
-
}
|
|
344
|
-
const entries = orderBy.flatMap((entry) => Object.entries(entry));
|
|
345
|
-
if (entries.length === 0 || entries.some(([, direction]) => direction !== "asc" && direction !== "desc")) {
|
|
346
|
-
throw new Error("orderBy must be a non-empty list of one-field entries");
|
|
347
|
-
}
|
|
348
|
-
return ` order by ${entries.map(([field, direction]) => `"${this.field(relation, metadata, field)}" ${direction}`).join(", ")}`;
|
|
349
|
-
}
|
|
350
|
-
assertUniqueWhere(relation, metadata, where) {
|
|
351
|
-
const fields = Object.keys(where).sort();
|
|
352
|
-
const matches = metadata.uniqueKeys.some((key) => [...key].sort().join("\0") === fields.join("\0"));
|
|
353
|
-
if (!matches) {
|
|
354
|
-
throw new Error(`where is not a stable unique key of ${relation}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
assertNumericField(relation, metadata, field) {
|
|
358
|
-
if (!metadata.numericFields.includes(field)) {
|
|
359
|
-
throw new Error(`${field} is not a numeric aggregate field of ${relation}`);
|
|
109
|
+
async groupPhysicalAsync(relation, query) {
|
|
110
|
+
const compiled = compilePqsRelationGroupBy(relation, query, this.profile);
|
|
111
|
+
try {
|
|
112
|
+
await this.ready;
|
|
113
|
+
return (await this.executor.query(compiled.text, compiled.values)).rows.map((row) => Object.fromEntries(Object.entries(row).map(([name, value]) => [name, name === "count" ? Number(value) : value instanceof Date ? value : value === null ? null : String(value)])));
|
|
360
114
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const column = metadata.fields[field];
|
|
364
|
-
if (column === undefined) {
|
|
365
|
-
throw new Error(`${field} is not a field of ${relation}`);
|
|
115
|
+
catch (cause) {
|
|
116
|
+
throw this.wrap(`${relation}.groupBy`, cause);
|
|
366
117
|
}
|
|
367
|
-
return column;
|
|
368
118
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
119
|
+
mapPhysicalRow(row, shape, scalarSource = row) {
|
|
120
|
+
const metadata = pqsRelationMetadata[shape.relation];
|
|
121
|
+
const scalar = Object.fromEntries(shape.fields
|
|
122
|
+
.filter(({ name }) => Object.hasOwn(scalarSource, name) || Object.hasOwn(scalarSource, metadata.fields[name] ?? ""))
|
|
123
|
+
.map(({ name }) => [name, mapPhysicalValue(scalarSource[name] ?? scalarSource[metadata.fields[name] ?? ""], shape.relation, metadata, name)]));
|
|
124
|
+
const projected = Object.fromEntries(shape.json.map((projection) => [projection.name, mapJsonValue(row[projection.name], projection.as)]));
|
|
125
|
+
const included = Object.fromEntries(shape.includes.map((include) => [include.edge, this.mapIncludedPhysicalValue(row[include.edge], include)]));
|
|
126
|
+
return { ...scalar, ...projected, ...included };
|
|
127
|
+
}
|
|
128
|
+
mapIncludedPhysicalValue(value, include) {
|
|
129
|
+
if (include.cardinality === "many") {
|
|
130
|
+
if (value === null || value === undefined)
|
|
131
|
+
return [];
|
|
132
|
+
if (!Array.isArray(value))
|
|
133
|
+
throw new Error(`Invalid included ${include.target} rows`);
|
|
134
|
+
return value.map((entry) => this.mapIncludedPhysicalValue(entry, { ...include, cardinality: "one" }));
|
|
374
135
|
}
|
|
375
|
-
}
|
|
376
|
-
mapPhysicalRow(row, metadata, fields, includes, json = {}) {
|
|
377
|
-
const scalar = Object.fromEntries(fields
|
|
378
|
-
.filter(([field]) => Object.hasOwn(row, field))
|
|
379
|
-
.map(([field]) => [field, mapPhysicalValue(row[field], metadata, field)]));
|
|
380
|
-
const related = Object.fromEntries(Object.entries(includes).map(([name, edge]) => [name, this.mapRelatedValue(row[name], edge.target, edge.options)]));
|
|
381
|
-
const projected = Object.fromEntries(Object.entries(json).map(([name, as]) => [name, mapJsonValue(row[name], as)]));
|
|
382
|
-
return { ...scalar, ...projected, ...related };
|
|
383
|
-
}
|
|
384
|
-
mapRelatedValue(value, relation, options) {
|
|
385
136
|
if (value === null || value === undefined)
|
|
386
|
-
return
|
|
387
|
-
if (Array.isArray(value))
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
throw new Error(`Invalid included ${relation} row`);
|
|
391
|
-
if (relation === "__contracts")
|
|
392
|
-
return this.mapLogicalContractValue(value, options);
|
|
393
|
-
const metadata = pqsRelationMetadata[relation];
|
|
394
|
-
const row = value;
|
|
395
|
-
const fields = this.selectedFields(relation, metadata, options.select);
|
|
396
|
-
const nested = Object.fromEntries(Object.entries(options.include ?? {}).map(([name, option]) => {
|
|
397
|
-
const edge = pqsRelationEdges[relation]?.[name];
|
|
398
|
-
if (edge === undefined)
|
|
399
|
-
throw new Error(`${name} is not a relation of ${relation}`);
|
|
400
|
-
return [name, { target: edge.target, options: option === true ? {} : option }];
|
|
401
|
-
}));
|
|
402
|
-
const json = Object.fromEntries(Object.entries(options.select?.json ?? {}).map(([name, projection]) => [name, projection.as]));
|
|
403
|
-
const normalized = Object.fromEntries(fields
|
|
404
|
-
.filter(([field, column]) => Object.hasOwn(row, field) || Object.hasOwn(row, column))
|
|
405
|
-
.map(([field, column]) => [field, row[field] ?? row[column]]));
|
|
406
|
-
for (const name of Object.keys(nested))
|
|
407
|
-
normalized[name] = row[name];
|
|
408
|
-
for (const name of Object.keys(json))
|
|
409
|
-
normalized[name] = row[name];
|
|
410
|
-
return this.mapPhysicalRow(normalized, metadata, fields, nested, json);
|
|
411
|
-
}
|
|
412
|
-
mapLogicalContractValue(row, options) {
|
|
413
|
-
const fields = options.select === undefined
|
|
414
|
-
? logicalContractFields
|
|
415
|
-
: Object.entries(options.select).filter(([field, enabled]) => field !== "json" && enabled === true).map(([field]) => {
|
|
416
|
-
if (!logicalContractFields.includes(field))
|
|
417
|
-
throw new Error(`${field} is not a field of __contracts`);
|
|
418
|
-
return field;
|
|
419
|
-
});
|
|
420
|
-
const scalar = Object.fromEntries(fields.filter((field) => Object.hasOwn(row, field)).map((field) => [field, row[field]]));
|
|
421
|
-
const relations = Object.fromEntries(Object.entries(options.include ?? {}).map(([name, option]) => {
|
|
422
|
-
const edge = pqsRelationEdges.__contracts?.[name];
|
|
423
|
-
if (edge === undefined)
|
|
424
|
-
throw new Error(`${name} is not a relation of __contracts`);
|
|
425
|
-
return [name, this.mapRelatedValue(row[name], edge.target, option === true ? {} : option)];
|
|
426
|
-
}));
|
|
427
|
-
const json = Object.fromEntries(Object.entries(options.select?.json ?? {}).map(([name, projection]) => [name, mapJsonValue(row[name], projection.as)]));
|
|
428
|
-
return { ...scalar, ...json, ...relations };
|
|
429
|
-
}
|
|
430
|
-
compileIncludes(relation, include, add, parentExpression = this.profile.relation(relation)) {
|
|
431
|
-
const selections = [];
|
|
432
|
-
const values = {};
|
|
433
|
-
for (const [name, option] of Object.entries(include ?? {})) {
|
|
434
|
-
const edge = pqsRelationEdges[relation]?.[name];
|
|
435
|
-
if (edge === undefined)
|
|
436
|
-
throw new Error(`${name} is not a relation of ${relation}`);
|
|
437
|
-
const options = option === true ? {} : option;
|
|
438
|
-
if (edge.cardinality === "many" && options.take === undefined)
|
|
439
|
-
throw new Error(`${name} is a to-many relation and requires take`);
|
|
440
|
-
if (options.take !== undefined && (!Number.isInteger(options.take) || options.take < 0))
|
|
441
|
-
throw new Error(`${name}.take must be a non-negative integer`);
|
|
442
|
-
const expression = this.compileIncludedExpression(name, edge.target, edge.sourceColumn, edge.targetColumn, edge.cardinality, options, add, parentExpression);
|
|
443
|
-
selections.push(`${expression} as "${name}"`);
|
|
444
|
-
values[name] = { target: edge.target, options };
|
|
445
|
-
}
|
|
446
|
-
return { selection: selections, values };
|
|
447
|
-
}
|
|
448
|
-
compileJsonSelections(relation, selections, add, tableAlias) {
|
|
449
|
-
const selection = [];
|
|
450
|
-
const fields = {};
|
|
451
|
-
for (const [name, projection] of Object.entries(selections ?? {})) {
|
|
452
|
-
if (!PqsSchemaProfileV1.jsonField(relation, projection.field))
|
|
453
|
-
throw new Error(`${projection.field} is not a JSON field of ${relation}`);
|
|
454
|
-
if (projection.path.length === 0 || projection.path.some((segment) => segment.length === 0))
|
|
455
|
-
throw new Error(`${name}.path must be a non-empty JSON path`);
|
|
456
|
-
const column = this.field(relation, pqsRelationMetadata[relation], projection.field);
|
|
457
|
-
const text = `${tableAlias === undefined ? "" : `${tableAlias}.`}"${column}" #>> ${add(projection.path)}::text[]`;
|
|
458
|
-
const expression = projection.as === "text" ? text : projection.as === "numeric" ? `(${text})::numeric::text` : projection.as === "boolean" ? `(${text})::boolean` : `(${text})::timestamptz`;
|
|
459
|
-
selection.push(`${expression} as "${name}"`);
|
|
460
|
-
fields[name] = projection.as;
|
|
461
|
-
}
|
|
462
|
-
return { selection, fields };
|
|
463
|
-
}
|
|
464
|
-
compileIncludedExpression(name, target, sourceColumn, targetColumn, cardinality, options, add, parentExpression) {
|
|
465
|
-
const alias = `"${name}"`;
|
|
466
|
-
const targetMetadata = pqsRelationMetadata[target];
|
|
467
|
-
const fields = target === "__contracts" ? [] : this.selectedFields(target, targetMetadata, options.select);
|
|
468
|
-
const json = target === "__contracts"
|
|
469
|
-
? this.compileLogicalContractJsonSelections(options.select?.json, add, alias)
|
|
470
|
-
: this.compileJsonSelections(target, options.select?.json, add, alias).selection;
|
|
471
|
-
const nested = this.compileIncludes(target, options.include, add, alias).selection;
|
|
472
|
-
const scalarSelections = target === "__contracts"
|
|
473
|
-
? this.compileLogicalContractSelections(alias, options.select)
|
|
474
|
-
: fields.map(([field, column]) => `'${field}', ${alias}."${column}"`);
|
|
475
|
-
const object = `jsonb_build_object(${[...scalarSelections, ...[...json, ...nested].flatMap((selection) => {
|
|
476
|
-
const match = /^(.*) as "([^"]+)"$/.exec(selection);
|
|
477
|
-
return match === null ? [] : [`'${match[2]}', ${match[1]}`];
|
|
478
|
-
})].join(", ")})`;
|
|
479
|
-
const { where } = this.compileWhere(target, targetMetadata, options.where, add, alias);
|
|
480
|
-
const condition = `${alias}."${targetColumn}" = ${parentExpression}."${sourceColumn}"`;
|
|
481
|
-
const childWhere = where.length === 0 ? ` where ${condition}` : `${where} and ${condition}`;
|
|
482
|
-
if (cardinality === "one")
|
|
483
|
-
return `(select ${object} from ${this.profile.relation(target)} ${alias}${childWhere})`;
|
|
484
|
-
const orderBy = this.compileOrderBy(target, targetMetadata, options.orderBy);
|
|
485
|
-
return `(select coalesce(jsonb_agg("${name}_limited".value), '[]'::jsonb) from (select ${object} as value from ${this.profile.relation(target)} ${alias}${childWhere}${orderBy} limit ${add(options.take)}) "${name}_limited")`;
|
|
486
|
-
}
|
|
487
|
-
compileLogicalContractSelections(alias, select) {
|
|
488
|
-
const selected = select === undefined
|
|
489
|
-
? logicalContractFields
|
|
490
|
-
: Object.entries(select).filter(([field, enabled]) => field !== "json" && enabled === true).map(([field]) => field);
|
|
491
|
-
if (selected.length === 0 && Object.keys(select?.json ?? {}).length === 0)
|
|
492
|
-
throw new Error("select must include at least one field");
|
|
493
|
-
const expressions = {
|
|
494
|
-
contractId: `${alias}."contract_id"`,
|
|
495
|
-
templateId: `jsonb_build_object('packageId', ${alias}."creation_package_id", 'moduleName', (select contract_type."module_name" from ${this.profile.relation("__contract_tpe")} contract_type where contract_type."pk" = ${alias}."tpe_pk"), 'entityName', (select contract_type."entity_name" from ${this.profile.relation("__contract_tpe")} contract_type where contract_type."pk" = ${alias}."tpe_pk"))`,
|
|
496
|
-
packageId: `${alias}."creation_package_id"`,
|
|
497
|
-
payload: `${alias}."payload"`,
|
|
498
|
-
witnesses: `${alias}."witnesses"`,
|
|
499
|
-
createdEventOffset: `${alias}."created_at_ix"::text`,
|
|
500
|
-
createdAt: `(select created_transaction."effective_at" from ${this.profile.relation("__transactions")} created_transaction where created_transaction."ix" = ${alias}."created_at_ix")`,
|
|
501
|
-
archivedEventOffset: `${alias}."archived_at_ix"::text`,
|
|
502
|
-
archivedAt: `(select archived_transaction."effective_at" from ${this.profile.relation("__transactions")} archived_transaction where archived_transaction."ix" = ${alias}."archived_at_ix")`,
|
|
503
|
-
active: `${alias}."archived_at_ix" is null`,
|
|
504
|
-
};
|
|
505
|
-
return selected.map((field) => {
|
|
506
|
-
const expression = expressions[field];
|
|
507
|
-
if (expression === undefined)
|
|
508
|
-
throw new Error(`${field} is not a field of __contracts`);
|
|
509
|
-
return `'${field}', ${expression}`;
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
compileLogicalContractJsonSelections(selections, add, alias) {
|
|
513
|
-
return Object.entries(selections ?? {}).map(([name, projection]) => {
|
|
514
|
-
if (projection.field !== "payload")
|
|
515
|
-
throw new Error(`${projection.field} is not a JSON field of __contracts`);
|
|
516
|
-
if (projection.path.length === 0 || projection.path.some((segment) => segment.length === 0))
|
|
517
|
-
throw new Error(`${name}.path must be a non-empty JSON path`);
|
|
518
|
-
const text = `${alias}."payload" #>> ${add(projection.path)}::text[]`;
|
|
519
|
-
const expression = projection.as === "text" ? text : projection.as === "numeric" ? `(${text})::numeric::text` : projection.as === "boolean" ? `(${text})::boolean` : `(${text})::timestamptz`;
|
|
520
|
-
return `${expression} as "${name}"`;
|
|
521
|
-
});
|
|
137
|
+
return null;
|
|
138
|
+
if (typeof value !== "object" || Array.isArray(value))
|
|
139
|
+
throw new Error(`Invalid included ${include.target} row`);
|
|
140
|
+
return this.mapPhysicalRow(value, include.shape);
|
|
522
141
|
}
|
|
523
|
-
async findContractsAsync(
|
|
524
|
-
const compiled = compileContractFindMany(
|
|
142
|
+
async findContractsAsync(query) {
|
|
143
|
+
const compiled = compileContractFindMany(query, this.profile);
|
|
525
144
|
try {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const select = "select" in args ? args.select : undefined;
|
|
529
|
-
if (select === undefined) {
|
|
530
|
-
return rows;
|
|
531
|
-
}
|
|
532
|
-
const fields = Object.entries(select).filter(([field, enabled]) => field !== "json" && enabled === true).map(([field]) => field);
|
|
533
|
-
const json = Object.keys(select.json ?? {});
|
|
534
|
-
if (fields.length === 0 && json.length === 0) {
|
|
535
|
-
throw new Error("select must include at least one field");
|
|
536
|
-
}
|
|
537
|
-
return rows.map((row) => Object.fromEntries([...fields.map((field) => [field, row[field]]), ...json.map((field) => [field, row[field]])]));
|
|
145
|
+
await this.ready;
|
|
146
|
+
return (await this.executor.query(compiled.text, compiled.values)).rows.map((row) => this.mapPhysicalRow(contractRootRow(row), compiled.resultShape, contractRootScalars(row)));
|
|
538
147
|
}
|
|
539
148
|
catch (cause) {
|
|
540
149
|
throw this.wrap("contracts.findMany", cause);
|
|
541
150
|
}
|
|
542
151
|
}
|
|
543
|
-
async
|
|
544
|
-
|
|
545
|
-
|
|
152
|
+
async countContractsAsync(query) {
|
|
153
|
+
const compiled = compileContractCount(query, this.profile);
|
|
154
|
+
try {
|
|
155
|
+
await this.ready;
|
|
156
|
+
return Number((await this.executor.query(compiled.text, compiled.values)).rows[0]?.count ?? 0);
|
|
546
157
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (args.count) {
|
|
550
|
-
result.count = rows.length;
|
|
158
|
+
catch (cause) {
|
|
159
|
+
throw this.wrap("contracts.count", cause);
|
|
551
160
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
161
|
+
}
|
|
162
|
+
async aggregateContractsAsync(normalized) {
|
|
163
|
+
const compiled = compileContractAggregate(normalized, this.profile);
|
|
164
|
+
try {
|
|
165
|
+
await this.ready;
|
|
166
|
+
const row = (await this.executor.query(compiled.text, compiled.values)).rows[0] ?? {};
|
|
167
|
+
const result = {};
|
|
168
|
+
if (normalized.aggregates.count)
|
|
169
|
+
result.count = Number(row.count ?? 0);
|
|
170
|
+
for (const [operation, fields] of [["min", normalized.aggregates.min], ["max", normalized.aggregates.max], ["sum", normalized.aggregates.sum]]) {
|
|
171
|
+
if (fields.length > 0)
|
|
172
|
+
result[operation] = Object.fromEntries(fields.map((field) => [field, nullableString(row[`${operation}_${field}`])]));
|
|
555
173
|
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
catch (cause) {
|
|
177
|
+
throw this.wrap("contracts.aggregate", cause);
|
|
556
178
|
}
|
|
557
|
-
return result;
|
|
558
179
|
}
|
|
559
|
-
async groupContractsAsync(
|
|
560
|
-
const compiled = compileContractGroupBy(
|
|
180
|
+
async groupContractsAsync(query) {
|
|
181
|
+
const compiled = compileContractGroupBy(query, this.profile);
|
|
561
182
|
try {
|
|
562
183
|
await this.ready;
|
|
563
184
|
return (await this.executor.query(compiled.text, compiled.values)).rows.map((row) => Object.fromEntries(Object.entries(row).map(([name, value]) => [name, name === "count" ? Number(value) : value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date ? value : String(value)])));
|
|
@@ -570,22 +191,39 @@ export class PqsQueryClient {
|
|
|
570
191
|
return new PqsQueryError({ operation, code: getPqsCode(cause), cause });
|
|
571
192
|
}
|
|
572
193
|
}
|
|
573
|
-
function mapPhysicalValue(value, metadata, field) {
|
|
574
|
-
if (value === null || value === undefined)
|
|
194
|
+
function mapPhysicalValue(value, relation, metadata, field) {
|
|
195
|
+
if (value === null || value === undefined)
|
|
575
196
|
return value ?? null;
|
|
576
|
-
|
|
577
|
-
|
|
197
|
+
if (relation === "__events" && field === "eventId" && typeof value === "object" && !Array.isArray(value)) {
|
|
198
|
+
const event = value;
|
|
199
|
+
if (event.offset !== undefined && event.node_id !== undefined)
|
|
200
|
+
return `${String(event.offset)}:${String(event.node_id)}`;
|
|
201
|
+
}
|
|
202
|
+
if (relation === "__events" && field === "type") {
|
|
203
|
+
if (value === "create")
|
|
204
|
+
return "created";
|
|
205
|
+
if (value === "exercise")
|
|
206
|
+
return "exercised";
|
|
207
|
+
}
|
|
208
|
+
if (relation === "__transactions" && field === "workflowId" && value === "")
|
|
209
|
+
return null;
|
|
210
|
+
if (relation === "__transactions" && field === "traceContext" && typeof value === "object" && !Array.isArray(value)) {
|
|
211
|
+
const trace = value;
|
|
212
|
+
const traceparent = trace.traceparent ?? trace.trace_parent;
|
|
213
|
+
const tracestate = trace.tracestate ?? trace.trace_state;
|
|
214
|
+
return Object.fromEntries([
|
|
215
|
+
...(traceparent === undefined || traceparent === null || traceparent === "" ? [] : [["traceparent", String(traceparent)]]),
|
|
216
|
+
...(tracestate === undefined || tracestate === null || tracestate === "" ? [] : [["tracestate", String(tracestate)]]),
|
|
217
|
+
]);
|
|
218
|
+
}
|
|
219
|
+
if (metadata.numericFields.includes(field))
|
|
578
220
|
return String(value);
|
|
579
|
-
|
|
580
|
-
else if (metadata.dateFields.includes(field)) {
|
|
221
|
+
if (metadata.dateFields.includes(field))
|
|
581
222
|
return value instanceof Date ? value : new Date(String(value));
|
|
582
|
-
|
|
583
|
-
else if (metadata.binaryFields.includes(field)) {
|
|
223
|
+
if (metadata.binaryFields.includes(field))
|
|
584
224
|
return value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
585
|
-
|
|
586
|
-
else if (metadata.arrayFields.includes(field)) {
|
|
225
|
+
if (metadata.arrayFields.includes(field))
|
|
587
226
|
return Array.isArray(value) ? value.map(String) : [];
|
|
588
|
-
}
|
|
589
227
|
return value;
|
|
590
228
|
}
|
|
591
229
|
function mapJsonValue(value, as) {
|
|
@@ -597,99 +235,30 @@ function mapJsonValue(value, as) {
|
|
|
597
235
|
return value instanceof Date ? value : new Date(String(value));
|
|
598
236
|
return String(value);
|
|
599
237
|
}
|
|
600
|
-
function
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
return null;
|
|
612
|
-
if (typeof value !== "object" || Array.isArray(value))
|
|
613
|
-
throw new Error(`Invalid included ${relation} row`);
|
|
614
|
-
const row = value;
|
|
615
|
-
const metadata = pqsRelationMetadata[relation];
|
|
616
|
-
return Object.fromEntries(Object.entries(metadata.fields)
|
|
617
|
-
.filter(([field, column]) => Object.hasOwn(row, field) || Object.hasOwn(row, column))
|
|
618
|
-
.map(([field, column]) => [field, mapPhysicalValue(row[field] ?? row[column], metadata, field)]));
|
|
619
|
-
}
|
|
620
|
-
function mapContractTypeJson(value) {
|
|
621
|
-
const row = mapProfileJsonRow(value, "__contract_tpe");
|
|
622
|
-
return row === null ? undefined : { pk: String(row.pk), payloadType: String(row.payloadType), aliases: stringArray(row.aliases), packageName: String(row.packageName), moduleName: String(row.moduleName), entityName: String(row.entityName), templateFqn: String(row.templateFqn) };
|
|
623
|
-
}
|
|
624
|
-
function mapTransactionJson(value) {
|
|
625
|
-
const row = mapProfileJsonRow(value, "__transactions");
|
|
626
|
-
return row === null ? undefined : { ix: String(row.ix), offset: String(row.offset), transactionId: nullableString(row.transactionId), effectiveAt: nullableDate(row.effectiveAt), workflowId: nullableString(row.workflowId), domainId: nullableString(row.domainId), traceContext: row.traceContext, externalTransactionHash: row.externalTransactionHash instanceof Uint8Array ? row.externalTransactionHash : null, paidTrafficCost: nullableString(row.paidTrafficCost) };
|
|
627
|
-
}
|
|
628
|
-
function mapEventJson(value) {
|
|
629
|
-
const row = mapSelectedProfileJsonRow(value, "__events");
|
|
630
|
-
return row ?? undefined;
|
|
631
|
-
}
|
|
632
|
-
function mapSelectedRelationJson(value, relation) {
|
|
633
|
-
const row = mapSelectedProfileJsonRow(value, relation);
|
|
634
|
-
return row ?? undefined;
|
|
635
|
-
}
|
|
636
|
-
function mapLogicalContractJson(value) {
|
|
637
|
-
if (value === null || value === undefined)
|
|
638
|
-
return undefined;
|
|
639
|
-
if (typeof value !== "object" || Array.isArray(value))
|
|
640
|
-
throw new Error("Invalid included __contracts row");
|
|
641
|
-
return value;
|
|
642
|
-
}
|
|
643
|
-
function mapExerciseJson(value) {
|
|
644
|
-
const raw = value;
|
|
645
|
-
const row = mapSelectedProfileJsonRow(value, "__exercises");
|
|
646
|
-
if (row === null)
|
|
647
|
-
throw new Error("Included exercise cannot be null");
|
|
648
|
-
const base = row;
|
|
649
|
-
const knownFields = new Set(Object.entries(pqsRelationMetadata.__exercises.fields).flatMap(([field, column]) => [field, column]));
|
|
650
|
-
const knownRelations = new Set(Object.keys(pqsRelationEdges.__exercises ?? {}));
|
|
651
|
-
const projections = Object.fromEntries(Object.entries(raw).filter(([name]) => !knownFields.has(name) && !knownRelations.has(name)));
|
|
652
|
-
return {
|
|
653
|
-
...base,
|
|
654
|
-
...projections,
|
|
655
|
-
...(raw.exerciseType === undefined ? {} : { exerciseType: mapSelectedRelationJson(raw.exerciseType, "__exercise_tpe") }),
|
|
656
|
-
...(raw.contractType === undefined ? {} : { contractType: mapSelectedRelationJson(raw.contractType, "__contract_tpe") }),
|
|
657
|
-
...(raw.event === undefined ? {} : { event: raw.event === null ? null : mapEventJson(raw.event) ?? null }),
|
|
658
|
-
...(raw.transaction === undefined ? {} : { transaction: raw.transaction === null ? null : mapTransactionJson(raw.transaction) ?? null }),
|
|
659
|
-
...(raw.package === undefined ? {} : { package: mapSelectedRelationJson(raw.package, "__packages") }),
|
|
660
|
-
...(raw.contract === undefined ? {} : { contract: mapLogicalContractJson(raw.contract) }),
|
|
238
|
+
function contractRootScalars(row) {
|
|
239
|
+
const scalars = {
|
|
240
|
+
contractId: row.contract_id,
|
|
241
|
+
packageId: row.package_id,
|
|
242
|
+
payload: row.payload,
|
|
243
|
+
witnesses: row.witnesses,
|
|
244
|
+
createdEventOffset: row.created_event_offset,
|
|
245
|
+
createdAt: row.created_at,
|
|
246
|
+
archivedEventOffset: row.archived_event_offset,
|
|
247
|
+
archivedAt: row.archived_at,
|
|
248
|
+
active: row.active,
|
|
661
249
|
};
|
|
250
|
+
if (row.template_package_id !== undefined && row.template_module_name !== undefined && row.template_entity_name !== undefined) {
|
|
251
|
+
scalars.templateId = { packageId: row.template_package_id, moduleName: row.template_module_name, entityName: row.template_entity_name };
|
|
252
|
+
}
|
|
253
|
+
return scalars;
|
|
662
254
|
}
|
|
663
|
-
function
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
...(include?.exercises === undefined ? {} : { exercises: Array.isArray(row.exercises) ? row.exercises.map(mapExerciseJson) : [] }),
|
|
255
|
+
function contractRootRow(row) {
|
|
256
|
+
return {
|
|
257
|
+
...row,
|
|
258
|
+
contractType: row.contractType ?? row.contract_type,
|
|
259
|
+
createdTransaction: row.createdTransaction ?? row.created_transaction,
|
|
260
|
+
archivedTransaction: row.archivedTransaction ?? row.archived_transaction,
|
|
670
261
|
};
|
|
671
|
-
const projections = Object.fromEntries(Object.entries(json ?? {}).map(([name, projection]) => [name, mapJsonValue(row[name], projection.as)]));
|
|
672
|
-
return { ...base, ...relations, ...projections };
|
|
673
|
-
}
|
|
674
|
-
function nullableString(value) {
|
|
675
|
-
return value === null || value === undefined ? null : String(value);
|
|
676
|
-
}
|
|
677
|
-
function nullableDate(value) {
|
|
678
|
-
return value === null || value === undefined ? null : value instanceof Date ? value : new Date(String(value));
|
|
679
|
-
}
|
|
680
|
-
function stringArray(value) {
|
|
681
|
-
return Array.isArray(value) ? value.map(String) : [];
|
|
682
|
-
}
|
|
683
|
-
function getPqsCode(cause) {
|
|
684
|
-
return typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string" ? cause.code : undefined;
|
|
685
|
-
}
|
|
686
|
-
function aggregateNumeric(values, operation) {
|
|
687
|
-
const numbers = values.filter((value) => value !== null).map(BigInt);
|
|
688
|
-
if (numbers.length === 0) {
|
|
689
|
-
return null;
|
|
690
|
-
}
|
|
691
|
-
else if (operation === "sum") {
|
|
692
|
-
return numbers.reduce((total, value) => total + value, 0n).toString();
|
|
693
|
-
}
|
|
694
|
-
return numbers.reduce((result, value) => operation === "min" ? value < result ? value : result : value > result ? value : result).toString();
|
|
695
262
|
}
|
|
263
|
+
function nullableString(value) { return value === null || value === undefined ? null : String(value); }
|
|
264
|
+
function getPqsCode(cause) { return typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string" ? cause.code : undefined; }
|