@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.
Files changed (47) hide show
  1. package/dist/aggregates.js +9 -6
  2. package/dist/db.js +4 -4
  3. package/dist/hooks.js +6 -2
  4. package/dist/main.js +3 -1
  5. package/dist/manage-record.js +6 -2
  6. package/dist/mysql/migration-generator.js +15 -6
  7. package/dist/mysql/migration-runner.js +5 -0
  8. package/dist/mysql/mysql-db.js +24 -14
  9. package/dist/mysql/schema-introspector.js +11 -6
  10. package/dist/orm-request.js +19 -11
  11. package/dist/postgres/connection.js +3 -1
  12. package/dist/postgres/migration-generator.js +9 -5
  13. package/dist/postgres/migration-runner.js +5 -0
  14. package/dist/postgres/postgres-db.js +14 -13
  15. package/dist/postgres/schema-introspector.js +11 -6
  16. package/dist/postgres/type-map.js +3 -0
  17. package/dist/relationships.js +2 -0
  18. package/dist/setup-rest-server.js +4 -6
  19. package/dist/store.js +2 -0
  20. package/dist/timescale/query-builder.d.ts +2 -0
  21. package/dist/timescale/query-builder.js +30 -2
  22. package/dist/utils.js +2 -1
  23. package/dist/view-resolver.js +3 -1
  24. package/package.json +1 -1
  25. package/src/aggregates.ts +9 -7
  26. package/src/belongs-to.ts +1 -1
  27. package/src/db.ts +4 -4
  28. package/src/hooks.ts +4 -2
  29. package/src/main.ts +3 -2
  30. package/src/manage-record.ts +5 -2
  31. package/src/mysql/migration-generator.ts +12 -7
  32. package/src/mysql/migration-runner.ts +5 -0
  33. package/src/mysql/mysql-db.ts +23 -17
  34. package/src/mysql/schema-introspector.ts +10 -7
  35. package/src/orm-request.ts +19 -12
  36. package/src/postgres/connection.ts +3 -1
  37. package/src/postgres/migration-generator.ts +7 -5
  38. package/src/postgres/migration-runner.ts +5 -0
  39. package/src/postgres/postgres-db.ts +14 -13
  40. package/src/postgres/schema-introspector.ts +10 -7
  41. package/src/postgres/type-map.ts +4 -0
  42. package/src/relationships.ts +3 -2
  43. package/src/setup-rest-server.ts +7 -10
  44. package/src/store.ts +2 -1
  45. package/src/timescale/query-builder.ts +39 -2
  46. package/src/utils.ts +2 -1
  47. 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
- const orderStr = orderBy ? ` ORDER BY ${orderBy}` : '';
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 pluralizedLast = basePluralize(parts.pop()!);
11
+ const last = parts.pop() as string;
12
+ const pluralizedLast = basePluralize(last);
12
13
  return [...parts, pluralizedLast].join('-');
13
14
  }
14
15
 
@@ -137,7 +137,8 @@ export default class ViewResolver {
137
137
  if (!groups.has(key)) {
138
138
  groups.set(key, []);
139
139
  }
140
- groups.get(key)!.push(record);
140
+ const group = groups.get(key);
141
+ if (group) group.push(record);
141
142
  }
142
143
 
143
144
  const results: unknown[] = [];