@teamkeel/functions-runtime 0.411.0 → 0.412.0-next.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.
Files changed (49) hide show
  1. package/dist/index.d.mts +340 -0
  2. package/dist/index.d.ts +340 -0
  3. package/dist/index.js +3093 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +3097 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +23 -5
  8. package/.env.test +0 -2
  9. package/compose.yaml +0 -10
  10. package/src/Duration.js +0 -40
  11. package/src/Duration.test.js +0 -34
  12. package/src/File.js +0 -295
  13. package/src/ModelAPI.js +0 -377
  14. package/src/ModelAPI.test.js +0 -1428
  15. package/src/QueryBuilder.js +0 -184
  16. package/src/QueryContext.js +0 -90
  17. package/src/RequestHeaders.js +0 -21
  18. package/src/TimePeriod.js +0 -89
  19. package/src/TimePeriod.test.js +0 -148
  20. package/src/applyAdditionalQueryConstraints.js +0 -22
  21. package/src/applyJoins.js +0 -67
  22. package/src/applyWhereConditions.js +0 -124
  23. package/src/auditing.js +0 -110
  24. package/src/auditing.test.js +0 -330
  25. package/src/camelCasePlugin.js +0 -52
  26. package/src/casing.js +0 -54
  27. package/src/casing.test.js +0 -56
  28. package/src/consts.js +0 -14
  29. package/src/database.js +0 -244
  30. package/src/errors.js +0 -160
  31. package/src/handleJob.js +0 -110
  32. package/src/handleJob.test.js +0 -270
  33. package/src/handleRequest.js +0 -153
  34. package/src/handleRequest.test.js +0 -463
  35. package/src/handleRoute.js +0 -112
  36. package/src/handleSubscriber.js +0 -105
  37. package/src/index.d.ts +0 -317
  38. package/src/index.js +0 -38
  39. package/src/parsing.js +0 -113
  40. package/src/parsing.test.js +0 -140
  41. package/src/permissions.js +0 -77
  42. package/src/permissions.test.js +0 -118
  43. package/src/tracing.js +0 -184
  44. package/src/tracing.test.js +0 -147
  45. package/src/tryExecuteFunction.js +0 -91
  46. package/src/tryExecuteJob.js +0 -29
  47. package/src/tryExecuteSubscriber.js +0 -17
  48. package/src/type-utils.js +0 -18
  49. package/vite.config.js +0 -7
