@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
@@ -1,141 +1,172 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.compilePqsRelationFindMany = compilePqsRelationFindMany;
4
+ exports.compilePqsRelationCount = compilePqsRelationCount;
5
+ exports.compilePqsRelationAggregate = compilePqsRelationAggregate;
4
6
  exports.compilePqsRelationGroupBy = compilePqsRelationGroupBy;
5
7
  const pqs_schema_profile_js_1 = require("./pqs-schema-profile.js");
6
8
  const pqs_schema_profile_js_2 = require("./pqs-schema-profile.js");
7
- function compilePqsRelationFindMany(relation, args, profile) {
8
- assertPage(args);
9
+ const pqs_sql_compiler_js_1 = require("./pqs-sql-compiler.js");
10
+ const pqs_sql_syntax_js_1 = require("./pqs-sql-syntax.js");
11
+ const pqs_result_shape_js_1 = require("./pqs-result-shape.js");
12
+ function compilePqsRelationFindMany(relation, query, profile) {
13
+ assertCanonicalPhysicalFindMany(relation, query);
9
14
  const metadata = pqs_schema_profile_js_1.pqsRelationMetadata[relation];
15
+ const source = logicalTypeRootSource(relation, profile);
10
16
  const values = [];
11
17
  const add = (value) => {
12
18
  values.push(value);
13
19
  return `$${values.length}`;
14
20
  };
15
- const fields = selectedFields(relation, metadata, args.select);
16
- const where = compileWhere(relation, metadata, args.where, add);
17
- const orderBy = compileOrderBy(relation, metadata, args.orderBy);
18
- const limit = args.take === undefined ? "" : ` limit ${add(args.take)}`;
19
- const offset = args.skip === undefined ? "" : ` offset ${add(args.skip)}`;
21
+ const fields = selectedCanonicalFields(relation, metadata, query.select?.fields, (query.select?.json.length ?? 0) > 0);
22
+ const predicate = query.predicate === undefined ? "" : (0, pqs_sql_compiler_js_1.compileCanonicalPhysicalPredicate)(relation, query.predicate, "", profile, add);
23
+ const where = predicate.length === 0 ? "" : ` where ${predicate}`;
24
+ const orderBy = compileCanonicalOrderBy(relation, metadata, query.orderBy, profile);
25
+ const limit = query.take === undefined ? "" : ` limit ${add(query.take)}`;
26
+ const offset = query.skip === 0 ? "" : ` offset ${add(query.skip)}`;
27
+ const included = (0, pqs_sql_compiler_js_1.compileCanonicalPhysicalIncludes)(relation, source.reference, query.includes, profile, add);
28
+ const json = compileCanonicalJsonSelections(relation, query.select, profile, add);
29
+ const shape = (0, pqs_result_shape_js_1.compilePqsResultShape)(relation, "many", query.select, query.includes);
20
30
  return {
21
- text: `select ${fields.map(([field, column]) => `"${column}" as "${field}"`).join(", ")} from ${profile.relation(relation)}${where}${orderBy}${limit}${offset}`,
31
+ text: `${source.withClause}select ${[...fields.map(([field]) => `${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, field, "", profile)} as "${field}"`), ...json, ...included.map((include) => include.selection)].join(", ")} from ${source.relation}${where}${orderBy}${limit}${offset}`,
22
32
  values,
33
+ resultShape: shape,
23
34
  };
24
35
  }
