@stonyx/orm 0.2.1-beta.86 → 0.2.1-beta.88
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/aggregates.js +9 -6
- package/dist/db.js +4 -4
- package/dist/hooks.js +6 -2
- package/dist/main.js +3 -1
- package/dist/manage-record.js +6 -2
- package/dist/mysql/migration-generator.js +15 -6
- package/dist/mysql/migration-runner.js +5 -0
- package/dist/mysql/mysql-db.js +24 -14
- package/dist/mysql/schema-introspector.js +11 -6
- package/dist/orm-request.js +19 -11
- package/dist/postgres/connection.js +3 -1
- package/dist/postgres/migration-generator.js +9 -5
- package/dist/postgres/migration-runner.js +5 -0
- package/dist/postgres/postgres-db.js +14 -13
- package/dist/postgres/schema-introspector.js +11 -6
- package/dist/postgres/type-map.js +3 -0
- package/dist/relationships.js +2 -0
- package/dist/setup-rest-server.js +4 -6
- package/dist/store.js +2 -0
- package/dist/timescale/query-builder.d.ts +2 -0
- package/dist/timescale/query-builder.js +30 -2
- package/dist/utils.js +2 -1
- package/dist/view-resolver.js +3 -1
- package/package.json +1 -1
- package/src/aggregates.ts +9 -7
- package/src/belongs-to.ts +1 -1
- package/src/db.ts +4 -4
- package/src/hooks.ts +4 -2
- package/src/main.ts +3 -2
- package/src/manage-record.ts +5 -2
- package/src/mysql/migration-generator.ts +12 -7
- package/src/mysql/migration-runner.ts +5 -0
- package/src/mysql/mysql-db.ts +23 -17
- package/src/mysql/schema-introspector.ts +10 -7
- package/src/orm-request.ts +19 -12
- package/src/postgres/connection.ts +3 -1
- package/src/postgres/migration-generator.ts +7 -5
- package/src/postgres/migration-runner.ts +5 -0
- package/src/postgres/postgres-db.ts +14 -13
- package/src/postgres/schema-introspector.ts +10 -7
- package/src/postgres/type-map.ts +4 -0
- package/src/relationships.ts +3 -2
- package/src/setup-rest-server.ts +7 -10
- package/src/store.ts +2 -1
- package/src/timescale/query-builder.ts +39 -2
- package/src/utils.ts +2 -1
- package/src/view-resolver.ts +2 -1
|
@@ -3,6 +3,26 @@ export { validateIdentifier, buildInsert, buildUpdate, buildDelete, buildSelect
|
|
|
3
3
|
|
|
4
4
|
import { validateIdentifier } from '../postgres/query-builder.js';
|
|
5
5
|
|
|
6
|
+
const SAFE_INTERVAL = /^\d+\s+(microsecond|millisecond|second|minute|hour|day|week|month|year)s?$/i;
|
|
7
|
+
|
|
8
|
+
export function validateInterval(interval: string, context: string = 'interval'): string {
|
|
9
|
+
if (!interval || typeof interval !== 'string' || !SAFE_INTERVAL.test(interval.trim())) {
|
|
10
|
+
throw new Error(`Invalid SQL ${context}: "${interval}". Intervals must match pattern like "7 days", "1 hour", "30 minutes".`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return interval.trim();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SAFE_AGGREGATE = /^(COUNT|SUM|AVG|MIN|MAX|FIRST|LAST)\s*\(\s*("?[a-zA-Z_][a-zA-Z0-9_]*"?|\*)\s*\)\s*(AS\s+"?[a-zA-Z_][a-zA-Z0-9_]*"?)?$/i;
|
|
17
|
+
|
|
18
|
+
export function validateAggregate(expr: string, context: string = 'aggregate'): string {
|
|
19
|
+
if (!expr || typeof expr !== 'string' || !SAFE_AGGREGATE.test(expr.trim())) {
|
|
20
|
+
throw new Error(`Invalid SQL ${context}: "${expr}". Aggregates must be simple function calls like "AVG(value) AS avg_value".`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return expr.trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
interface QueryResult {
|
|
7
27
|
sql: string;
|
|
8
28
|
values: unknown[];
|
|
@@ -36,6 +56,7 @@ export function buildCreateHypertable(table: string, timeColumn: string, options
|
|
|
36
56
|
validateIdentifier(timeColumn, 'column name');
|
|
37
57
|
|
|
38
58
|
const { chunkInterval = '7 days' } = options;
|
|
59
|
+
validateInterval(chunkInterval, 'chunk interval');
|
|
39
60
|
|
|
40
61
|
const sql = `SELECT create_hypertable('"${table}"', '${timeColumn}', chunk_time_interval => INTERVAL '${chunkInterval}', if_not_exists => TRUE)`;
|
|
41
62
|
|
|
@@ -57,7 +78,7 @@ export function buildTimeBucket(table: string, timeColumn: string, bucketSize: s
|
|
|
57
78
|
values.push(bucketSize);
|
|
58
79
|
|
|
59
80
|
for (const agg of aggregates) {
|
|
60
|
-
selectCols.push(agg);
|
|
81
|
+
selectCols.push(validateAggregate(agg));
|
|
61
82
|
}
|
|
62
83
|
|
|
63
84
|
const whereClauses: string[] = [];
|
|
@@ -70,7 +91,20 @@ export function buildTimeBucket(table: string, timeColumn: string, bucketSize: s
|
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
const whereStr = whereClauses.length > 0 ? ` WHERE ${whereClauses.join(' AND ')}` : '';
|
|
73
|
-
|
|
94
|
+
let orderStr = '';
|
|
95
|
+
if (orderBy) {
|
|
96
|
+
const parts = orderBy.trim().split(/\s+/);
|
|
97
|
+
const col = parts[0];
|
|
98
|
+
const dir = parts[1]?.toUpperCase();
|
|
99
|
+
|
|
100
|
+
validateIdentifier(col, 'ORDER BY column');
|
|
101
|
+
|
|
102
|
+
if (dir && dir !== 'ASC' && dir !== 'DESC') {
|
|
103
|
+
throw new Error(`Invalid ORDER BY direction: "${dir}". Must be ASC or DESC.`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
orderStr = ` ORDER BY "${col}"${dir ? ` ${dir}` : ''}`;
|
|
107
|
+
}
|
|
74
108
|
let limitStr = '';
|
|
75
109
|
if (limit != null) {
|
|
76
110
|
limitStr = ` LIMIT $${paramIndex++}`;
|
|
@@ -91,6 +125,8 @@ export function buildContinuousAggregate(viewName: string, table: string, timeCo
|
|
|
91
125
|
validateIdentifier(timeColumn, 'column name');
|
|
92
126
|
|
|
93
127
|
const { withNoData = false } = options;
|
|
128
|
+
validateInterval(bucketSize, 'bucket size');
|
|
129
|
+
aggregates.forEach(agg => validateAggregate(agg));
|
|
94
130
|
|
|
95
131
|
const selectCols: string[] = [
|
|
96
132
|
`time_bucket('${bucketSize}', "${timeColumn}") AS bucket`,
|
|
@@ -109,6 +145,7 @@ export function buildContinuousAggregate(viewName: string, table: string, timeCo
|
|
|
109
145
|
*/
|
|
110
146
|
export function buildCompressionPolicy(table: string, compressAfter: string): SqlResult {
|
|
111
147
|
validateIdentifier(table, 'table name');
|
|
148
|
+
validateInterval(compressAfter, 'compress after interval');
|
|
112
149
|
|
|
113
150
|
const sql = `SELECT add_compression_policy('"${table}"', INTERVAL '${compressAfter}', if_not_exists => TRUE)`;
|
|
114
151
|
|
package/src/utils.ts
CHANGED
|
@@ -8,7 +8,8 @@ export function isDbError(error: unknown): error is { code: string; message: str
|
|
|
8
8
|
export function pluralize(word: string): string {
|
|
9
9
|
if (word.includes('-')) {
|
|
10
10
|
const parts = word.split('-');
|
|
11
|
-
const
|
|
11
|
+
const last = parts.pop() as string;
|
|
12
|
+
const pluralizedLast = basePluralize(last);
|
|
12
13
|
return [...parts, pluralizedLast].join('-');
|
|
13
14
|
}
|
|
14
15
|
|
package/src/view-resolver.ts
CHANGED