@@ -1,184 +0,0 @@
1
- const { applyWhereConditions } = require("./applyWhereConditions");
2
- const {
3
- applyLimit,
4
- applyOffset,
5
- applyOrderBy,
6
- } = require("./applyAdditionalQueryConstraints");
7
- const { applyJoins } = require("./applyJoins");
8
- const {
9
- camelCaseObject,
10
- snakeCaseObject,
11
- upperCamelCase,
12
- } = require("./casing");
13
- const { useDatabase } = require("./database");
14
- const { transformRichDataTypes } = require("./parsing");
15
- const { QueryContext } = require("./QueryContext");
16
- const tracing = require("./tracing");
17
- const { DatabaseError } = require("./errors");
18
-
19
- class QueryBuilder {
20
- /**
21
- * @param {string} tableName
22
- * @param {import("./QueryContext").QueryContext} context
23
- * @param {import("kysely").Kysely} db
24
- */
25
- constructor(tableName, context, db) {
26
- this._tableName = tableName;
27
- this._context = context;
28
- this._db = db;
29
- this._modelName = upperCamelCase(this._tableName);
30
- }
31
-
32
- where(where) {
33
- const context = this._context.clone();
34
-
35
- let builder = applyJoins(context, this._db, where);
36
- builder = applyWhereConditions(context, builder, where);
37
-
38
- return new QueryBuilder(this._tableName, context, builder);
39
- }
40
-
41
- sql() {
42
- return this._db.compile().sql;
43
- }
44
-
45
- async update(values) {
46
- const name = tracing.spanNameForModelAPI(this._modelName, "update");
47
- const db = useDatabase();
48
-
49
- return tracing.withSpan(name, async (span) => {
50
- // we build a sub-query to add to the WHERE id IN (XXX) containing all of the
51
- // wheres added in previous .where() chains.
52
- const sub = this._db.clearSelect().select("id");
53
-
54
- const query = db
55
- .updateTable(this._tableName)
56
- .set(snakeCaseObject(values))
57
- .returningAll()
58
- .where("id", "in", sub);
59
-
60
- try {
61
- const result = await query.execute();
62
- const numUpdatedRows = result.length;
63
-
64
- // the double (==) is important because we are comparing bigint to int
65
- if (numUpdatedRows == 0) {
66
- return null;
67
- }
68
-
69
- if (numUpdatedRows > 1) {
70
- throw new DatabaseError(
71
- new Error(
72
- "more than one row matched update constraints - only unique fields should be used when updating."
73
- )
74
- );
75
- }
76
-
77
- return transformRichDataTypes(camelCaseObject(result[0]));
78
- } catch (e) {
79
- throw new DatabaseError(e);
80
- }
81
- });
82
- }
83
-
84
- async delete() {
85
- const name = tracing.spanNameForModelAPI(this._modelName, "delete");
86
- const db = useDatabase();
87
-
88
- return tracing.withSpan(name, async (span) => {
89
- // the original query selects the distinct id + the model.* so we need to clear
90
- const sub = this._db.clearSelect().select("id");
91
- let builder = db.deleteFrom(this._tableName).where("id", "in", sub);
92
-
93
- const query = builder.returning(["id"]);
94
-
95
- // final query looks something like:
96
- // delete from "person" where "id" in (select distinct on ("person"."id") "id" from "person" where "person"."id" = $1) returning "id"
97
-
98
- span.setAttribute("sql", query.compile().sql);
99
-
100
- try {
101
- const row = await query.executeTakeFirstOrThrow();
102
- return row.id;
103
- } catch (e) {
104
- throw new DatabaseError(e);
105
- }
106
- });
107
- }
108
-
109
- async findOne() {
110
- const name = tracing.spanNameForModelAPI(this._modelName, "findOne");
111
- const db = useDatabase();
112
-
113
- return tracing.withSpan(name, async (span) => {
114
- let builder = db
115
- .selectFrom((qb) => {
116
- // this._db contains all of the where constraints and joins
117
- // we want to include that in the sub query in the same way we
118
- // add all of this information into the sub query in the ModelAPI's
119
- // implementation of findOne
120
- return this._db.as(this._tableName);
121
- })
122
- .selectAll();
123
-
124
- span.setAttribute("sql", builder.compile().sql);
125
-
126
- const row = await builder.executeTakeFirst();
127
- if (!row) {
128
- return null;
129
- }
130
-
131
- return transformRichDataTypes(camelCaseObject(row));
132
- });
133
- }
134
-
135
- async findMany(params) {
136
- const name = tracing.spanNameForModelAPI(this._modelName, "findMany");
137
- const db = useDatabase();
138
-
139
- return tracing.withSpan(name, async (span) => {
140
- const context = new QueryContext([this._tableName], this._tableConfigMap);
141
-
142
- let builder = db
143
- .selectFrom((qb) => {
144
- // this._db contains all of the where constraints and joins
145
- // we want to include that in the sub query in the same way we
146
- // add all of this information into the sub query in the ModelAPI's
147
- // implementation of findMany
148
- return this._db.as(this._tableName);
149
- })
150
- .selectAll();
151
-
152
- // The only constraints added to the main query are the orderBy, limit and offset as they are performed on the "outer" set
153
- if (params?.limit) {
154
- builder = applyLimit(context, builder, params.limit);
155
- }
156
-
157
- if (params?.offset) {
158
- builder = applyOffset(context, builder, params.offset);
159
- }
160
-
161
- if (
162
- params?.orderBy !== undefined &&
163
- Object.keys(params?.orderBy).length > 0
164
- ) {
165
- builder = applyOrderBy(
166
- context,
167
- builder,
168
- this._tableName,
169
- params.orderBy
170
- );
171
- } else {
172
- builder = builder.orderBy(`${this._tableName}.id`);
173
- }
174
-
175
- const query = builder;
176
-
177
- span.setAttribute("sql", query.compile().sql);
178
- const rows = await builder.execute();
179
- return rows.map((x) => transformRichDataTypes(camelCaseObject(x)));
180
- });
181
- }
182
- }
183
-
184
- module.exports.QueryBuilder = QueryBuilder;
@@ -1,90 +0,0 @@
1
- /**
2
- * QueryContext is used to store state about the current query, for example
3
- * which joins have already been applied. It is used by applyJoins and
4
- * applyWhereConditions to generate consistent table aliases for joins.
5
- *
6
- * This class has the concept of a "table path". This is just a list of tables, starting
7
- * with some "root" table and ending with the table we're currently joining to. So
8
- * for example if we started with a "product" table and joined from there to "order_item"
9
- * and then to "order" and then to "customer" the table path would be:
10
- * ["product", "order_item", "order", "customer"]
11
- * At this point the "current" table is "customer" and it's alias would be:
12
- * "product$order_item$order$customer"
13
- */
14
- class QueryContext {
15
- /**
16
- * @param {string[]} tablePath This is the path from the "root" table to the "current table".
17
- * @param {import("./ModelAPI").TableConfigMap} tableConfigMap
18
- * @param {string[]} joins
19
- */
20
- constructor(tablePath, tableConfigMap, joins = []) {
21
- this._tablePath = tablePath;
22
- this._tableConfigMap = tableConfigMap;
23
- this._joins = joins;
24
- }
25
-
26
- clone() {
27
- return new QueryContext([...this._tablePath], this._tableConfigMap, [
28
- ...this._joins,
29
- ]);
30
- }
31
-
32
- /**
33
- * Returns true if, given the current table path, a join to the given
34
- * table has already been added.
35
- * @param {string} table
36
- * @returns {boolean}
37
- */
38
- hasJoin(table) {
39
- const alias = joinAlias([...this._tablePath, table]);
40
- return this._joins.includes(alias);
41
- }
42
-
43
- /**
44
- * Adds table to the QueryContext's path and registers the join,
45
- * calls fn, then pops the table off the path.
46
- * @param {string} table
47
- * @param {Function} fn
48
- */
49
- withJoin(table, fn) {
50
- this._tablePath.push(table);
51
- this._joins.push(this.tableAlias());
52
-
53
- fn();
54
-
55
- // Don't change the _joins list, we want to remember those
56
- this._tablePath.pop();
57
- }
58
-
59
- /**
60
- * Returns the alias that will be used for the current table
61
- * @returns {string}
62
- */
63
- tableAlias() {
64
- return joinAlias(this._tablePath);
65
- }
66
-
67
- /**
68
- * Returns the current table name
69
- * @returns {string}
70
- */
71
- tableName() {
72
- return this._tablePath[this._tablePath.length - 1];
73
- }
74
-
75
- /**
76
- * Return the TableConfig for the current table
77
- * @returns {import("./ModelAPI").TableConfig | undefined}
78
- */
79
- tableConfig() {
80
- return this._tableConfigMap[this.tableName()];
81
- }
82
- }
83
-
84
- function joinAlias(tablePath) {
85
- return tablePath.join("$");
86
- }
87
-
88
- module.exports = {
89
- QueryContext,
90
- };
@@ -1,21 +0,0 @@
1
- class RequestHeaders {
2
- /**
3
- * @param {{Object.<string, string>}} requestHeaders Map of request headers submitted from the client
4
- */
5
-
6
- constructor(requestHeaders) {
7
- this._headers = new Headers(requestHeaders);
8
- }
9
-
10
- get(key) {
11
- return this._headers.get(key);
12
- }
13
-
14
- has(key) {
15
- return this._headers.has(key);
16
- }
17
- }
18
-
19
- module.exports = {
20
- RequestHeaders,
21
- };
package/src/TimePeriod.js DELETED
@@ -1,89 +0,0 @@
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
- };
@@ -1,148 +0,0 @@
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,22 +0,0 @@
1
- const { snakeCase } = require("change-case");
2
-
3
- function applyLimit(context, qb, limit) {
4
- return qb.limit(limit);
5
- }
6
-
7
- function applyOffset(context, qb, offset) {
8
- return qb.offset(offset);
9
- }
10
-
11
- function applyOrderBy(context, qb, tableName, orderBy = {}) {
12
- Object.entries(orderBy).forEach(([key, sortOrder]) => {
13
- qb = qb.orderBy(`${tableName}.${snakeCase(key)}`, sortOrder.toLowerCase());
14
- });
15
- return qb;
16
- }
17
-
18
- module.exports = {
19
- applyLimit,
20
- applyOffset,
21
- applyOrderBy,
22
- };
package/src/applyJoins.js DELETED
@@ -1,67 +0,0 @@
1
- const { snakeCase } = require("./casing");
2
-
3
- /**
4
- * Adds the joins required by the where conditions to the given
5
- * Kysely instance and returns the resulting new Kysely instance.
6
- * @param {import("./QueryContext").QueryContext} context
7
- * @param {import("kysely").Kysely} qb
8
- * @param {Object} where
9
- * @returns {import("kysely").Kysely}
10
- */
11
- function applyJoins(context, qb, where) {
12
- const conf = context.tableConfig();
13
- if (!conf) {
14
- return qb;
15
- }
16
-
17
- const srcTable = context.tableAlias();
18
-
19
- for (const key of Object.keys(where)) {
20
- const rel = conf[snakeCase(key)];
21
- if (!rel) {
22
- continue;
23
- }
24
-
25
- const targetTable = rel.referencesTable;
26
-
27
- if (context.hasJoin(targetTable)) {
28
- continue;
29
- }
30
-
31
- context.withJoin(targetTable, () => {
32
- switch (rel.relationshipType) {
33
- case "hasMany":
34
- // For hasMany the primary key is on the source table
35
- // and the foreign key is on the target table
36
- qb = qb.innerJoin(
37
- `${targetTable} as ${context.tableAlias()}`,
38
- `${srcTable}.id`,
39
- `${context.tableAlias()}.${rel.foreignKey}`
40
- );
41
- break;
42
-
43
- case "belongsTo":
44
- // For belongsTo the primary key is on the target table
45
- // and the foreign key is on the source table
46
- qb = qb.innerJoin(
47
- `${targetTable} as ${context.tableAlias()}`,
48
- `${srcTable}.${rel.foreignKey}`,
49
- `${context.tableAlias()}.id`
50
- );
51
- break;
52
- default:
53
- throw new Error(`unknown relationshipType: ${rel.relationshipType}`);
54
- }
55
-
56
- // Keep traversing through the where conditions to see if
57
- // more joins need to be applied
58
- qb = applyJoins(context, qb, where[key]);
59
- });
60
- }
61
-
62
- return qb;
63
- }
64
-
65
- module.exports = {
66
- applyJoins,
67
- };