25
- function compilePqsRelationGroupBy(relation, args, profile, parameterOffset = 0) {
26
- if (args.by.length === 0 || (!args.aggregate.count && !args.aggregate.min && !args.aggregate.max && !args.aggregate.sum))
36
+ function compilePqsRelationCount(relation, query, profile) {
37
+ assertCanonicalPhysicalQuery(relation, query, "count");
38
+ const source = logicalTypeRootSource(relation, profile);
39
+ const values = [];
40
+ const add = (value) => {
41
+ values.push(value);
42
+ return `$${values.length}`;
43
+ };
44
+ const predicate = query.predicate === undefined ? "" : ` where ${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalPredicate)(relation, query.predicate, "", profile, add)}`;
45
+ return { text: `${source.withClause}select count(*)::text as count from ${source.relation}${predicate}`, values };
46
+ }
47
+ function compilePqsRelationAggregate(relation, query, profile) {
48
+ assertCanonicalPhysicalQuery(relation, query, "aggregate");
49
+ const metadata = pqs_schema_profile_js_1.pqsRelationMetadata[relation];
50
+ const source = logicalTypeRootSource(relation, profile);
51
+ const values = [];
52
+ const add = (value) => {
53
+ values.push(value);
54
+ return `$${values.length}`;
55
+ };
56
+ const selected = [];
57
+ if (query.aggregates.count)
58
+ selected.push("count(*)::text as count");
59
+ for (const [operation, fields] of [["min", query.aggregates.min], ["max", query.aggregates.max], ["sum", query.aggregates.sum]]) {
60
+ for (const name of fields) {
61
+ if (!metadata.numericFields.includes(name))
62
+ throw new Error(`${name} is not a numeric aggregate field of ${relation}`);
63
+ selected.push(`${operation}(${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, name, "", profile)})::text as "${operation}_${name}"`);
64
+ }
65
+ }
66
+ if (selected.length === 0)
67
+ throw new Error("aggregate must request at least one result");
68
+ const predicate = query.predicate === undefined ? "" : ` where ${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalPredicate)(relation, query.predicate, "", profile, add)}`;
69
+ return { text: `${source.withClause}select ${selected.join(", ")} from ${source.relation}${predicate}`, values };
70
+ }
71
+ function compilePqsRelationGroupBy(relation, query, profile) {
72
+ assertCanonicalPhysicalQuery(relation, query, "groupBy");
73
+ if (query.by.length === 0 || (!query.aggregates.count && query.aggregates.min.length === 0 && query.aggregates.max.length === 0 && query.aggregates.sum.length === 0))
27
74
  throw new Error("groupBy requires a key and aggregate");
28
75
  const metadata = pqs_schema_profile_js_1.pqsRelationMetadata[relation];
76
+ const source = logicalTypeRootSource(relation, profile);
29
77
  const root = relation === "__events" ? "event" : "root";
30
78
  const joins = [];
31
79
  const expressions = [];
32
80
  const selected = [];
33
81
  const values = [];
34
- const add = (value) => { values.push(value); return `$${parameterOffset + values.length}`; };
35
- for (const key of args.by) {
36
- if (typeof key === "string") {
37
- const column = field(relation, metadata, key);
38
- const expression = metadata.arrayFields.includes(key)
39
- ? `"${key}".value`
40
- : `"${root}"."${column}"`;
41
- if (metadata.arrayFields.includes(key))
42
- joins.push(`cross join lateral unnest("${root}"."${column}") as "${key}"(value)`);
82
+ const add = (value) => { values.push(value); return `$${values.length}`; };
83
+ const predicate = query.predicate === undefined ? "" : ` where ${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalPredicate)(relation, query.predicate, `"${root}"`, profile, add)}`;
84
+ for (const key of query.by) {
85
+ if (key.kind === "field") {
86
+ const fieldName = key.path[0];
87
+ const column = field(relation, metadata, fieldName);
88
+ const expression = metadata.arrayFields.includes(fieldName)
89
+ ? `"${fieldName}".value`
90
+ : (0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, fieldName, `"${root}"`, profile);
91
+ if (metadata.arrayFields.includes(fieldName))
92
+ joins.push(`cross join lateral unnest("${root}"."${column}") as "${fieldName}"(value)`);
43
93
  expressions.push(expression);
44
- selected.push(`${expression} as "${key}"`);
94
+ selected.push(`${expression} as "${fieldName}"`);
45
95
  continue;
46
96
  }
47
- if (key === null || typeof key !== "object")
48
- throw new Error("invalid group key");
49
- const [edgeName, nested] = Object.entries(key)[0] ?? [];
50
- if (nested !== null && typeof nested === "object" && "name" in nested && "path" in nested && "as" in nested) {
51
- const projection = nested;
52
- if (typeof projection.name !== "string" || !Array.isArray(projection.path) || !projection.path.every((segment) => typeof segment === "string" && segment.length > 0) || !["text", "numeric", "boolean", "timestamp"].includes(String(projection.as)) || !pqs_schema_profile_js_1.PqsSchemaProfileV1.jsonField(relation, edgeName))
53
- throw new Error("invalid profiled JSON group key");
54
- const column = field(relation, metadata, edgeName);
55
- const text = `"${root}"."${column}" #>> ${add(projection.path)}::text[]`;
56
- const expression = projection.as === "text" ? text : projection.as === "numeric" ? `(${text})::numeric` : projection.as === "boolean" ? `(${text})::boolean` : `(${text})::timestamptz`;
97
+ if (key.kind === "json") {
98
+ field(relation, metadata, key.field);
99
+ const text = `${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, key.field, `"${root}"`, profile)} #>> ${add(key.path)}::text[]`;
100
+ const expression = key.as === "text" ? text : key.as === "numeric" ? `(${text})::numeric` : key.as === "boolean" ? `(${text})::boolean` : `(${text})::timestamptz`;
57
101
  expressions.push(expression);
58
- selected.push(`${expression} as "${projection.name}"`);
102
+ selected.push(`${expression} as ${(0, pqs_sql_syntax_js_1.quotePqsIdentifier)(key.name)}`);
59
103
  continue;
60
104
  }
61
- if (nested !== null && typeof nested === "object" && "bucket" in nested && pqs_schema_profile_js_1.PqsSchemaProfileV1.bucketField(relation, edgeName)) {
62
- const bucket = nested.bucket;
63
- if (typeof bucket !== "string" || !["hour", "day", "week", "month"].includes(bucket))
64
- throw new Error("invalid profiled time bucket");
65
- const column = field(relation, metadata, edgeName);
66
- const expression = `date_trunc('${bucket}', "${root}"."${column}")`;
105
+ if (key.kind === "bucket" && key.path.length === 1) {
106
+ field(relation, metadata, key.path[0]);
107
+ const expression = `date_trunc('${key.bucket}', ${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, key.path[0], `"${root}"`, profile)})`;
67
108
  expressions.push(expression);
68
- selected.push(`${expression} as "${edgeName}_${bucket}"`);
109
+ selected.push(`${expression} as "${key.path[0]}_${key.bucket}"`);
69
110
  continue;
70
111
  }
112
+ const edgeName = key.path[0];
113
+ const fieldName = key.path[1];
71
114
  const edge = pqs_schema_profile_js_2.pqsRelationEdges[relation]?.[edgeName];
72
- if (edge === undefined || edge.cardinality !== "one" || nested === null || typeof nested !== "object")
115
+ if (edge === undefined || edge.cardinality !== "one")
73
116
  throw new Error("group key must follow a profiled to-one edge");
74
- const [fieldName, bucketValue] = Object.entries(nested)[0] ?? [];
75
- const bucket = bucketValue !== null && typeof bucketValue === "object" ? bucketValue.bucket : undefined;
76
- if (typeof bucket !== "string" || !["hour", "day", "week", "month"].includes(bucket) || !pqs_schema_profile_js_1.PqsSchemaProfileV1.bucketField(edge.target, fieldName))
77
- throw new Error("invalid profiled time bucket");
78
- const targetColumn = field(edge.target, pqs_schema_profile_js_1.pqsRelationMetadata[edge.target], fieldName);
117
+ field(edge.target, pqs_schema_profile_js_1.pqsRelationMetadata[edge.target], fieldName);
79
118
  joins.push(`join ${profile.relation(edge.target)} "${edgeName}" on "${edgeName}"."${edge.targetColumn}" = "${root}"."${edge.sourceColumn}"`);
80
- const expression = `date_trunc('${bucket}', "${edgeName}"."${targetColumn}")`;
119
+ const expression = `date_trunc('${key.bucket}', ${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(edge.target, fieldName, `"${edgeName}"`, profile)})`;
81
120
  expressions.push(expression);
82
- selected.push(`${expression} as "${edgeName}_${fieldName}_${bucket}"`);
121
+ selected.push(`${expression} as "${edgeName}_${fieldName}_${key.bucket}"`);
83
122
  }
84
- if (args.aggregate.count)
123
+ if (query.aggregates.count)
85
124
  selected.push("count(*)::text as count");
86
- for (const [operation, fields] of [["min", args.aggregate.min], ["max", args.aggregate.max], ["sum", args.aggregate.sum]])
87
- for (const name of fields ?? []) {
125
+ for (const [operation, fields] of [["min", query.aggregates.min], ["max", query.aggregates.max], ["sum", query.aggregates.sum]])
126
+ for (const name of fields) {
88
127
  if (!metadata.numericFields.includes(name))
89
128
  throw new Error(`${name} is not a numeric aggregate field of ${relation}`);
90
- selected.push(`${operation}("${root}"."${field(relation, metadata, name)}")::text as "${operation}_${name}"`);
129
+ field(relation, metadata, name);
130
+ selected.push(`${operation}(${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, name, `"${root}"`, profile)})::text as "${operation}_${name}"`);
91
131
  }
92
- return { text: `select ${selected.join(", ")} from ${profile.relation(relation)} "${root}" ${joins.join(" ")} group by ${expressions.join(", ")}`, values };
132
+ return { text: `${source.withClause}select ${selected.join(", ")} from ${source.relation} "${root}"${joins.length === 0 ? "" : ` ${joins.join(" ")}`}${predicate} group by ${expressions.join(", ")}`, values };
133
+ }
134
+ function logicalTypeRootSource(relation, profile) {
135
+ if (relation !== "__contract_tpe" && relation !== "__exercise_tpe") {
136
+ const physical = profile.relation(relation);
137
+ return { withClause: "", relation: physical, reference: physical };
138
+ }
139
+ const publicKey = (0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, "pk", '"physical_type"', profile);
140
+ const logicalRelation = '"logical_type_root"';
141
+ return {
142
+ withClause: `with ${logicalRelation} as (select distinct on (${publicKey}) "physical_type".* from ${profile.relation(relation)} "physical_type" order by ${publicKey}, "physical_type"."pk" asc) `,
143
+ relation: logicalRelation,
144
+ reference: logicalRelation,
145
+ };
93
146
  }
94
- function selectedFields(relation, metadata, select) {
95
- const fields = select === undefined
96
- ? Object.entries(metadata.fields)
97
- : Object.entries(select).filter(([, enabled]) => enabled).map(([name]) => [name, field(relation, metadata, name)]);
98
- if (fields.length === 0)
147
+ function selectedCanonicalFields(relation, metadata, selected, allowEmpty = false) {
148
+ const fields = selected === undefined ? Object.entries(metadata.fields) : selected.map((name) => [name, field(relation, metadata, name)]);
149
+ if (fields.length === 0 && !allowEmpty)
99
150
  throw new Error("select must include at least one field");
100
151
  return fields;
101
152
  }
102
- function compileWhere(relation, metadata, where, add) {
103
- const conditions = [];
104
- for (const [name, value] of Object.entries(where ?? {})) {
105
- const column = field(relation, metadata, name);
106
- if (value === null || Array.isArray(value) || typeof value !== "object")
107
- throw new Error(`${name} must be a filter`);
108
- const filter = value;
109
- for (const [operator, operand] of Object.entries(filter)) {
110
- if (operator === "equals")
111
- conditions.push(`"${column}" = ${add(operand)}`);
112
- else if (operator === "in" && Array.isArray(operand))
113
- conditions.push(operand.length === 0 ? "false" : `"${column}" = any(${add(operand)})`);
114
- else if (operator === "is" && operand === null)
115
- conditions.push(`"${column}" is null`);
116
- else if (operator === "isNot" && operand === null)
117
- conditions.push(`"${column}" is not null`);
118
- else if (["lt", "lte", "gt", "gte", "like", "ilike"].includes(operator)) {
119
- const sql = { lt: "<", lte: "<=", gt: ">", gte: ">=", like: "like", ilike: "ilike" }[operator];
120
- conditions.push(`"${column}" ${sql} ${add(operand)}`);
121
- }
122
- else
123
- throw new Error(`${operator} is not supported for ${name}`);
124
- }
125
- }
126
- return conditions.length === 0 ? "" : ` where ${conditions.join(" and ")}`;
153
+ function compileCanonicalJsonSelections(relation, selection, profile, add) {
154
+ return (selection?.json ?? []).map((projection) => {
155
+ if (!pqs_schema_profile_js_1.PqsSchemaProfileV1.jsonField(relation, projection.field))
156
+ throw new Error(`${projection.field} is not a JSON field of ${relation}`);
157
+ field(relation, pqs_schema_profile_js_1.pqsRelationMetadata[relation], projection.field);
158
+ const text = `${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, projection.field, "", profile)} #>> ${add(projection.path)}::text[]`;
159
+ const expression = projection.as === "text" ? text : projection.as === "numeric" ? `(${text})::numeric::text` : projection.as === "boolean" ? `(${text})::boolean` : `(${text})::timestamptz`;
160
+ return `${expression} as ${(0, pqs_sql_syntax_js_1.quotePqsIdentifier)(projection.name)}`;
161
+ });
127
162
  }
128
- function compileOrderBy(relation, metadata, orderBy) {
129
- if (orderBy === undefined)
163
+ function compileCanonicalOrderBy(relation, metadata, orderBy, profile) {
164
+ if (orderBy.length === 0)
130
165
  return "";
131
- const requested = orderBy.flatMap((entry) => Object.entries(entry));
132
- if (requested.length === 0 || requested.some(([, direction]) => direction !== "asc" && direction !== "desc"))
133
- throw new Error("orderBy must be a non-empty list of one-field entries");
134
- const ordered = requested.map(([name, direction]) => [field(relation, metadata, name), direction]);
135
- const stableKey = metadata.uniqueKeys[0]?.[0];
136
- if (stableKey !== undefined && !ordered.some(([column]) => column === metadata.fields[stableKey]))
137
- ordered.push([metadata.fields[stableKey], "asc"]);
138
- return ` order by ${ordered.map(([column, direction]) => `"${column}" ${direction}`).join(", ")}`;
166
+ return ` order by ${orderBy.map((order) => {
167
+ field(relation, metadata, order.path[0]);
168
+ return `${(0, pqs_sql_compiler_js_1.compileCanonicalPhysicalField)(relation, order.path[0], "", profile)} ${order.direction}`;
169
+ }).join(", ")}`;
139
170
  }
140
171
  function field(relation, metadata, name) {
141
172
  const column = metadata.fields[name];
@@ -143,8 +174,20 @@ function field(relation, metadata, name) {
143
174
  throw new Error(`${name} is not a field of ${relation}`);
144
175
  return column;
145
176
  }
146
- function assertPage(args) {
147
- for (const [name, value] of [["take", args.take], ["skip", args.skip]])
148
- if (value !== undefined && (!Number.isInteger(value) || value < 0))
149
- throw new Error(`${name} must be a non-negative integer`);
177
+ function assertCanonicalPhysicalFindMany(relation, query) {
178
+ if (query === null || typeof query !== "object" || query.kind !== "findMany" || !Array.isArray(query.includes) || !Array.isArray(query.orderBy)) {
179
+ throw new Error("compilePqsRelationFindMany requires a canonical findMany query");
180
+ }
181
+ if (relationForCanonical(query.relation) !== relation)
182
+ throw new Error(`Canonical relation ${query.relation} does not match ${relation}`);
183
+ }
184
+ function assertCanonicalPhysicalQuery(relation, query, kind) {
185
+ if (query === null || typeof query !== "object" || query.kind !== kind) {
186
+ throw new Error(`compilePqsRelation${kind[0].toUpperCase()}${kind.slice(1)} requires a canonical ${kind} query`);
187
+ }
188
+ if (relationForCanonical(query.relation) !== relation)
189
+ throw new Error(`Canonical relation ${query.relation} does not match ${relation}`);
190
+ }
191
+ function relationForCanonical(relation) {
192
+ return { contracts: "__contracts", contractTypes: "__contract_tpe", events: "__events", exercises: "__exercises", exerciseTypes: "__exercise_tpe", packages: "__packages", transactions: "__transactions", watermark: "__watermark" }[relation];
150
193
  }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compilePqsResultShape = compilePqsResultShape;
4
+ const pqs_schema_profile_js_1 = require("./pqs-schema-profile.js");
5
+ function compilePqsResultShape(relation, cardinality, selection, includes) {
6
+ const fields = relation === "__contracts"
7
+ ? selection?.fields ?? ["contractId", "templateId", "packageId", "payload", "witnesses", "createdEventOffset", "createdAt", "archivedEventOffset", "archivedAt", "active"]
8
+ : selection?.fields ?? Object.keys(pqs_schema_profile_js_1.pqsRelationMetadata[relation].fields);
9
+ const shape = {
10
+ relation,
11
+ cardinality,
12
+ fields: fields.map((name) => Object.freeze({ name })),
13
+ json: (selection?.json ?? []).map((projection) => Object.freeze({ ...projection, path: Object.freeze([...projection.path]) })),
14
+ includes: includes.map((include) => {
15
+ const edge = pqs_schema_profile_js_1.pqsRelationEdges[relation]?.[include.edge];
16
+ if (edge === undefined || edge.target !== relationForCanonical(include.relation) || edge.cardinality !== include.cardinality)
17
+ throw new Error(`Invalid canonical include ${include.edge}`);
18
+ return Object.freeze({ edge: include.edge, target: edge.target, cardinality: include.cardinality, shape: compilePqsResultShape(edge.target, include.cardinality, include.select, include.includes) });
19
+ }),
20
+ };
21
+ Object.freeze(shape.fields);
22
+ Object.freeze(shape.json);
23
+ Object.freeze(shape.includes);
24
+ return Object.freeze(shape);
25
+ }
26
+ function relationForCanonical(relation) {
27
+ return { contracts: "__contracts", contractTypes: "__contract_tpe", events: "__events", exercises: "__exercises", exerciseTypes: "__exercise_tpe", packages: "__packages", transactions: "__transactions", watermark: "__watermark" }[relation];
28
+ }
@@ -4,6 +4,7 @@ exports.PqsSchemaProfileV1 = exports.requiredPqsColumns = exports.pqsRelationMet
4
4
  exports.validatePqsSchemaAsync = validatePqsSchemaAsync;
5
5
  exports.assertPqsSchemaIdentifier = assertPqsSchemaIdentifier;
6
6
  const pqs_schema_profile_error_js_1 = require("../errors/pqs-schema-profile-error.js");
7
+ const query_schema_js_1 = require("../canonical/query-schema.js");
7
8
  const schemaIdentifier = /^[A-Za-z_][A-Za-z0-9_]*$/;
8
9
  exports.requiredPqsRelations = [
9
10
  "__contracts",
@@ -15,56 +16,56 @@ exports.requiredPqsRelations = [
15
16
  "__transactions",
16
17
  "__watermark",
17
18
  ];
18
- exports.pqsRelationEdges = {
19
- __contracts: {
20
- contractType: { target: "__contract_tpe", sourceColumn: "tpe_pk", targetColumn: "pk", cardinality: "one", nullable: false },
21
- createdTransaction: { target: "__transactions", sourceColumn: "created_at_ix", targetColumn: "ix", cardinality: "one", nullable: false },
22
- archivedTransaction: { target: "__transactions", sourceColumn: "archived_at_ix", targetColumn: "ix", cardinality: "one", nullable: true },
23
- exercises: { target: "__exercises", sourceColumn: "contract_id", targetColumn: "contract_id", cardinality: "many", nullable: false },
24
- },
25
- __contract_tpe: {
26
- contracts: { target: "__contracts", sourceColumn: "pk", targetColumn: "tpe_pk", cardinality: "many", nullable: false },
27
- exercises: { target: "__exercises", sourceColumn: "pk", targetColumn: "contract_tpe_pk", cardinality: "many", nullable: false },
28
- },
29
- __events: {
30
- transaction: { target: "__transactions", sourceColumn: "tx_ix", targetColumn: "ix", cardinality: "one", nullable: false },
31
- exercises: { target: "__exercises", sourceColumn: "pk", targetColumn: "exercise_event_pk", cardinality: "many", nullable: false },
32
- },
33
- __exercises: {
34
- exerciseType: { target: "__exercise_tpe", sourceColumn: "tpe_pk", targetColumn: "pk", cardinality: "one", nullable: false },
35
- contractType: { target: "__contract_tpe", sourceColumn: "contract_tpe_pk", targetColumn: "pk", cardinality: "one", nullable: false },
36
- event: { target: "__events", sourceColumn: "exercise_event_pk", targetColumn: "pk", cardinality: "one", nullable: true },
37
- transaction: { target: "__transactions", sourceColumn: "exercised_at_ix", targetColumn: "ix", cardinality: "one", nullable: true },
38
- package: { target: "__packages", sourceColumn: "package_pk", targetColumn: "pk", cardinality: "one", nullable: false },
39
- contract: { target: "__contracts", sourceColumn: "contract_id", targetColumn: "contract_id", cardinality: "one", nullable: false },
40
- },
41
- __exercise_tpe: { exercises: { target: "__exercises", sourceColumn: "pk", targetColumn: "tpe_pk", cardinality: "many", nullable: false } },
42
- __packages: { exercises: { target: "__exercises", sourceColumn: "pk", targetColumn: "package_pk", cardinality: "many", nullable: false } },
43
- __transactions: {
44
- events: { target: "__events", sourceColumn: "ix", targetColumn: "tx_ix", cardinality: "many", nullable: false },
45
- createdContracts: { target: "__contracts", sourceColumn: "ix", targetColumn: "created_at_ix", cardinality: "many", nullable: false },
46
- archivedContracts: { target: "__contracts", sourceColumn: "ix", targetColumn: "archived_at_ix", cardinality: "many", nullable: false },
47
- exercises: { target: "__exercises", sourceColumn: "ix", targetColumn: "exercised_at_ix", cardinality: "many", nullable: false },
48
- },
19
+ const queryRelationByPqsRelation = {
20
+ __contracts: "contracts",
21
+ __contract_tpe: "contractTypes",
22
+ __events: "events",
23
+ __exercises: "exercises",
24
+ __exercise_tpe: "exerciseTypes",
25
+ __packages: "packages",
26
+ __transactions: "transactions",
27
+ __watermark: "watermark",
49
28
  };
50
- const pqsJsonFields = {
51
- __contracts: ["payload"],
52
- __exercises: ["argument", "result"],
53
- __transactions: ["traceContext"],
29
+ const pqsRelationByQueryRelation = {
30
+ contracts: "__contracts",
31
+ contractTypes: "__contract_tpe",
32
+ events: "__events",
33
+ exercises: "__exercises",
34
+ exerciseTypes: "__exercise_tpe",
35
+ packages: "__packages",
36
+ transactions: "__transactions",
37
+ watermark: "__watermark",
54
38
  };
55
- const pqsBucketFields = {
56
- __transactions: ["effectiveAt"],
39
+ const pqsEdgeColumns = {
40
+ __contracts: { contractType: { sourceColumn: "tpe_pk", targetColumn: "pk" }, createdTransaction: { sourceColumn: "created_at_ix", targetColumn: "ix" }, archivedTransaction: { sourceColumn: "archived_at_ix", targetColumn: "ix" }, exercises: { sourceColumn: "contract_id", targetColumn: "contract_id" } },
41
+ __contract_tpe: { contracts: { sourceColumn: "pk", targetColumn: "tpe_pk" }, exercises: { sourceColumn: "pk", targetColumn: "contract_tpe_pk" } },
42
+ __events: { transaction: { sourceColumn: "tx_ix", targetColumn: "ix" }, exercises: { sourceColumn: "pk", targetColumn: "exercise_event_pk" } },
43
+ __exercises: { exerciseType: { sourceColumn: "tpe_pk", targetColumn: "pk" }, contractType: { sourceColumn: "contract_tpe_pk", targetColumn: "pk" }, event: { sourceColumn: "exercise_event_pk", targetColumn: "pk" }, transaction: { sourceColumn: "exercised_at_ix", targetColumn: "ix" }, package: { sourceColumn: "package_pk", targetColumn: "pk" }, contract: { sourceColumn: "contract_id", targetColumn: "contract_id" } },
44
+ __exercise_tpe: { exercises: { sourceColumn: "pk", targetColumn: "tpe_pk" } },
45
+ __packages: { exercises: { sourceColumn: "pk", targetColumn: "package_pk" } },
46
+ __transactions: { events: { sourceColumn: "ix", targetColumn: "tx_ix" }, createdContracts: { sourceColumn: "ix", targetColumn: "created_at_ix" }, archivedContracts: { sourceColumn: "ix", targetColumn: "archived_at_ix" }, exercises: { sourceColumn: "ix", targetColumn: "exercised_at_ix" } },
57
47
  };
58
- exports.pqsRelationMetadata = {
59
- __contracts: { fields: {}, uniqueKeys: [["contractId"]], numericFields: ["createdEventOffset", "archivedEventOffset"], arrayFields: ["witnesses"], dateFields: ["createdAt", "archivedAt"], binaryFields: [] },
60
- __contract_tpe: { fields: { pk: "pk", payloadType: "payload_type", aliases: "aliases", packageName: "package_name", moduleName: "module_name", entityName: "entity_name", templateFqn: "template_fqn" }, uniqueKeys: [["pk"]], numericFields: ["pk"], arrayFields: ["aliases"], dateFields: [], binaryFields: [] },
61
- __events: { fields: { pk: "pk", txIx: "tx_ix", eventId: "event_id", type: "type" }, uniqueKeys: [["pk"]], numericFields: ["pk", "txIx"], arrayFields: [], dateFields: [], binaryFields: [], stringFields: ["eventId", "type"] },
62
- __exercises: { fields: { tpePk: "tpe_pk", contractTpePk: "contract_tpe_pk", exerciseEventPk: "exercise_event_pk", exercisedAtIx: "exercised_at_ix", contractId: "contract_id", argument: "argument", result: "result", redactionId: "redaction_id", packagePk: "package_pk", controllers: "controllers", lastDescendantNodeId: "last_descendant_node_id", witnesses: "witnesses" }, uniqueKeys: [], numericFields: ["tpePk", "contractTpePk", "exerciseEventPk", "exercisedAtIx", "packagePk", "lastDescendantNodeId"], arrayFields: ["controllers", "witnesses"], dateFields: [], binaryFields: [] },
63
- __exercise_tpe: { fields: { pk: "pk", choice: "choice", consuming: "consuming", aliases: "aliases", packageName: "package_name", moduleName: "module_name", entityName: "entity_name", templateFqn: "template_fqn", choiceFqn: "choice_fqn" }, uniqueKeys: [["pk"]], numericFields: ["pk"], arrayFields: ["aliases"], dateFields: [], binaryFields: [] },
64
- __packages: { fields: { pk: "pk", name: "name", version: "version", id: "id" }, uniqueKeys: [["pk"], ["id"]], numericFields: ["pk"], arrayFields: [], dateFields: [], binaryFields: [], stringFields: ["name", "version", "id"] },
65
- __transactions: { fields: { ix: "ix", offset: "offset", transactionId: "transaction_id", effectiveAt: "effective_at", workflowId: "workflow_id", domainId: "domain_id", traceContext: "trace_context", externalTransactionHash: "external_transaction_hash", paidTrafficCost: "paid_traffic_cost" }, uniqueKeys: [["ix"], ["offset"]], numericFields: ["ix", "offset", "paidTrafficCost"], arrayFields: [], dateFields: ["effectiveAt"], binaryFields: ["externalTransactionHash"] },
66
- __watermark: { fields: { singleton: "singleton", ix: "ix", offset: "offset", instanceId: "instance_id" }, uniqueKeys: [["singleton"]], numericFields: ["ix", "offset"], arrayFields: [], dateFields: [], binaryFields: [] },
48
+ exports.pqsRelationEdges = Object.fromEntries(exports.requiredPqsRelations.map((relation) => [relation, Object.fromEntries(Object.entries(query_schema_js_1.queryRelationEdges[queryRelationByPqsRelation[relation]] ?? {}).map(([name, edge]) => {
49
+ const columns = pqsEdgeColumns[relation]?.[name];
50
+ if (columns === undefined) {
51
+ throw new Error(`Missing physical PQS edge mapping for ${relation}.${name}`);
52
+ }
53
+ return [name, { target: pqsRelationByQueryRelation[edge.target], ...columns, cardinality: edge.cardinality, nullable: edge.nullable }];
54
+ }))]));
55
+ const pqsFields = {
56
+ __contracts: {},
57
+ __contract_tpe: { pk: "pk", payloadType: "payload_type", aliases: "aliases", packageName: "package_name", moduleName: "module_name", entityName: "entity_name", templateFqn: "template_fqn" },
58
+ __events: { pk: "pk", txIx: "tx_ix", eventId: "event_id", type: "type" },
59
+ __exercises: { tpePk: "tpe_pk", contractTpePk: "contract_tpe_pk", exerciseEventPk: "exercise_event_pk", exercisedAtIx: "exercised_at_ix", contractId: "contract_id", argument: "argument", result: "result", redactionId: "redaction_id", packagePk: "package_pk", controllers: "controllers", lastDescendantNodeId: "last_descendant_node_id", witnesses: "witnesses" },
60
+ __exercise_tpe: { pk: "pk", choice: "choice", consuming: "consuming", aliases: "aliases", packageName: "package_name", moduleName: "module_name", entityName: "entity_name", templateFqn: "template_fqn", choiceFqn: "choice_fqn" },
61
+ __packages: { pk: "pk", name: "name", version: "version", id: "id" },
62
+ __transactions: { ix: "ix", offset: "offset", transactionId: "transaction_id", effectiveAt: "effective_at", workflowId: "workflow_id", domainId: "domain_id", traceContext: "trace_context", externalTransactionHash: "external_transaction_hash", paidTrafficCost: "paid_traffic_cost" },
63
+ __watermark: { singleton: "singleton", ix: "ix", offset: "offset", instanceId: "instance_id" },
67
64
  };
65
+ exports.pqsRelationMetadata = Object.fromEntries(exports.requiredPqsRelations.map((relation) => {
66
+ const logical = query_schema_js_1.queryRelationMetadata[queryRelationByPqsRelation[relation]];
67
+ return [relation, { fields: pqsFields[relation], uniqueKeys: logical.uniqueKeys, numericFields: logical.numericFields, arrayFields: logical.arrayFields, dateFields: logical.dateFields, binaryFields: logical.binaryFields, stringFields: logical.stringFields }];
68
+ }));
68
69
  exports.requiredPqsColumns = {
69
70
  __contracts: [
70
71
  "tpe_pk",
@@ -145,10 +146,10 @@ class PqsSchemaProfileV1 {
145
146
  return `${quoteIdentifier(this.schema)}.${quoteIdentifier(relation)}`;
146
147
  }
147
148
  static jsonField(relation, field) {
148
- return pqsJsonFields[relation]?.includes(field) ?? false;
149
+ return query_schema_js_1.queryRelationMetadata[queryRelationByPqsRelation[relation]].jsonFields.includes(field);
149
150
  }
150
151
  static bucketField(relation, field) {
151
- return pqsBucketFields[relation]?.includes(field) ?? false;
152
+ return query_schema_js_1.queryRelationMetadata[queryRelationByPqsRelation[relation]].bucketFields.includes(field);
152
153
  }
153
154
  }
154
155
  exports.PqsSchemaProfileV1 = PqsSchemaProfileV1;