@teamkeel/functions-runtime 0.396.8 → 0.397.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/package.json +3 -2
- package/src/File.js +3 -7
- package/src/TimePeriod.js +89 -0
- package/src/TimePeriod.test.js +148 -0
- package/src/applyWhereConditions.js +17 -0
- package/src/database.js +16 -15
- package/src/handleJob.js +3 -1
- package/src/handleRequest.js +7 -5
- package/src/handleSubscriber.js +3 -1
- package/src/index.d.ts +33 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamkeel/functions-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.397.1",
|
|
4
4
|
"description": "Internal package used by @teamkeel/sdk",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,5 +35,6 @@
|
|
|
35
35
|
"pg": "^8.11.3",
|
|
36
36
|
"traceparent": "^1.0.0",
|
|
37
37
|
"ws": "^8.17.1"
|
|
38
|
-
}
|
|
38
|
+
},
|
|
39
|
+
"packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
|
|
39
40
|
}
|
package/src/File.js
CHANGED
|
@@ -5,7 +5,7 @@ const {
|
|
|
5
5
|
} = require("@aws-sdk/client-s3");
|
|
6
6
|
const { fromEnv } = require("@aws-sdk/credential-providers");
|
|
7
7
|
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
|
8
|
-
const {
|
|
8
|
+
const { useDatabase } = require("./database");
|
|
9
9
|
const { DatabaseError } = require("./errors");
|
|
10
10
|
const KSUID = require("ksuid");
|
|
11
11
|
|
|
@@ -126,7 +126,7 @@ class File extends InlineFile {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// default to db storage
|
|
129
|
-
const db =
|
|
129
|
+
const db = useDatabase();
|
|
130
130
|
|
|
131
131
|
try {
|
|
132
132
|
let query = db
|
|
@@ -138,8 +138,6 @@ class File extends InlineFile {
|
|
|
138
138
|
return row.data;
|
|
139
139
|
} catch (e) {
|
|
140
140
|
throw new DatabaseError(e);
|
|
141
|
-
} finally {
|
|
142
|
-
await db.destroy();
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
143
|
|
|
@@ -241,7 +239,7 @@ async function storeFile(contents, key, filename, contentType, expires) {
|
|
|
241
239
|
throw error;
|
|
242
240
|
}
|
|
243
241
|
} else {
|
|
244
|
-
const db =
|
|
242
|
+
const db = useDatabase();
|
|
245
243
|
|
|
246
244
|
try {
|
|
247
245
|
let query = db
|
|
@@ -267,8 +265,6 @@ async function storeFile(contents, key, filename, contentType, expires) {
|
|
|
267
265
|
await query.execute();
|
|
268
266
|
} catch (e) {
|
|
269
267
|
throw new DatabaseError(e);
|
|
270
|
-
} finally {
|
|
271
|
-
await db.destroy();
|
|
272
268
|
}
|
|
273
269
|
}
|
|
274
270
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
class TimePeriod {
|
|
2
|
+
constructor(period = "", value = 0, offset = 0, complete = false) {
|
|
3
|
+
this.period = period;
|
|
4
|
+
this.value = value;
|
|
5
|
+
this.offset = offset;
|
|
6
|
+
this.complete = complete;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static fromExpression(expression) {
|
|
10
|
+
// Regex pattern
|
|
11
|
+
const pattern =
|
|
12
|
+
/^(this|next|last)?\s*(\d+)?\s*(complete)?\s*(second|minute|hour|day|week|month|year|seconds|minutes|hours|days|weeks|months|years)?$/i;
|
|
13
|
+
|
|
14
|
+
const shorthandPattern = /^(now|today|tomorrow|yesterday)$/i;
|
|
15
|
+
|
|
16
|
+
const shorthandMatch = shorthandPattern.exec(expression.trim());
|
|
17
|
+
if (shorthandMatch) {
|
|
18
|
+
const shorthand = shorthandMatch[1].toLowerCase();
|
|
19
|
+
switch (shorthand) {
|
|
20
|
+
case "now":
|
|
21
|
+
return new TimePeriod();
|
|
22
|
+
case "today":
|
|
23
|
+
return TimePeriod.fromExpression("this day");
|
|
24
|
+
case "tomorrow":
|
|
25
|
+
return TimePeriod.fromExpression("next complete day");
|
|
26
|
+
case "yesterday":
|
|
27
|
+
return TimePeriod.fromExpression("last complete day");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const match = pattern.exec(expression.trim());
|
|
32
|
+
if (!match) {
|
|
33
|
+
throw new Error("Invalid time period expression");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [, direction, rawValue, isComplete, rawPeriod] = match;
|
|
37
|
+
|
|
38
|
+
let period = rawPeriod ? rawPeriod.toLowerCase().replace(/s$/, "") : "";
|
|
39
|
+
let value = rawValue ? parseInt(rawValue, 10) : 1;
|
|
40
|
+
let complete = Boolean(isComplete);
|
|
41
|
+
let offset = 0;
|
|
42
|
+
|
|
43
|
+
switch (direction?.toLowerCase()) {
|
|
44
|
+
case "this":
|
|
45
|
+
offset = 0;
|
|
46
|
+
complete = true;
|
|
47
|
+
break;
|
|
48
|
+
case "next":
|
|
49
|
+
offset = complete ? 1 : 0;
|
|
50
|
+
break;
|
|
51
|
+
case "last":
|
|
52
|
+
offset = -value;
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
throw new Error(
|
|
56
|
+
"Time period expression must start with this, next, or last"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return new TimePeriod(period, value, offset, complete);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
periodStartSQL() {
|
|
64
|
+
let sql = "NOW()";
|
|
65
|
+
if (this.offset !== 0) {
|
|
66
|
+
sql = `${sql} + INTERVAL '${this.offset} ${this.period}'`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this.complete) {
|
|
70
|
+
sql = `DATE_TRUNC('${this.period}', ${sql})`;
|
|
71
|
+
} else {
|
|
72
|
+
sql = `(${sql})`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return sql;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
periodEndSQL() {
|
|
79
|
+
let sql = this.periodStartSQL();
|
|
80
|
+
if (this.value != 0) {
|
|
81
|
+
sql = `(${sql} + INTERVAL '${this.value} ${this.period}')`;
|
|
82
|
+
}
|
|
83
|
+
return sql;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
TimePeriod,
|
|
89
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
const { TimePeriod } = require("./TimePeriod");
|
|
3
|
+
|
|
4
|
+
test("shorthands test", async () => {
|
|
5
|
+
const today = TimePeriod.fromExpression("today");
|
|
6
|
+
expect(today).toEqual({
|
|
7
|
+
period: "day",
|
|
8
|
+
value: 1,
|
|
9
|
+
complete: true,
|
|
10
|
+
offset: 0,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const tomorrow = TimePeriod.fromExpression("tomorrow");
|
|
14
|
+
expect(tomorrow).toEqual({
|
|
15
|
+
period: "day",
|
|
16
|
+
value: 1,
|
|
17
|
+
complete: true,
|
|
18
|
+
offset: 1,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const yesterday = TimePeriod.fromExpression("yesterday");
|
|
22
|
+
expect(yesterday).toEqual({
|
|
23
|
+
period: "day",
|
|
24
|
+
value: 1,
|
|
25
|
+
complete: true,
|
|
26
|
+
offset: -1,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const now = TimePeriod.fromExpression("now");
|
|
30
|
+
expect(now).toEqual({
|
|
31
|
+
period: "",
|
|
32
|
+
value: 0,
|
|
33
|
+
complete: false,
|
|
34
|
+
offset: 0,
|
|
35
|
+
});
|
|
36
|
+
expect(now.periodStartSQL()).toEqual("(NOW())");
|
|
37
|
+
expect(now.periodEndSQL()).toEqual("(NOW())");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("next test", async () => {
|
|
41
|
+
let period = TimePeriod.fromExpression("next day");
|
|
42
|
+
expect(period).toEqual({
|
|
43
|
+
period: "day",
|
|
44
|
+
value: 1,
|
|
45
|
+
complete: false,
|
|
46
|
+
offset: 0,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
period = TimePeriod.fromExpression("next complete day");
|
|
50
|
+
expect(period).toEqual({
|
|
51
|
+
period: "day",
|
|
52
|
+
value: 1,
|
|
53
|
+
complete: true,
|
|
54
|
+
offset: 1,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
period = TimePeriod.fromExpression("next 5 complete day");
|
|
58
|
+
expect(period).toEqual({
|
|
59
|
+
period: "day",
|
|
60
|
+
value: 5,
|
|
61
|
+
complete: true,
|
|
62
|
+
offset: 1,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
period = TimePeriod.fromExpression("next 5 months");
|
|
66
|
+
expect(period).toEqual({
|
|
67
|
+
period: "month",
|
|
68
|
+
value: 5,
|
|
69
|
+
complete: false,
|
|
70
|
+
offset: 0,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
period = TimePeriod.fromExpression("next 2 complete years");
|
|
74
|
+
expect(period).toEqual({
|
|
75
|
+
period: "year",
|
|
76
|
+
value: 2,
|
|
77
|
+
complete: true,
|
|
78
|
+
offset: 1,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("last test", async () => {
|
|
83
|
+
let period = TimePeriod.fromExpression("last day");
|
|
84
|
+
expect(period).toEqual({
|
|
85
|
+
period: "day",
|
|
86
|
+
value: 1,
|
|
87
|
+
complete: false,
|
|
88
|
+
offset: -1,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
period = TimePeriod.fromExpression("last complete day");
|
|
92
|
+
expect(period).toEqual({
|
|
93
|
+
period: "day",
|
|
94
|
+
value: 1,
|
|
95
|
+
complete: true,
|
|
96
|
+
offset: -1,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
period = TimePeriod.fromExpression("last 5 complete day");
|
|
100
|
+
expect(period).toEqual({
|
|
101
|
+
period: "day",
|
|
102
|
+
value: 5,
|
|
103
|
+
complete: true,
|
|
104
|
+
offset: -5,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
period = TimePeriod.fromExpression("last 5 months");
|
|
108
|
+
expect(period).toEqual({
|
|
109
|
+
period: "month",
|
|
110
|
+
value: 5,
|
|
111
|
+
complete: false,
|
|
112
|
+
offset: -5,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
period = TimePeriod.fromExpression("last 2 complete years");
|
|
116
|
+
expect(period).toEqual({
|
|
117
|
+
period: "year",
|
|
118
|
+
value: 2,
|
|
119
|
+
complete: true,
|
|
120
|
+
offset: -2,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
period = TimePeriod.fromExpression("last complete year");
|
|
124
|
+
expect(period).toEqual({
|
|
125
|
+
period: "year",
|
|
126
|
+
value: 1,
|
|
127
|
+
complete: true,
|
|
128
|
+
offset: -1,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("errors test", async () => {
|
|
133
|
+
expect(() => TimePeriod.fromExpression("last test day")).toThrowError(
|
|
134
|
+
"Invalid time period expression"
|
|
135
|
+
);
|
|
136
|
+
expect(() => TimePeriod.fromExpression("today now")).toThrowError(
|
|
137
|
+
"Invalid time period expression"
|
|
138
|
+
);
|
|
139
|
+
expect(() => TimePeriod.fromExpression("last -5 days")).toThrowError(
|
|
140
|
+
"Invalid time period expression"
|
|
141
|
+
);
|
|
142
|
+
expect(() => TimePeriod.fromExpression("5 mont")).toThrowError(
|
|
143
|
+
"Invalid time period expression"
|
|
144
|
+
);
|
|
145
|
+
expect(() => TimePeriod.fromExpression("5 days")).toThrowError(
|
|
146
|
+
"Time period expression must start with this, next, or last"
|
|
147
|
+
);
|
|
148
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { sql, Kysely } = require("kysely");
|
|
2
2
|
const { snakeCase } = require("./casing");
|
|
3
|
+
const { TimePeriod } = require("./TimePeriod");
|
|
3
4
|
|
|
4
5
|
const opMapping = {
|
|
5
6
|
startsWith: { op: "like", value: (v) => `${v}%` },
|
|
@@ -16,6 +17,22 @@ const opMapping = {
|
|
|
16
17
|
onOrAfter: { op: ">=" },
|
|
17
18
|
equals: { op: sql`is not distinct from` },
|
|
18
19
|
notEquals: { op: sql`is distinct from` },
|
|
20
|
+
equalsRelative: {
|
|
21
|
+
op: sql`BETWEEN`,
|
|
22
|
+
value: (v) =>
|
|
23
|
+
sql`${sql.raw(
|
|
24
|
+
TimePeriod.fromExpression(v).periodStartSQL()
|
|
25
|
+
)} AND ${sql.raw(TimePeriod.fromExpression(v).periodEndSQL())}`,
|
|
26
|
+
},
|
|
27
|
+
beforeRelative: {
|
|
28
|
+
op: "<",
|
|
29
|
+
value: (v) =>
|
|
30
|
+
sql`${sql.raw(TimePeriod.fromExpression(v).periodStartSQL())}`,
|
|
31
|
+
},
|
|
32
|
+
afterRelative: {
|
|
33
|
+
op: ">=",
|
|
34
|
+
value: (v) => sql`${sql.raw(TimePeriod.fromExpression(v).periodEndSQL())}`,
|
|
35
|
+
},
|
|
19
36
|
any: {
|
|
20
37
|
isArrayQuery: true,
|
|
21
38
|
greaterThan: { op: ">" },
|
package/src/database.js
CHANGED
|
@@ -5,6 +5,7 @@ const { AuditContextPlugin } = require("./auditing");
|
|
|
5
5
|
const pg = require("pg");
|
|
6
6
|
const { withSpan } = require("./tracing");
|
|
7
7
|
const ws = require("ws");
|
|
8
|
+
const fs = require("node:fs");
|
|
8
9
|
|
|
9
10
|
// withDatabase is responsible for setting the correct database client in our AsyncLocalStorage
|
|
10
11
|
// so that the the code in a custom function uses the correct client.
|
|
@@ -65,9 +66,9 @@ function useDatabase() {
|
|
|
65
66
|
// createDatabaseClient will return a brand new instance of Kysely. Every instance of Kysely
|
|
66
67
|
// represents an individual connection to the database.
|
|
67
68
|
// not to be exported externally from our sdk - consumers should use useDatabase
|
|
68
|
-
function createDatabaseClient() {
|
|
69
|
+
function createDatabaseClient({ connString } = {}) {
|
|
69
70
|
const db = new Kysely({
|
|
70
|
-
dialect: getDialect(),
|
|
71
|
+
dialect: getDialect(connString),
|
|
71
72
|
plugins: [
|
|
72
73
|
// ensures that the audit context data is written to Postgres configuration parameters
|
|
73
74
|
new AuditContextPlugin(),
|
|
@@ -139,8 +140,8 @@ class InstrumentedClient extends pg.Client {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
function getDialect() {
|
|
143
|
-
const dbConnType = process.env
|
|
143
|
+
function getDialect(connString) {
|
|
144
|
+
const dbConnType = process.env.KEEL_DB_CONN_TYPE;
|
|
144
145
|
switch (dbConnType) {
|
|
145
146
|
case "pg":
|
|
146
147
|
// Adding a custom type parser for numeric fields: see https://kysely.dev/docs/recipes/data-types#configuring-runtime-javascript-types
|
|
@@ -165,7 +166,14 @@ function getDialect() {
|
|
|
165
166
|
// time is takes for a lambda to freeze (which is not a constant, but could be as short as several minutes,
|
|
166
167
|
// https://www.pluralsight.com/resources/blog/cloud/how-long-does-aws-lambda-keep-your-idle-functions-around-before-a-cold-start)
|
|
167
168
|
idleTimeoutMillis: 50000,
|
|
168
|
-
|
|
169
|
+
|
|
170
|
+
// If connString is not passed fall back to reading from env var
|
|
171
|
+
connectionString: connString || process.env.KEEL_DB_CONN,
|
|
172
|
+
|
|
173
|
+
// Allow the setting of a cert (.pem) file. RDS requires this to enforce SSL.
|
|
174
|
+
...(process.env.KEEL_DB_CERT
|
|
175
|
+
? { ssl: { ca: fs.readFileSync(process.env.KEEL_DB_CERT) } }
|
|
176
|
+
: undefined),
|
|
169
177
|
}),
|
|
170
178
|
});
|
|
171
179
|
case "neon":
|
|
@@ -178,7 +186,8 @@ function getDialect() {
|
|
|
178
186
|
neonserverless.neonConfig.webSocketConstructor = ws;
|
|
179
187
|
|
|
180
188
|
const pool = new InstrumentedNeonServerlessPool({
|
|
181
|
-
|
|
189
|
+
// If connString is not passed fall back to reading from env var
|
|
190
|
+
connectionString: connString || process.env.KEEL_DB_CONN,
|
|
182
191
|
});
|
|
183
192
|
|
|
184
193
|
pool.on("connect", (client) => {
|
|
@@ -196,7 +205,7 @@ function getDialect() {
|
|
|
196
205
|
return withSpan(spanName, function (span) {
|
|
197
206
|
if (sqlAttribute) {
|
|
198
207
|
span.setAttribute("sql", args[0]);
|
|
199
|
-
span.setAttribute("dialect",
|
|
208
|
+
span.setAttribute("dialect", dbConnType);
|
|
200
209
|
}
|
|
201
210
|
return originalQuery.apply(client, args);
|
|
202
211
|
});
|
|
@@ -211,14 +220,6 @@ function getDialect() {
|
|
|
211
220
|
}
|
|
212
221
|
}
|
|
213
222
|
|
|
214
|
-
function mustEnv(key) {
|
|
215
|
-
const v = process.env[key];
|
|
216
|
-
if (!v) {
|
|
217
|
-
throw new Error(`expected environment variable ${key} to be set`);
|
|
218
|
-
}
|
|
219
|
-
return v;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
223
|
module.exports = {
|
|
223
224
|
createDatabaseClient,
|
|
224
225
|
useDatabase,
|
package/src/handleJob.js
CHANGED
|
@@ -53,7 +53,9 @@ async function handleJob(request, config) {
|
|
|
53
53
|
? true
|
|
54
54
|
: null;
|
|
55
55
|
|
|
56
|
-
db = createDatabaseClient(
|
|
56
|
+
db = createDatabaseClient({
|
|
57
|
+
connString: request.meta?.secrets?.KEEL_DB_CONN,
|
|
58
|
+
});
|
|
57
59
|
const jobFunction = jobs[request.method];
|
|
58
60
|
const actionType = PROTO_ACTION_TYPES.JOB;
|
|
59
61
|
|
package/src/handleRequest.js
CHANGED
|
@@ -64,7 +64,9 @@ async function handleRequest(request, config) {
|
|
|
64
64
|
? true
|
|
65
65
|
: null;
|
|
66
66
|
|
|
67
|
-
db = createDatabaseClient(
|
|
67
|
+
db = createDatabaseClient({
|
|
68
|
+
connString: request.meta?.secrets?.KEEL_DB_CONN,
|
|
69
|
+
});
|
|
68
70
|
const customFunction = functions[request.method];
|
|
69
71
|
const actionType = actionTypes[request.method];
|
|
70
72
|
|
|
@@ -87,7 +89,9 @@ async function handleRequest(request, config) {
|
|
|
87
89
|
// Return the custom function to the containing tryExecuteFunction block
|
|
88
90
|
// Once the custom function is called, tryExecuteFunction will check the schema's permission rules to see if it can continue committing
|
|
89
91
|
// the transaction to the db. If a permission rule is violated, any changes made inside the transaction are rolled back.
|
|
90
|
-
|
|
92
|
+
const result = await customFunction(ctx, inputs);
|
|
93
|
+
|
|
94
|
+
return parseOutputs(result);
|
|
91
95
|
}
|
|
92
96
|
);
|
|
93
97
|
|
|
@@ -100,9 +104,7 @@ async function handleRequest(request, config) {
|
|
|
100
104
|
return errorToJSONRPCResponse(request, result);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
const response = createJSONRPCSuccessResponse(request.id, parsed);
|
|
107
|
+
const response = createJSONRPCSuccessResponse(request.id, result);
|
|
106
108
|
|
|
107
109
|
const responseHeaders = {};
|
|
108
110
|
for (const pair of headers.entries()) {
|
package/src/handleSubscriber.js
CHANGED
|
@@ -48,7 +48,9 @@ async function handleSubscriber(request, config) {
|
|
|
48
48
|
meta: request.meta,
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
db = createDatabaseClient(
|
|
51
|
+
db = createDatabaseClient({
|
|
52
|
+
connString: request.meta?.secrets?.KEEL_DB_CONN,
|
|
53
|
+
});
|
|
52
54
|
const subscriberFunction = subscribers[request.method];
|
|
53
55
|
const actionType = PROTO_ACTION_TYPES.SUBSCRIBER;
|
|
54
56
|
|
package/src/index.d.ts
CHANGED
|
@@ -29,9 +29,12 @@ export type NumberWhereCondition = {
|
|
|
29
29
|
|
|
30
30
|
export type DateWhereCondition = {
|
|
31
31
|
equals?: Date | string | null;
|
|
32
|
+
equalsRelative?: RelativeDateString | null;
|
|
32
33
|
before?: Date | string | null;
|
|
34
|
+
beforeRelative?: RelativeDateString | null;
|
|
33
35
|
onOrBefore?: Date | string | null;
|
|
34
36
|
after?: Date | string | null;
|
|
37
|
+
afterRelative?: RelativeDateString | null;
|
|
35
38
|
onOrAfter?: Date | string | null;
|
|
36
39
|
};
|
|
37
40
|
|
|
@@ -46,6 +49,9 @@ export type DateQueryInput = {
|
|
|
46
49
|
export type TimestampQueryInput = {
|
|
47
50
|
before: string | null;
|
|
48
51
|
after: string | null;
|
|
52
|
+
equalsRelative?: RelativeDateString | null;
|
|
53
|
+
beforeRelative?: RelativeDateString | null;
|
|
54
|
+
afterRelative?: RelativeDateString | null;
|
|
49
55
|
};
|
|
50
56
|
|
|
51
57
|
export type StringArrayWhereCondition = {
|
|
@@ -239,3 +245,30 @@ export type FunctionConfig = {
|
|
|
239
245
|
export type FuncWithConfig<T> = T & {
|
|
240
246
|
config: FunctionConfig;
|
|
241
247
|
};
|
|
248
|
+
|
|
249
|
+
type unit =
|
|
250
|
+
| "year"
|
|
251
|
+
| "years"
|
|
252
|
+
| "month"
|
|
253
|
+
| "months"
|
|
254
|
+
| "day"
|
|
255
|
+
| "days"
|
|
256
|
+
| "hour"
|
|
257
|
+
| "hours"
|
|
258
|
+
| "minute"
|
|
259
|
+
| "minutes"
|
|
260
|
+
| "second"
|
|
261
|
+
| "seconds";
|
|
262
|
+
type direction = "next" | "last";
|
|
263
|
+
type completed = "complete";
|
|
264
|
+
type value = number;
|
|
265
|
+
|
|
266
|
+
export type RelativeDateString =
|
|
267
|
+
| "now"
|
|
268
|
+
| "today"
|
|
269
|
+
| "tomorrow"
|
|
270
|
+
| "yesterday"
|
|
271
|
+
| `this ${unit}`
|
|
272
|
+
| `${direction} ${unit}`
|
|
273
|
+
| `${direction} ${value} ${unit}`
|
|
274
|
+
| `${direction} ${value} ${completed} ${unit}`;
|