@dyrected/core 2.9.0 → 2.10.0
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/dist/{app-config-gEQJFvnN.d.cts → app-config-DQhcrGDE.d.cts} +297 -37
- package/dist/{app-config-gEQJFvnN.d.ts → app-config-DQhcrGDE.d.ts} +297 -37
- package/dist/{chunk-XRIONGRY.js → chunk-TYPDQTE2.js} +110 -30
- package/dist/index.cjs +173 -67
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +64 -38
- package/dist/server.cjs +130 -32
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +21 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
validatePreviewUrlExpression,
|
|
102
102
|
wordCount,
|
|
103
103
|
workflowCapabilities
|
|
104
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-TYPDQTE2.js";
|
|
105
105
|
import "./chunk-JSBRDJBE.js";
|
|
106
106
|
|
|
107
107
|
// src/types/workflows.ts
|
|
@@ -114,44 +114,17 @@ var LIFECYCLE_EVENT_NAMES = [
|
|
|
114
114
|
|
|
115
115
|
// src/types/views.ts
|
|
116
116
|
function defineView(config) {
|
|
117
|
-
return
|
|
118
|
-
slug: config.slug,
|
|
119
|
-
label: config.label,
|
|
120
|
-
icon: config.icon,
|
|
121
|
-
layout: config.layout ?? "table",
|
|
122
|
-
filter: config.filter,
|
|
123
|
-
groupBy: config.groupBy,
|
|
124
|
-
dateField: config.dateField,
|
|
125
|
-
startDateField: config.startDateField,
|
|
126
|
-
endDateField: config.endDateField,
|
|
127
|
-
columns: config.columns,
|
|
128
|
-
sort: config.sort,
|
|
129
|
-
actions: config.actions,
|
|
130
|
-
features: config.features,
|
|
131
|
-
actionOrder: config.actionOrder,
|
|
132
|
-
metrics: config.metrics,
|
|
133
|
-
access: config.access
|
|
134
|
-
};
|
|
117
|
+
return config;
|
|
135
118
|
}
|
|
136
119
|
function defineAction(config) {
|
|
137
|
-
return
|
|
138
|
-
name: config.name,
|
|
139
|
-
label: config.label,
|
|
140
|
-
icon: config.icon,
|
|
141
|
-
type: config.type ?? "row",
|
|
142
|
-
confirm: config.confirm,
|
|
143
|
-
fields: config.fields,
|
|
144
|
-
mutation: config.mutation,
|
|
145
|
-
handler: config.handler,
|
|
146
|
-
access: config.access
|
|
147
|
-
};
|
|
120
|
+
return config;
|
|
148
121
|
}
|
|
149
122
|
|
|
150
123
|
// src/utils/parse-where.ts
|
|
151
124
|
function assertNever(op, context) {
|
|
152
125
|
throw new Error(`[dyrected/core] Unhandled where operator "${op}" in ${context}`);
|
|
153
126
|
}
|
|
154
|
-
function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "LIKE") {
|
|
127
|
+
function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = placeholder === "pg" ? "ILIKE" : "LIKE") {
|
|
155
128
|
const params = [];
|
|
156
129
|
let pgIndex = 1;
|
|
157
130
|
function next() {
|
|
@@ -162,8 +135,16 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "L
|
|
|
162
135
|
}
|
|
163
136
|
function buildOperator(field, value) {
|
|
164
137
|
const c = col(field);
|
|
138
|
+
const isJsonExtract = c.startsWith("data->>") || c.startsWith("json_extract(") || c.startsWith("JSON_UNQUOTE(");
|
|
165
139
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
166
|
-
|
|
140
|
+
if (value === null) {
|
|
141
|
+
return `${c} IS NULL`;
|
|
142
|
+
}
|
|
143
|
+
if (value === "" && placeholder === "pg") {
|
|
144
|
+
params.push("");
|
|
145
|
+
return `(${c})::text = ${next()}`;
|
|
146
|
+
}
|
|
147
|
+
params.push(typeof value === "boolean" && isJsonExtract ? String(value) : value);
|
|
167
148
|
return `${c} = ${next()}`;
|
|
168
149
|
}
|
|
169
150
|
const entries = Object.entries(value);
|
|
@@ -174,18 +155,63 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "L
|
|
|
174
155
|
return buildSingleOp(c, op, operand);
|
|
175
156
|
}
|
|
176
157
|
function buildSingleOp(c, op, operand) {
|
|
158
|
+
const isJsonExtract = c.startsWith("data->>") || c.startsWith("json_extract(") || c.startsWith("JSON_UNQUOTE(");
|
|
177
159
|
switch (op) {
|
|
178
160
|
case "equals":
|
|
179
|
-
|
|
161
|
+
if (operand === null) {
|
|
162
|
+
return `${c} IS NULL`;
|
|
163
|
+
}
|
|
164
|
+
if (operand === "" && placeholder === "pg") {
|
|
165
|
+
params.push("");
|
|
166
|
+
return `(${c})::text = ${next()}`;
|
|
167
|
+
}
|
|
168
|
+
if (typeof operand === "boolean") {
|
|
169
|
+
if (placeholder === "?") {
|
|
170
|
+
params.push(operand ? 1 : 0);
|
|
171
|
+
} else if (isJsonExtract) {
|
|
172
|
+
params.push(String(operand));
|
|
173
|
+
} else {
|
|
174
|
+
params.push(operand);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
params.push(operand);
|
|
178
|
+
}
|
|
180
179
|
return `${c} = ${next()}`;
|
|
181
180
|
case "not_equals":
|
|
182
|
-
|
|
181
|
+
if (operand === null) {
|
|
182
|
+
return `${c} IS NOT NULL`;
|
|
183
|
+
}
|
|
184
|
+
if (operand === "" && placeholder === "pg") {
|
|
185
|
+
params.push("");
|
|
186
|
+
return `(${c})::text != ${next()}`;
|
|
187
|
+
}
|
|
188
|
+
if (typeof operand === "boolean") {
|
|
189
|
+
if (placeholder === "?") {
|
|
190
|
+
params.push(operand ? 1 : 0);
|
|
191
|
+
} else if (isJsonExtract) {
|
|
192
|
+
params.push(String(operand));
|
|
193
|
+
} else {
|
|
194
|
+
params.push(operand);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
params.push(operand);
|
|
198
|
+
}
|
|
183
199
|
return `${c} != ${next()}`;
|
|
184
200
|
case "in": {
|
|
185
201
|
const vals = Array.isArray(operand) ? operand : [operand];
|
|
186
202
|
if (vals.length === 0) return "1=0";
|
|
187
203
|
const placeholders = vals.map((v) => {
|
|
188
|
-
|
|
204
|
+
if (typeof v === "boolean") {
|
|
205
|
+
if (placeholder === "?") {
|
|
206
|
+
params.push(v ? 1 : 0);
|
|
207
|
+
} else if (isJsonExtract) {
|
|
208
|
+
params.push(String(v));
|
|
209
|
+
} else {
|
|
210
|
+
params.push(v);
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
params.push(v);
|
|
214
|
+
}
|
|
189
215
|
return next();
|
|
190
216
|
});
|
|
191
217
|
return `${c} IN (${placeholders.join(", ")})`;
|
|
@@ -194,7 +220,7 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "L
|
|
|
194
220
|
const vals = Array.isArray(operand) ? operand : [operand];
|
|
195
221
|
if (vals.length === 0) return "1=1";
|
|
196
222
|
const placeholders = vals.map((v) => {
|
|
197
|
-
params.push(typeof v === "boolean" ? String(v) : v);
|
|
223
|
+
params.push(typeof v === "boolean" && isJsonExtract ? String(v) : v);
|
|
198
224
|
return next();
|
|
199
225
|
});
|
|
200
226
|
return `${c} NOT IN (${placeholders.join(", ")})`;
|
|
@@ -220,10 +246,10 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "L
|
|
|
220
246
|
case "contains":
|
|
221
247
|
case "like":
|
|
222
248
|
params.push(`%${operand}%`);
|
|
223
|
-
return `${c} ${likeOperator} ${next()}`;
|
|
249
|
+
return placeholder === "pg" ? `(${c})::text ${likeOperator} ${next()}` : `${c} ${likeOperator} ${next()}`;
|
|
224
250
|
case "starts_with":
|
|
225
251
|
params.push(`${operand}%`);
|
|
226
|
-
return `${c} ${likeOperator} ${next()}`;
|
|
252
|
+
return placeholder === "pg" ? `(${c})::text ${likeOperator} ${next()}` : `${c} ${likeOperator} ${next()}`;
|
|
227
253
|
case "exists":
|
|
228
254
|
return operand ? `${c} IS NOT NULL` : `${c} IS NULL`;
|
|
229
255
|
default:
|
package/dist/server.cjs
CHANGED
|
@@ -6247,9 +6247,23 @@ var CollectionController = class {
|
|
|
6247
6247
|
400
|
|
6248
6248
|
);
|
|
6249
6249
|
}
|
|
6250
|
+
let rawAggregates;
|
|
6251
|
+
let groupBy;
|
|
6252
|
+
if ("aggregates" in body && typeof body.aggregates === "object" && body.aggregates !== null && !Array.isArray(body.aggregates)) {
|
|
6253
|
+
rawAggregates = body.aggregates;
|
|
6254
|
+
if (typeof body.groupBy === "string" && body.groupBy.trim()) {
|
|
6255
|
+
groupBy = body.groupBy.trim();
|
|
6256
|
+
}
|
|
6257
|
+
} else {
|
|
6258
|
+
rawAggregates = body;
|
|
6259
|
+
const queryGroupBy = c.req.query("groupBy");
|
|
6260
|
+
if (typeof queryGroupBy === "string" && queryGroupBy.trim()) {
|
|
6261
|
+
groupBy = queryGroupBy.trim();
|
|
6262
|
+
}
|
|
6263
|
+
}
|
|
6250
6264
|
const { sanitizeWhereClause: sanitizeWhereClause2 } = await Promise.resolve().then(() => (init_where_sanitizer(), where_sanitizer_exports));
|
|
6251
6265
|
const sanitizedAggregates = {};
|
|
6252
|
-
for (const [key, op] of Object.entries(
|
|
6266
|
+
for (const [key, op] of Object.entries(rawAggregates)) {
|
|
6253
6267
|
if (!op || typeof op !== "object" || Array.isArray(op)) {
|
|
6254
6268
|
return c.json(
|
|
6255
6269
|
{
|
|
@@ -6274,7 +6288,8 @@ var CollectionController = class {
|
|
|
6274
6288
|
}
|
|
6275
6289
|
const result = await db.aggregate({
|
|
6276
6290
|
collection: this.collection.slug,
|
|
6277
|
-
aggregates: sanitizedAggregates
|
|
6291
|
+
aggregates: sanitizedAggregates,
|
|
6292
|
+
...groupBy ? { groupBy } : {}
|
|
6278
6293
|
});
|
|
6279
6294
|
return c.json(result);
|
|
6280
6295
|
}
|
|
@@ -8959,39 +8974,94 @@ function generateOpenApi(config) {
|
|
|
8959
8974
|
post: {
|
|
8960
8975
|
tags: [collectionTag],
|
|
8961
8976
|
summary: `Aggregate ${labels.plural}`,
|
|
8962
|
-
description: `Compute statistical aggregations (count, sum, avg, min, max) across ${labels.plural}.`,
|
|
8977
|
+
description: `Compute statistical aggregations (count, countDistinct, distinct, sum, avg, min, max, groupBy) across ${labels.plural}.`,
|
|
8978
|
+
parameters: [
|
|
8979
|
+
{
|
|
8980
|
+
name: "groupBy",
|
|
8981
|
+
in: "query",
|
|
8982
|
+
description: "Optional field name to compute aggregates grouped by distinct values",
|
|
8983
|
+
required: false,
|
|
8984
|
+
schema: { type: "string" }
|
|
8985
|
+
}
|
|
8986
|
+
],
|
|
8963
8987
|
requestBody: {
|
|
8964
8988
|
required: true,
|
|
8965
8989
|
content: {
|
|
8966
8990
|
"application/json": {
|
|
8967
8991
|
schema: {
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
sum: { type: "string", description: "Field to sum" },
|
|
8974
|
-
avg: { type: "string", description: "Field to average" },
|
|
8975
|
-
min: { type: "string", description: "Field for minimum" },
|
|
8976
|
-
max: { type: "string", description: "Field for maximum" },
|
|
8977
|
-
cast: {
|
|
8978
|
-
type: "string",
|
|
8979
|
-
enum: [
|
|
8980
|
-
"number",
|
|
8981
|
-
"integer",
|
|
8982
|
-
"float",
|
|
8983
|
-
"boolean",
|
|
8984
|
-
"date",
|
|
8985
|
-
"string"
|
|
8986
|
-
]
|
|
8987
|
-
},
|
|
8988
|
-
where: {
|
|
8992
|
+
oneOf: [
|
|
8993
|
+
{
|
|
8994
|
+
type: "object",
|
|
8995
|
+
description: "Flat aggregate operations dictionary",
|
|
8996
|
+
additionalProperties: {
|
|
8989
8997
|
type: "object",
|
|
8990
|
-
|
|
8991
|
-
|
|
8998
|
+
properties: {
|
|
8999
|
+
count: { type: "string", example: "*" },
|
|
9000
|
+
countDistinct: { type: "string", description: "Field to count unique non-null values" },
|
|
9001
|
+
distinct: { type: "string", description: "Field to extract unique non-null values" },
|
|
9002
|
+
sum: { type: "string", description: "Field to sum" },
|
|
9003
|
+
avg: { type: "string", description: "Field to average" },
|
|
9004
|
+
min: { type: "string", description: "Field for minimum" },
|
|
9005
|
+
max: { type: "string", description: "Field for maximum" },
|
|
9006
|
+
cast: {
|
|
9007
|
+
type: "string",
|
|
9008
|
+
enum: [
|
|
9009
|
+
"number",
|
|
9010
|
+
"integer",
|
|
9011
|
+
"float",
|
|
9012
|
+
"boolean",
|
|
9013
|
+
"date",
|
|
9014
|
+
"string"
|
|
9015
|
+
]
|
|
9016
|
+
},
|
|
9017
|
+
where: {
|
|
9018
|
+
type: "object",
|
|
9019
|
+
description: "Per-aggregate filter condition",
|
|
9020
|
+
additionalProperties: true
|
|
9021
|
+
}
|
|
9022
|
+
}
|
|
8992
9023
|
}
|
|
9024
|
+
},
|
|
9025
|
+
{
|
|
9026
|
+
type: "object",
|
|
9027
|
+
description: "Grouped aggregate payload with explicit groupBy property",
|
|
9028
|
+
properties: {
|
|
9029
|
+
groupBy: { type: "string", description: "Field name to group aggregates by" },
|
|
9030
|
+
aggregates: {
|
|
9031
|
+
type: "object",
|
|
9032
|
+
additionalProperties: {
|
|
9033
|
+
type: "object",
|
|
9034
|
+
properties: {
|
|
9035
|
+
count: { type: "string", example: "*" },
|
|
9036
|
+
countDistinct: { type: "string", description: "Field to count unique non-null values" },
|
|
9037
|
+
distinct: { type: "string", description: "Field to extract unique non-null values" },
|
|
9038
|
+
sum: { type: "string", description: "Field to sum" },
|
|
9039
|
+
avg: { type: "string", description: "Field to average" },
|
|
9040
|
+
min: { type: "string", description: "Field for minimum" },
|
|
9041
|
+
max: { type: "string", description: "Field for maximum" },
|
|
9042
|
+
cast: {
|
|
9043
|
+
type: "string",
|
|
9044
|
+
enum: [
|
|
9045
|
+
"number",
|
|
9046
|
+
"integer",
|
|
9047
|
+
"float",
|
|
9048
|
+
"boolean",
|
|
9049
|
+
"date",
|
|
9050
|
+
"string"
|
|
9051
|
+
]
|
|
9052
|
+
},
|
|
9053
|
+
where: {
|
|
9054
|
+
type: "object",
|
|
9055
|
+
description: "Per-aggregate filter condition",
|
|
9056
|
+
additionalProperties: true
|
|
9057
|
+
}
|
|
9058
|
+
}
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
},
|
|
9062
|
+
required: ["aggregates"]
|
|
8993
9063
|
}
|
|
8994
|
-
|
|
9064
|
+
]
|
|
8995
9065
|
}
|
|
8996
9066
|
}
|
|
8997
9067
|
}
|
|
@@ -9002,11 +9072,36 @@ function generateOpenApi(config) {
|
|
|
9002
9072
|
content: {
|
|
9003
9073
|
"application/json": {
|
|
9004
9074
|
schema: {
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9075
|
+
oneOf: [
|
|
9076
|
+
{
|
|
9077
|
+
type: "object",
|
|
9078
|
+
description: "Flat aggregate results map",
|
|
9079
|
+
additionalProperties: {
|
|
9080
|
+
oneOf: [
|
|
9081
|
+
{ type: "number", nullable: true },
|
|
9082
|
+
{ type: "array", items: { type: "string" } }
|
|
9083
|
+
]
|
|
9084
|
+
}
|
|
9085
|
+
},
|
|
9086
|
+
{
|
|
9087
|
+
type: "object",
|
|
9088
|
+
description: "Grouped aggregate results breakdown",
|
|
9089
|
+
properties: {
|
|
9090
|
+
groups: {
|
|
9091
|
+
type: "object",
|
|
9092
|
+
additionalProperties: {
|
|
9093
|
+
type: "object",
|
|
9094
|
+
additionalProperties: {
|
|
9095
|
+
oneOf: [
|
|
9096
|
+
{ type: "number", nullable: true },
|
|
9097
|
+
{ type: "array", items: { type: "string" } }
|
|
9098
|
+
]
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9101
|
+
}
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
9104
|
+
]
|
|
9010
9105
|
}
|
|
9011
9106
|
}
|
|
9012
9107
|
}
|
|
@@ -9753,6 +9848,8 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
9753
9848
|
columns: view.columns,
|
|
9754
9849
|
sort: view.sort,
|
|
9755
9850
|
metrics: view.metrics,
|
|
9851
|
+
features: view.features,
|
|
9852
|
+
actionOrder: view.actionOrder,
|
|
9756
9853
|
actions: await Promise.all(
|
|
9757
9854
|
(view.actions || []).map(async (action) => ({
|
|
9758
9855
|
name: action.name,
|
|
@@ -9829,6 +9926,7 @@ function registerRoutes(app, config) {
|
|
|
9829
9926
|
auth: !!col.auth,
|
|
9830
9927
|
audit: !!col.audit,
|
|
9831
9928
|
drafts: !!col.drafts,
|
|
9929
|
+
defaultView: col.defaultView ?? col.admin?.defaultView,
|
|
9832
9930
|
views: await Promise.all((col.views || []).map((view) => serializeViewForApi(view, serializeAccess))),
|
|
9833
9931
|
admin: col.admin,
|
|
9834
9932
|
detail: serializeDetailForApi(col.detail),
|
package/dist/server.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono_types from 'hono/types';
|
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Context, Hono } from 'hono';
|
|
4
4
|
import { Logger } from 'pino';
|
|
5
|
-
import { D as DyrectedConfig,
|
|
5
|
+
import { D as DyrectedConfig, b4 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-DQhcrGDE.cjs';
|
|
6
6
|
import { JWTPayload } from 'jose';
|
|
7
7
|
import { Tracer, Counter, Attributes, Histogram, Span } from '@opentelemetry/api';
|
|
8
8
|
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
|
@@ -498,7 +498,7 @@ declare class CollectionController {
|
|
|
498
498
|
}, 403, "json">) | (Response & hono.TypedResponse<{
|
|
499
499
|
message: string;
|
|
500
500
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
501
|
-
[x: string]:
|
|
501
|
+
[x: string]: any;
|
|
502
502
|
}, hono_utils_http_status.ContentfulStatusCode, "json">)>;
|
|
503
503
|
}
|
|
504
504
|
|
package/dist/server.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono_types from 'hono/types';
|
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Context, Hono } from 'hono';
|
|
4
4
|
import { Logger } from 'pino';
|
|
5
|
-
import { D as DyrectedConfig,
|
|
5
|
+
import { D as DyrectedConfig, b4 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-DQhcrGDE.js';
|
|
6
6
|
import { JWTPayload } from 'jose';
|
|
7
7
|
import { Tracer, Counter, Attributes, Histogram, Span } from '@opentelemetry/api';
|
|
8
8
|
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
|
@@ -498,7 +498,7 @@ declare class CollectionController {
|
|
|
498
498
|
}, 403, "json">) | (Response & hono.TypedResponse<{
|
|
499
499
|
message: string;
|
|
500
500
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
501
|
-
[x: string]:
|
|
501
|
+
[x: string]: any;
|
|
502
502
|
}, hono_utils_http_status.ContentfulStatusCode, "json">)>;
|
|
503
503
|
}
|
|
504
504
|
|
package/dist/server.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
touchAuthSession,
|
|
31
31
|
transitionWorkflow,
|
|
32
32
|
verifyCollectionToken
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-TYPDQTE2.js";
|
|
34
34
|
import {
|
|
35
35
|
attachRequestMetrics,
|
|
36
36
|
bindObservabilityRuntime,
|
|
@@ -2200,9 +2200,23 @@ var CollectionController = class {
|
|
|
2200
2200
|
400
|
|
2201
2201
|
);
|
|
2202
2202
|
}
|
|
2203
|
+
let rawAggregates;
|
|
2204
|
+
let groupBy;
|
|
2205
|
+
if ("aggregates" in body && typeof body.aggregates === "object" && body.aggregates !== null && !Array.isArray(body.aggregates)) {
|
|
2206
|
+
rawAggregates = body.aggregates;
|
|
2207
|
+
if (typeof body.groupBy === "string" && body.groupBy.trim()) {
|
|
2208
|
+
groupBy = body.groupBy.trim();
|
|
2209
|
+
}
|
|
2210
|
+
} else {
|
|
2211
|
+
rawAggregates = body;
|
|
2212
|
+
const queryGroupBy = c.req.query("groupBy");
|
|
2213
|
+
if (typeof queryGroupBy === "string" && queryGroupBy.trim()) {
|
|
2214
|
+
groupBy = queryGroupBy.trim();
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2203
2217
|
const { sanitizeWhereClause } = await import("./where-sanitizer-3MNFMYA2.js");
|
|
2204
2218
|
const sanitizedAggregates = {};
|
|
2205
|
-
for (const [key, op] of Object.entries(
|
|
2219
|
+
for (const [key, op] of Object.entries(rawAggregates)) {
|
|
2206
2220
|
if (!op || typeof op !== "object" || Array.isArray(op)) {
|
|
2207
2221
|
return c.json(
|
|
2208
2222
|
{
|
|
@@ -2227,7 +2241,8 @@ var CollectionController = class {
|
|
|
2227
2241
|
}
|
|
2228
2242
|
const result = await db.aggregate({
|
|
2229
2243
|
collection: this.collection.slug,
|
|
2230
|
-
aggregates: sanitizedAggregates
|
|
2244
|
+
aggregates: sanitizedAggregates,
|
|
2245
|
+
...groupBy ? { groupBy } : {}
|
|
2231
2246
|
});
|
|
2232
2247
|
return c.json(result);
|
|
2233
2248
|
}
|
|
@@ -4564,6 +4579,8 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
4564
4579
|
columns: view.columns,
|
|
4565
4580
|
sort: view.sort,
|
|
4566
4581
|
metrics: view.metrics,
|
|
4582
|
+
features: view.features,
|
|
4583
|
+
actionOrder: view.actionOrder,
|
|
4567
4584
|
actions: await Promise.all(
|
|
4568
4585
|
(view.actions || []).map(async (action) => ({
|
|
4569
4586
|
name: action.name,
|
|
@@ -4640,6 +4657,7 @@ function registerRoutes(app, config) {
|
|
|
4640
4657
|
auth: !!col.auth,
|
|
4641
4658
|
audit: !!col.audit,
|
|
4642
4659
|
drafts: !!col.drafts,
|
|
4660
|
+
defaultView: col.defaultView ?? col.admin?.defaultView,
|
|
4643
4661
|
views: await Promise.all((col.views || []).map((view) => serializeViewForApi(view, serializeAccess))),
|
|
4644
4662
|
admin: col.admin,
|
|
4645
4663
|
detail: serializeDetailForApi(col.detail),
|