@protobase/server 0.23.0 → 0.23.1
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/aggregate-groups.js +1 -1
- package/dist/aggregate-service.js +14 -11
- package/dist/range-ends.js +29 -0
- package/package.json +4 -4
package/dist/aggregate-groups.js
CHANGED
|
@@ -104,7 +104,7 @@ const aggregate = async (deps, entry, session, input) => {
|
|
|
104
104
|
if (dated) {
|
|
105
105
|
const granularity = params.granularity ?? "day";
|
|
106
106
|
const timeZone = params.time_zone ?? "UTC";
|
|
107
|
-
const range = await seriesRange(deps, access, params.group_by, params, scope);
|
|
107
|
+
const range = await seriesRange(deps, access, params.group_by, params, timeZone, scope);
|
|
108
108
|
if (params.split_by !== void 0) {
|
|
109
109
|
const split = {
|
|
110
110
|
by: params.split_by,
|
|
@@ -5,6 +5,7 @@ import { columnId, filterCondition, scopeConditions, tableId, whereClause } from
|
|
|
5
5
|
import { readTransaction } from "./transactions.js";
|
|
6
6
|
import { parseParams } from "./params.js";
|
|
7
7
|
import { labelRequests, relatedLabels } from "./related-labels.js";
|
|
8
|
+
import { rangeEnd, rangeStart } from "./range-ends.js";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
import { sql } from "kysely";
|
|
10
11
|
import { facetCounts, histogram, series, withoutField } from "@protobase/query";
|
|
@@ -103,22 +104,24 @@ const earliest = async (deps, access, fieldName, scope) => {
|
|
|
103
104
|
return (await readTransaction(deps.db, deps.statementTimeoutMs, (trx) => sql`select min(${columnId(target)})::text as lowest from ${tableId(access.full)} as t${whereClause(conditions)}`.execute(trx))).rows[0]?.lowest ?? void 0;
|
|
104
105
|
};
|
|
105
106
|
const bound = (model, fieldName, value) => model.fields[fieldName]?.type === "date" ? value.slice(0, 10) : value;
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
/**
|
|
108
|
+
* The span a series of `field` covers: `from` and `to`, else the `range` up to now, `all` from the earliest row. A
|
|
109
|
+
* relative range of a date field runs over the days of `timeZone`, the viewer's today included.
|
|
110
|
+
*/
|
|
111
|
+
const seriesRange = async (deps, access, field, params, timeZone, scope) => {
|
|
109
112
|
const { model } = access;
|
|
113
|
+
const type = model.fields[field]?.type;
|
|
114
|
+
const now = Date.now();
|
|
115
|
+
const to = params.to ?? rangeEnd(type, now, timeZone);
|
|
110
116
|
if (params.from) return {
|
|
111
117
|
from: bound(model, field, params.from),
|
|
112
118
|
to: bound(model, field, to)
|
|
113
119
|
};
|
|
114
120
|
const range = params.range ?? "30d";
|
|
115
|
-
if (range !== "all") {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
to: bound(model, field, to)
|
|
120
|
-
};
|
|
121
|
-
}
|
|
121
|
+
if (range !== "all") return {
|
|
122
|
+
from: rangeStart(type, now - rangeDays[range] * 864e5, timeZone),
|
|
123
|
+
to: bound(model, field, to)
|
|
124
|
+
};
|
|
122
125
|
const lowest = await earliest(deps, access, field, scope);
|
|
123
126
|
return lowest ? {
|
|
124
127
|
from: bound(model, field, lowest),
|
|
@@ -133,8 +136,8 @@ const timeSeries = async (deps, entry, session, input) => {
|
|
|
133
136
|
parameter: "field",
|
|
134
137
|
message: "Unknown field"
|
|
135
138
|
}] });
|
|
136
|
-
const range = await seriesRange(deps, access, params.field, params, scope);
|
|
137
139
|
const timeZone = params.time_zone ?? "UTC";
|
|
140
|
+
const range = await seriesRange(deps, access, params.field, params, timeZone, scope);
|
|
138
141
|
const points = range ? await readTransaction(deps.db, deps.statementTimeoutMs, (trx) => series(trx, model, params.field, {
|
|
139
142
|
range,
|
|
140
143
|
granularity: params.granularity,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { badRequest } from "./problem.js";
|
|
2
|
+
//#region src/range-ends.ts
|
|
3
|
+
const dayMs = 864e5;
|
|
4
|
+
/** The day an instant falls on in `zone`, as YYYY-MM-DD; an unknown zone is a bad request. */
|
|
5
|
+
const dayIn = (ms, zone) => {
|
|
6
|
+
try {
|
|
7
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
8
|
+
timeZone: zone,
|
|
9
|
+
year: "numeric",
|
|
10
|
+
month: "2-digit",
|
|
11
|
+
day: "2-digit"
|
|
12
|
+
}).format(ms);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error instanceof RangeError) throw badRequest("invalid-parameter", `Unknown time zone "${zone}"`, { errors: [{
|
|
15
|
+
parameter: "time_zone",
|
|
16
|
+
message: "Unknown time zone"
|
|
17
|
+
}] });
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/** Where a range starting at instant `ms` starts: that instant, or for a date the viewer's day of it. */
|
|
22
|
+
const rangeStart = (type, ms, zone) => type === "date" ? dayIn(ms, zone) : new Date(ms).toISOString();
|
|
23
|
+
/** Where a range up to `now` ends, exclusive: that instant, or for a date the day after the viewer's today. */
|
|
24
|
+
const rangeEnd = (type, now, zone) => {
|
|
25
|
+
if (type !== "date") return new Date(now).toISOString();
|
|
26
|
+
return new Date(Date.parse(dayIn(now, zone)) + dayMs).toISOString().slice(0, 10);
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { dayIn, rangeEnd, rangeStart };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@better-auth/passkey": "^1.7.7",
|
|
33
33
|
"@better-auth/sso": "^1.7.7",
|
|
34
|
-
"@protobase/layout": "0.23.
|
|
35
|
-
"@protobase/query": "0.23.
|
|
36
|
-
"@protobase/schema": "0.23.
|
|
34
|
+
"@protobase/layout": "0.23.1",
|
|
35
|
+
"@protobase/query": "0.23.1",
|
|
36
|
+
"@protobase/schema": "0.23.1",
|
|
37
37
|
"@react-email/render": "^2.1.0",
|
|
38
38
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
39
39
|
"@sentry/core": "^11.7.0",
|