@gblikas/querykit 0.4.0 → 0.5.0

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.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Helper utilities for creating raw SQL expressions in virtual fields
3
+ */
4
+
5
+ import { sql } from 'drizzle-orm';
6
+ import { IRawSqlExpression } from '../parser/types';
7
+
8
+ /**
9
+ * Validates field name to prevent SQL injection.
10
+ * Only allows alphanumeric characters, dots, and underscores.
11
+ * @private
12
+ */
13
+ function validateFieldName(field: string): void {
14
+ if (!/^[a-zA-Z][a-zA-Z0-9._]*$/.test(field)) {
15
+ throw new Error(`Invalid field name: ${field}`);
16
+ }
17
+ if (field.length > 64) {
18
+ throw new Error(`Field name too long: ${field}`);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Create a JSONB array contains expression (PostgreSQL).
24
+ * Checks if the JSONB array field contains the given value.
25
+ *
26
+ * @param field - The JSONB field name (e.g., 'assigned_to')
27
+ * @param value - The value to check for in the array
28
+ * @returns A raw SQL expression for JSONB contains check
29
+ *
30
+ * @example
31
+ * // Check if assignedTo contains the current user ID
32
+ * jsonbContains('assigned_to', ctx.currentUserId)
33
+ * // Generates: assigned_to @> '["user123"]'::jsonb
34
+ */
35
+ export function jsonbContains(
36
+ field: string,
37
+ value: unknown
38
+ ): IRawSqlExpression {
39
+ validateFieldName(field);
40
+ return {
41
+ type: 'raw',
42
+ toSql: () =>
43
+ sql`${sql.identifier(field)} @> ${sql.raw("'" + JSON.stringify(Array.isArray(value) ? value : [value]).replace(/'/g, "''") + "'::jsonb")}`
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Validates days parameter to ensure it's a positive finite number.
49
+ * @private
50
+ */
51
+ function validateDaysParameter(days: number): void {
52
+ if (!Number.isFinite(days)) {
53
+ throw new Error(`Invalid days parameter: ${days}. Must be a finite number.`);
54
+ }
55
+ if (days <= 0) {
56
+ throw new Error(`Invalid days parameter: ${days}. Must be a positive number.`);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Create a date range expression.
62
+ * Checks if a timestamp field is within the specified number of days from now.
63
+ *
64
+ * @param field - The timestamp field name (e.g., 'created_at')
65
+ * @param days - Number of days from now (must be a positive finite number)
66
+ * @returns A raw SQL expression for date range check
67
+ *
68
+ * @example
69
+ * // Check if created within last day
70
+ * dateWithinDays('created_at', 1)
71
+ * // Generates: created_at >= NOW() - INTERVAL '1 days'
72
+ */
73
+ export function dateWithinDays(field: string, days: number): IRawSqlExpression {
74
+ validateFieldName(field);
75
+ validateDaysParameter(days);
76
+ return {
77
+ type: 'raw',
78
+ toSql: () =>
79
+ sql`${sql.identifier(field)} >= NOW() - INTERVAL '${sql.raw(days.toString())} days'`
80
+ };
81
+ }
@@ -4,3 +4,4 @@
4
4
 
5
5
  export * from './types';
6
6
  export * from './resolver';
7
+ export * from './helpers';