@distrohelena/canton-typescript-sdk 0.1.36 → 0.1.38
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 +143 -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 +657 -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 +648 -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/model-types.d.ts +2 -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 +16 -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/node/start-local-splice-0.6.12.sh +6 -0
- package/node/start-local-splice-0.6.13.sh +6 -0
- package/node/start-local-splice-0.6.14.sh +6 -0
- package/node/start-local-splice-0.7.0.sh +6 -0
- package/node/stop-local-splice-0.6.12.sh +6 -0
- package/node/stop-local-splice-0.6.13.sh +6 -0
- package/node/stop-local-splice-0.6.14.sh +6 -0
- package/node/stop-local-splice-0.7.0.sh +6 -0
- package/node/test-start-local-splice-0.6.12.sh +28 -0
- package/node/test-start-local-splice-0.6.13.sh +28 -0
- package/node/test-start-local-splice-0.6.14.sh +28 -0
- package/node/test-start-local-splice-0.7.0.sh +28 -0
- package/node/test-stop-local-splice-0.6.12.sh +28 -0
- package/node/test-stop-local-splice-0.6.13.sh +28 -0
- package/node/test-stop-local-splice-0.6.14.sh +28 -0
- package/node/test-stop-local-splice-0.7.0.sh +28 -0
- package/package.json +29 -4
- 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
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InMemoryQueryEvaluator = void 0;
|
|
4
|
+
const query_dataset_js_1 = require("./query-dataset.js");
|
|
5
|
+
const query_schema_js_1 = require("./query-schema.js");
|
|
6
|
+
/** Reference evaluator for normalized canonical plans; it intentionally has no transport concerns. */
|
|
7
|
+
class InMemoryQueryEvaluator {
|
|
8
|
+
execute(dataset, query) {
|
|
9
|
+
const compiled = (0, query_dataset_js_1.createQueryDataset)(dataset);
|
|
10
|
+
switch (query.kind) {
|
|
11
|
+
case "findMany": return freeze(this.findMany(compiled, query));
|
|
12
|
+
case "findUnique": return freeze(this.findMany(compiled, { kind: "findMany", relation: query.relation, predicate: query.predicate, select: query.select, includes: query.includes, orderBy: [], skip: 0, take: 1, activeOnly: false })[0]);
|
|
13
|
+
case "count": return this.filtered(compiled, query.relation, query.predicate, query.parties, query.activeOnly).length;
|
|
14
|
+
case "aggregate": return freeze(this.aggregate(this.filtered(compiled, query.relation, query.predicate), query));
|
|
15
|
+
case "groupBy": return freeze(this.groupBy(compiled, query));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
findMany(dataset, query) {
|
|
19
|
+
const rows = this.order(this.filtered(dataset, query.relation, query.predicate, query.parties, query.activeOnly), query.orderBy)
|
|
20
|
+
.slice(query.skip, query.take === undefined ? undefined : query.skip + query.take);
|
|
21
|
+
return rows.map((row) => this.shape(dataset, query.relation, row, query.select, query.includes));
|
|
22
|
+
}
|
|
23
|
+
filtered(dataset, relation, predicate, parties, activeOnly = false) {
|
|
24
|
+
return dataset.rows[relation].filter((row) => (!activeOnly || row.active === true)
|
|
25
|
+
&& (parties === undefined || (Array.isArray(row.witnesses) && row.witnesses.some((party) => parties.includes(String(party)))))
|
|
26
|
+
&& (predicate === undefined || this.matches(dataset, relation, row, predicate)));
|
|
27
|
+
}
|
|
28
|
+
matches(dataset, relation, row, predicate) {
|
|
29
|
+
switch (predicate.kind) {
|
|
30
|
+
case "and": return predicate.children.every((child) => this.matches(dataset, relation, row, child));
|
|
31
|
+
case "or": return predicate.children.some((child) => this.matches(dataset, relation, row, child));
|
|
32
|
+
case "not": return !this.matches(dataset, relation, row, predicate.child);
|
|
33
|
+
case "relation": {
|
|
34
|
+
const edge = query_schema_js_1.queryRelationEdges[relation]?.[predicate.edge];
|
|
35
|
+
if (edge === undefined) {
|
|
36
|
+
throw new Error(`Unknown ${relation}.${predicate.edge} edge`);
|
|
37
|
+
}
|
|
38
|
+
const related = this.related(dataset, relation, row, predicate.edge);
|
|
39
|
+
const result = related.map((target) => this.matches(dataset, edge.target, target, predicate.predicate));
|
|
40
|
+
return predicate.quantifier === "one" ? result[0] === true : predicate.quantifier === "some" ? result.some(Boolean) : predicate.quantifier === "none" ? !result.some(Boolean) : result.every(Boolean);
|
|
41
|
+
}
|
|
42
|
+
case "scalar": {
|
|
43
|
+
const actual = at(row, predicate.path);
|
|
44
|
+
const expected = predicate.value;
|
|
45
|
+
switch (predicate.operator) {
|
|
46
|
+
case "equals": return actual !== null && actual !== undefined && expected !== null && expected !== undefined && equal(actual, expected);
|
|
47
|
+
case "in": return actual !== null && actual !== undefined && Array.isArray(expected) && expected.some((candidate) => candidate !== null && candidate !== undefined && equal(actual, candidate));
|
|
48
|
+
case "is": return actual === null || actual === undefined;
|
|
49
|
+
case "isNot": return actual !== null && actual !== undefined;
|
|
50
|
+
case "lt": return compare(actual, expected) < 0;
|
|
51
|
+
case "lte": return compare(actual, expected) <= 0;
|
|
52
|
+
case "gt": return compare(actual, expected) > 0;
|
|
53
|
+
case "gte": return compare(actual, expected) >= 0;
|
|
54
|
+
case "like": return typeof actual === "string" && typeof expected === "string" && sqlLike(actual, expected, false);
|
|
55
|
+
case "ilike": return typeof actual === "string" && typeof expected === "string" && sqlLike(actual, expected, true);
|
|
56
|
+
case "has": return Array.isArray(actual) && actual.some((entry) => equal(entry, expected));
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
related(dataset, relation, row, edge) {
|
|
63
|
+
return [...(0, query_dataset_js_1.relatedQueryRows)(dataset, relation, row, edge)];
|
|
64
|
+
}
|
|
65
|
+
shape(dataset, relation, row, selection, includes) {
|
|
66
|
+
const fields = selection?.fields ?? Object.keys(row);
|
|
67
|
+
const result = Object.fromEntries(fields.filter((field) => Object.hasOwn(row, field)).map((field) => [field, clone(row[field])]));
|
|
68
|
+
for (const projection of selection?.json ?? []) {
|
|
69
|
+
result[projection.name] = project(at(row, [projection.field, ...projection.path]), projection.as);
|
|
70
|
+
}
|
|
71
|
+
for (const include of includes) {
|
|
72
|
+
const targets = this.related(dataset, relation, row, include.edge).filter((target) => include.predicate === undefined || this.matches(dataset, include.relation, target, include.predicate));
|
|
73
|
+
const bounded = this.order(targets, include.orderBy).slice(include.skip, include.take === undefined ? undefined : include.skip + include.take)
|
|
74
|
+
.map((target) => this.shape(dataset, include.relation, target, include.select, include.includes));
|
|
75
|
+
result[include.edge] = include.cardinality === "many" ? bounded : (bounded[0] ?? null);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
order(rows, orderBy) {
|
|
80
|
+
return rows.map((row, index) => ({ row, index })).sort((left, right) => {
|
|
81
|
+
for (const order of orderBy) {
|
|
82
|
+
const compared = postgresCompare(at(left.row, order.path), at(right.row, order.path), order.direction);
|
|
83
|
+
if (compared !== 0) {
|
|
84
|
+
return compared;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return left.index - right.index;
|
|
88
|
+
}).map(({ row }) => row);
|
|
89
|
+
}
|
|
90
|
+
aggregate(rows, query) {
|
|
91
|
+
const metadata = query_schema_js_1.queryRelationMetadata[query.relation];
|
|
92
|
+
const result = {};
|
|
93
|
+
if (query.aggregates.count) {
|
|
94
|
+
result.count = rows.length;
|
|
95
|
+
}
|
|
96
|
+
for (const operation of ["min", "max", "sum"]) {
|
|
97
|
+
if (query.aggregates[operation].length > 0) {
|
|
98
|
+
for (const field of query.aggregates[operation]) {
|
|
99
|
+
if (!metadata.numericFields.includes(field)) {
|
|
100
|
+
throw new Error(`${field} is not a numeric aggregate field of ${query.relation}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
result[operation] = Object.fromEntries(query.aggregates[operation].map((field) => [field, aggregate(rows.map((row) => at(row, [field])), operation)]));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
groupBy(dataset, query) {
|
|
109
|
+
const groups = new Map();
|
|
110
|
+
for (const row of this.filtered(dataset, query.relation, query.predicate)) {
|
|
111
|
+
const keyVariants = query.by.map((key) => this.groupValues(dataset, query.relation, row, key));
|
|
112
|
+
for (const keys of cartesian(keyVariants)) {
|
|
113
|
+
const identity = JSON.stringify(keys.map(keyIdentity));
|
|
114
|
+
const group = groups.get(identity) ?? { keys, rows: [] };
|
|
115
|
+
group.rows.push(row);
|
|
116
|
+
groups.set(identity, group);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return [...groups.values()].map(({ keys, rows }) => {
|
|
120
|
+
const result = Object.fromEntries(query.by.map((key, index) => [groupName(key), clone(keys[index])]));
|
|
121
|
+
if (query.aggregates.count) {
|
|
122
|
+
result.count = rows.length;
|
|
123
|
+
}
|
|
124
|
+
for (const operation of ["min", "max", "sum"]) {
|
|
125
|
+
for (const field of query.aggregates[operation]) {
|
|
126
|
+
result[`${operation}_${field}`] = aggregate(rows.map((row) => at(row, [field])), operation);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
groupValues(dataset, relation, row, key) {
|
|
133
|
+
if (key.kind === "field") {
|
|
134
|
+
const value = at(row, key.path);
|
|
135
|
+
return Array.isArray(value) ? value : [value];
|
|
136
|
+
}
|
|
137
|
+
else if (key.kind === "json") {
|
|
138
|
+
return [project(at(row, [key.field, ...key.path]), key.as)];
|
|
139
|
+
}
|
|
140
|
+
else if (key.kind !== "bucket") {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
const value = key.path.length === 1 ? at(row, key.path) : this.related(dataset, relation, row, key.path[0])[0] && at(this.related(dataset, relation, row, key.path[0])[0], [key.path[1]]);
|
|
144
|
+
return [bucket(value, key.bucket)];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.InMemoryQueryEvaluator = InMemoryQueryEvaluator;
|
|
148
|
+
function at(value, path) {
|
|
149
|
+
let current = value;
|
|
150
|
+
for (const key of path) {
|
|
151
|
+
if (current === null || current === undefined || typeof current !== "object") {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
current = current[key];
|
|
155
|
+
}
|
|
156
|
+
return current;
|
|
157
|
+
}
|
|
158
|
+
function clone(value) {
|
|
159
|
+
return (0, query_dataset_js_1.immutableQueryValue)(value);
|
|
160
|
+
}
|
|
161
|
+
function freeze(value) {
|
|
162
|
+
if (value === null || typeof value !== "object" || value instanceof Date || value instanceof Uint8Array) {
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
else if (Object.isFrozen(value)) {
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
for (const child of Object.values(value)) {
|
|
169
|
+
freeze(child);
|
|
170
|
+
}
|
|
171
|
+
return Object.freeze(value);
|
|
172
|
+
}
|
|
173
|
+
function equal(left, right) {
|
|
174
|
+
if (left instanceof Date) {
|
|
175
|
+
return left.toISOString() === (right instanceof Date ? right.toISOString() : String(right));
|
|
176
|
+
}
|
|
177
|
+
else if (right instanceof Date) {
|
|
178
|
+
return String(left) === right.toISOString();
|
|
179
|
+
}
|
|
180
|
+
else if (left instanceof Uint8Array || right instanceof Uint8Array) {
|
|
181
|
+
return JSON.stringify(Array.from(left)) === JSON.stringify(Array.from(right));
|
|
182
|
+
}
|
|
183
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
184
|
+
}
|
|
185
|
+
function compare(left, right) {
|
|
186
|
+
if (left === null || left === undefined || right === null || right === undefined) {
|
|
187
|
+
return Number.NaN;
|
|
188
|
+
}
|
|
189
|
+
else if (left instanceof Date || right instanceof Date) {
|
|
190
|
+
const leftTime = left instanceof Date ? left.getTime() : new Date(String(left)).getTime();
|
|
191
|
+
const rightTime = right instanceof Date ? right.getTime() : new Date(String(right)).getTime();
|
|
192
|
+
return !Number.isFinite(leftTime) || !Number.isFinite(rightTime) ? Number.NaN : leftTime < rightTime ? -1 : leftTime > rightTime ? 1 : 0;
|
|
193
|
+
}
|
|
194
|
+
const leftValue = numeric(left) ?? String(left);
|
|
195
|
+
const rightValue = numeric(right) ?? String(right);
|
|
196
|
+
return leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0;
|
|
197
|
+
}
|
|
198
|
+
function numeric(value) {
|
|
199
|
+
return typeof value === "string" && /^-?\d+$/.test(value) ? BigInt(value) : typeof value === "number" && Number.isFinite(value) ? BigInt(value) : undefined;
|
|
200
|
+
}
|
|
201
|
+
function postgresCompare(left, right, direction) {
|
|
202
|
+
const leftNull = left === null || left === undefined;
|
|
203
|
+
const rightNull = right === null || right === undefined;
|
|
204
|
+
if (leftNull || rightNull) {
|
|
205
|
+
if (leftNull && rightNull) {
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
return leftNull ? (direction === "asc" ? 1 : -1) : (direction === "asc" ? -1 : 1);
|
|
209
|
+
}
|
|
210
|
+
const result = compare(left, right);
|
|
211
|
+
return direction === "asc" ? result : -result;
|
|
212
|
+
}
|
|
213
|
+
function sqlLike(value, pattern, insensitive) {
|
|
214
|
+
let expression = "^";
|
|
215
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
216
|
+
const character = pattern[index];
|
|
217
|
+
if (character === "\\" && index + 1 < pattern.length) {
|
|
218
|
+
expression += escapeRegex(pattern[++index]);
|
|
219
|
+
}
|
|
220
|
+
else if (character === "%") {
|
|
221
|
+
expression += ".*";
|
|
222
|
+
}
|
|
223
|
+
else if (character === "_") {
|
|
224
|
+
expression += ".";
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
expression += escapeRegex(character);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return new RegExp(`${expression}$`, insensitive ? "isu" : "su").test(value);
|
|
231
|
+
}
|
|
232
|
+
function escapeRegex(value) {
|
|
233
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
234
|
+
}
|
|
235
|
+
function project(value, as) {
|
|
236
|
+
if (value === null || value === undefined) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
else if (as === "boolean") {
|
|
240
|
+
return value === true || value === "true";
|
|
241
|
+
}
|
|
242
|
+
else if (as === "timestamp") {
|
|
243
|
+
return (0, query_dataset_js_1.immutableQueryValue)(value instanceof Date ? value : new Date(String(value)));
|
|
244
|
+
}
|
|
245
|
+
return String(value);
|
|
246
|
+
}
|
|
247
|
+
function aggregate(values, operation) {
|
|
248
|
+
const present = values.filter((value) => value !== null && value !== undefined);
|
|
249
|
+
if (present.length === 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const numbers = present.map((value) => BigInt(String(value)));
|
|
253
|
+
if (operation === "sum") {
|
|
254
|
+
return numbers.reduce((total, value) => total + value, 0n).toString();
|
|
255
|
+
}
|
|
256
|
+
return numbers.reduce((winner, value) => operation === "min" ? value < winner ? value : winner : value > winner ? value : winner).toString();
|
|
257
|
+
}
|
|
258
|
+
function cartesian(parts) {
|
|
259
|
+
return parts.reduce((all, part) => all.flatMap((prefix) => part.map((value) => [...prefix, value])), [[]]);
|
|
260
|
+
}
|
|
261
|
+
function keyIdentity(value) {
|
|
262
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
263
|
+
}
|
|
264
|
+
function groupName(key) {
|
|
265
|
+
return key.kind === "field" ? key.path.join("_") : key.kind === "json" ? key.name : key.path.join("_") + "_" + key.bucket;
|
|
266
|
+
}
|
|
267
|
+
function bucket(value, unit) {
|
|
268
|
+
if (value === null || value === undefined) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const date = value instanceof Date ? new Date(value.getTime()) : new Date(String(value));
|
|
272
|
+
if (Number.isNaN(date.getTime())) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
else if (unit === "hour") {
|
|
276
|
+
date.setUTCMinutes(0, 0, 0);
|
|
277
|
+
}
|
|
278
|
+
else if (unit === "day") {
|
|
279
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
280
|
+
}
|
|
281
|
+
else if (unit === "month") {
|
|
282
|
+
date.setUTCDate(1);
|
|
283
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
const day = date.getUTCDay() || 7;
|
|
287
|
+
date.setUTCDate(date.getUTCDate() - day + 1);
|
|
288
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
289
|
+
}
|
|
290
|
+
return date;
|
|
291
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canonicalPublicNumericIdentity = canonicalPublicNumericIdentity;
|
|
4
|
+
exports.canonicalPublicNumericIdentityParts = canonicalPublicNumericIdentityParts;
|
|
5
|
+
exports.compileCanonicalPublicNumericIdentitySql = compileCanonicalPublicNumericIdentitySql;
|
|
6
|
+
exports.compileCanonicalPublicNumericIdentityPartsSql = compileCanonicalPublicNumericIdentityPartsSql;
|
|
7
|
+
const node_buffer_1 = require("node:buffer");
|
|
8
|
+
const MAX_CANONICAL_PUBLIC_IDENTITY_BYTES = 43_690;
|
|
9
|
+
/**
|
|
10
|
+
* Encodes an arbitrary UTF-8 semantic identity as a positive decimal integer.
|
|
11
|
+
*
|
|
12
|
+
* The leading decimal digit is a sentinel and each byte occupies three decimal
|
|
13
|
+
* digits, so leading NUL bytes and the empty string remain distinct. This is an
|
|
14
|
+
* injective encoding, not a hash.
|
|
15
|
+
*/
|
|
16
|
+
function canonicalPublicNumericIdentity(identity) {
|
|
17
|
+
const bytes = node_buffer_1.Buffer.from(identity, "utf8");
|
|
18
|
+
if (bytes.length > MAX_CANONICAL_PUBLIC_IDENTITY_BYTES) {
|
|
19
|
+
throw new RangeError(`Canonical public identity exceeds ${MAX_CANONICAL_PUBLIC_IDENTITY_BYTES} UTF-8 bytes`);
|
|
20
|
+
}
|
|
21
|
+
let value = "1";
|
|
22
|
+
for (const byte of bytes) {
|
|
23
|
+
value += byte.toString().padStart(3, "0");
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
/** Frames multiple semantic components without a delimiter ambiguity. */
|
|
28
|
+
function canonicalPublicNumericIdentityParts(parts) {
|
|
29
|
+
return canonicalPublicNumericIdentity(parts.map((part) => `${node_buffer_1.Buffer.byteLength(part, "utf8")}:${part}`).join(""));
|
|
30
|
+
}
|
|
31
|
+
/** PostgreSQL expression exactly equivalent to {@link canonicalPublicNumericIdentity}. */
|
|
32
|
+
function compileCanonicalPublicNumericIdentitySql(text) {
|
|
33
|
+
const bytes = `convert_to((${text})::text, 'UTF8')`;
|
|
34
|
+
const encoded = `('1' || coalesce((select string_agg(lpad(get_byte(canonical_identity_bytes, canonical_identity_byte)::text, 3, '0'), '' order by canonical_identity_byte) from generate_series(0, octet_length(canonical_identity_bytes) - 1) as canonical_identity_byte), ''))::numeric`;
|
|
35
|
+
const overflow = `('1' || repeat('0', octet_length(canonical_identity_bytes) * 3))::numeric`;
|
|
36
|
+
return `(select case when octet_length(canonical_identity_bytes) <= ${MAX_CANONICAL_PUBLIC_IDENTITY_BYTES} then ${encoded} else ${overflow} end from (select ${bytes} as canonical_identity_bytes) canonical_identity)`;
|
|
37
|
+
}
|
|
38
|
+
/** PostgreSQL framing equivalent to {@link canonicalPublicNumericIdentityParts}. */
|
|
39
|
+
function compileCanonicalPublicNumericIdentityPartsSql(parts) {
|
|
40
|
+
return `concat(${parts.flatMap((part) => [`octet_length(convert_to((${part})::text, 'UTF8'))`, "':'", `(${part})::text`]).join(", ")})`;
|
|
41
|
+
}
|