alepha 0.14.0 → 0.14.2

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 (149) hide show
  1. package/README.md +3 -3
  2. package/dist/api/audits/index.d.ts +80 -1
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js.map +1 -1
  5. package/dist/api/files/index.d.ts +80 -1
  6. package/dist/api/files/index.d.ts.map +1 -1
  7. package/dist/api/files/index.js.map +1 -1
  8. package/dist/api/jobs/index.d.ts +236 -157
  9. package/dist/api/jobs/index.d.ts.map +1 -1
  10. package/dist/api/jobs/index.js.map +1 -1
  11. package/dist/api/notifications/index.d.ts +21 -1
  12. package/dist/api/notifications/index.d.ts.map +1 -1
  13. package/dist/api/parameters/index.d.ts +451 -4
  14. package/dist/api/parameters/index.d.ts.map +1 -1
  15. package/dist/api/parameters/index.js.map +1 -1
  16. package/dist/api/users/index.d.ts +252 -249
  17. package/dist/api/users/index.d.ts.map +1 -1
  18. package/dist/api/users/index.js +4 -0
  19. package/dist/api/users/index.js.map +1 -1
  20. package/dist/api/verifications/index.d.ts +128 -128
  21. package/dist/api/verifications/index.d.ts.map +1 -1
  22. package/dist/batch/index.js.map +1 -1
  23. package/dist/cache/core/index.js.map +1 -1
  24. package/dist/cli/index.d.ts +304 -115
  25. package/dist/cli/index.d.ts.map +1 -1
  26. package/dist/cli/index.js +650 -531
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/command/index.d.ts +210 -13
  29. package/dist/command/index.d.ts.map +1 -1
  30. package/dist/command/index.js +306 -69
  31. package/dist/command/index.js.map +1 -1
  32. package/dist/core/index.browser.js.map +1 -1
  33. package/dist/core/index.d.ts +1 -1
  34. package/dist/core/index.d.ts.map +1 -1
  35. package/dist/core/index.js +7 -6
  36. package/dist/core/index.js.map +1 -1
  37. package/dist/core/index.native.js +7 -6
  38. package/dist/core/index.native.js.map +1 -1
  39. package/dist/datetime/index.js.map +1 -1
  40. package/dist/fake/index.js.map +1 -1
  41. package/dist/file/index.d.ts.map +1 -1
  42. package/dist/file/index.js.map +1 -1
  43. package/dist/lock/redis/index.js.map +1 -1
  44. package/dist/logger/index.js.map +1 -1
  45. package/dist/mcp/index.js.map +1 -1
  46. package/dist/orm/index.browser.js +26 -5
  47. package/dist/orm/index.browser.js.map +1 -1
  48. package/dist/orm/index.d.ts +294 -215
  49. package/dist/orm/index.d.ts.map +1 -1
  50. package/dist/orm/index.js +522 -523
  51. package/dist/orm/index.js.map +1 -1
  52. package/dist/queue/redis/index.js +2 -4
  53. package/dist/queue/redis/index.js.map +1 -1
  54. package/dist/redis/index.d.ts +400 -29
  55. package/dist/redis/index.d.ts.map +1 -1
  56. package/dist/redis/index.js +412 -21
  57. package/dist/redis/index.js.map +1 -1
  58. package/dist/retry/index.js.map +1 -1
  59. package/dist/router/index.js.map +1 -1
  60. package/dist/scheduler/index.js.map +1 -1
  61. package/dist/security/index.d.ts.map +1 -1
  62. package/dist/security/index.js.map +1 -1
  63. package/dist/server/auth/index.d.ts +155 -155
  64. package/dist/server/auth/index.js.map +1 -1
  65. package/dist/server/cache/index.js.map +1 -1
  66. package/dist/server/cookies/index.browser.js.map +1 -1
  67. package/dist/server/cookies/index.js.map +1 -1
  68. package/dist/server/core/index.browser.js.map +1 -1
  69. package/dist/server/core/index.d.ts +0 -1
  70. package/dist/server/core/index.d.ts.map +1 -1
  71. package/dist/server/core/index.js.map +1 -1
  72. package/dist/server/helmet/index.d.ts +4 -1
  73. package/dist/server/helmet/index.d.ts.map +1 -1
  74. package/dist/server/helmet/index.js.map +1 -1
  75. package/dist/server/links/index.browser.js.map +1 -1
  76. package/dist/server/links/index.js.map +1 -1
  77. package/dist/server/multipart/index.d.ts.map +1 -1
  78. package/dist/server/multipart/index.js.map +1 -1
  79. package/dist/server/proxy/index.js.map +1 -1
  80. package/dist/server/rate-limit/index.js.map +1 -1
  81. package/dist/server/security/index.d.ts +9 -9
  82. package/dist/server/security/index.js.map +1 -1
  83. package/dist/server/swagger/index.js.map +1 -1
  84. package/dist/thread/index.js.map +1 -1
  85. package/dist/topic/core/index.js.map +1 -1
  86. package/dist/topic/redis/index.js +3 -3
  87. package/dist/topic/redis/index.js.map +1 -1
  88. package/dist/vite/index.js +9 -6
  89. package/dist/vite/index.js.map +1 -1
  90. package/dist/websocket/index.browser.js.map +1 -1
  91. package/dist/websocket/index.d.ts +7 -7
  92. package/dist/websocket/index.js.map +1 -1
  93. package/package.json +3 -3
  94. package/src/api/users/index.ts +4 -0
  95. package/src/cli/apps/AlephaCli.ts +36 -14
  96. package/src/cli/apps/AlephaPackageBuilderCli.ts +5 -1
  97. package/src/cli/assets/appRouterTs.ts +1 -1
  98. package/src/cli/atoms/changelogOptions.ts +45 -0
  99. package/src/cli/commands/{ViteCommands.ts → build.ts} +4 -93
  100. package/src/cli/commands/changelog.ts +244 -0
  101. package/src/cli/commands/clean.ts +14 -0
  102. package/src/cli/commands/{DrizzleCommands.ts → db.ts} +37 -124
  103. package/src/cli/commands/deploy.ts +118 -0
  104. package/src/cli/commands/dev.ts +57 -0
  105. package/src/cli/commands/format.ts +17 -0
  106. package/src/cli/commands/{CoreCommands.ts → init.ts} +2 -40
  107. package/src/cli/commands/lint.ts +17 -0
  108. package/src/cli/commands/root.ts +32 -0
  109. package/src/cli/commands/run.ts +24 -0
  110. package/src/cli/commands/test.ts +42 -0
  111. package/src/cli/commands/typecheck.ts +19 -0
  112. package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
  113. package/src/cli/defineConfig.ts +24 -0
  114. package/src/cli/index.ts +17 -5
  115. package/src/cli/services/AlephaCliUtils.ts +4 -21
  116. package/src/cli/services/GitMessageParser.ts +77 -0
  117. package/src/command/helpers/EnvUtils.ts +37 -0
  118. package/src/command/index.ts +3 -1
  119. package/src/command/primitives/$command.ts +172 -6
  120. package/src/command/providers/CliProvider.ts +424 -91
  121. package/src/core/Alepha.ts +8 -5
  122. package/src/file/providers/NodeFileSystemProvider.ts +3 -1
  123. package/src/orm/index.browser.ts +1 -1
  124. package/src/orm/index.ts +18 -10
  125. package/src/orm/interfaces/PgQueryWhere.ts +1 -26
  126. package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
  127. package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
  128. package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
  129. package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
  130. package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
  131. package/src/orm/services/QueryManager.ts +10 -125
  132. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
  133. package/src/redis/index.ts +65 -3
  134. package/src/redis/providers/BunRedisProvider.ts +304 -0
  135. package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
  136. package/src/redis/providers/NodeRedisProvider.ts +280 -0
  137. package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
  138. package/src/redis/providers/RedisProvider.ts +134 -140
  139. package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
  140. package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
  141. package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
  142. package/src/server/core/providers/ServerProvider.ts +7 -4
  143. package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
  144. package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
  145. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  146. package/src/vite/tasks/buildServer.ts +1 -0
  147. package/src/cli/commands/BiomeCommands.ts +0 -29
  148. package/src/cli/commands/ChangelogCommands.ts +0 -389
  149. package/src/orm/services/PgJsonQueryManager.ts +0 -511
@@ -1,511 +0,0 @@
1
- import type { TObject } from "alepha";
2
- import { type SQL, sql } from "drizzle-orm";
3
- import type { PgColumn } from "drizzle-orm/pg-core";
4
- import type { FilterOperators } from "../interfaces/FilterOperators.ts";
5
- import type { PgQueryWhere } from "../interfaces/PgQueryWhere.ts";
6
-
7
- /**
8
- * Manages JSONB query generation for nested object and array queries in PostgreSQL.
9
- * This class handles complex nested queries using PostgreSQL's JSONB operators.
10
- */
11
- export class PgJsonQueryManager {
12
- /**
13
- * Check if a query contains nested JSONB queries.
14
- * A nested query is when the value is an object with operator keys.
15
- */
16
- public hasNestedQuery(where: PgQueryWhere<TObject>): boolean {
17
- for (const [key, value] of Object.entries(where)) {
18
- // Skip logical operators
19
- if (key === "and" || key === "or" || key === "not") {
20
- continue;
21
- }
22
-
23
- // Check if value is an object with nested properties
24
- if (value && typeof value === "object" && !Array.isArray(value)) {
25
- // Check if it has operator keys or nested object keys
26
- const keys = Object.keys(value);
27
- const hasOperators = keys.some((k) =>
28
- [
29
- "eq",
30
- "ne",
31
- "gt",
32
- "gte",
33
- "lt",
34
- "lte",
35
- "like",
36
- "ilike",
37
- "isNull",
38
- "isNotNull",
39
- "inArray",
40
- "notInArray",
41
- ].includes(k),
42
- );
43
-
44
- // If it doesn't have operators, it might be a nested query
45
- if (!hasOperators && keys.length > 0) {
46
- return true;
47
- }
48
- }
49
- }
50
- return false;
51
- }
52
-
53
- /**
54
- * Build a JSONB query condition for nested object queries.
55
- * Supports deep nesting like: { profile: { contact: { email: { eq: "test@example.com" } } } }
56
- *
57
- * @param column The JSONB column
58
- * @param path The path to the nested property (e.g., ['profile', 'contact', 'email'])
59
- * @param operator The filter operator (e.g., { eq: "test@example.com" })
60
- * @param dialect Database dialect (postgresql or sqlite)
61
- * @param columnSchema Optional schema of the JSON column for type inference
62
- * @returns SQL condition
63
- */
64
- public buildJsonbCondition(
65
- column: PgColumn,
66
- path: string[],
67
- operator: FilterOperators<any>,
68
- dialect: "postgresql" | "sqlite",
69
- columnSchema?: any,
70
- ): SQL | undefined {
71
- if (path.length === 0) {
72
- return undefined;
73
- }
74
-
75
- // Check if operator is an array operator that needs JSONB (not text extraction)
76
- const isArrayOperator =
77
- operator.arrayContains !== undefined ||
78
- operator.arrayContained !== undefined ||
79
- operator.arrayOverlaps !== undefined;
80
-
81
- let jsonValue: SQL;
82
-
83
- if (dialect === "sqlite") {
84
- // SQLite: json_extract(column, '$.path.to.field')
85
- const pathStr = `$.${path.join(".")}`;
86
- jsonValue = sql`json_extract(${column}, ${pathStr})`;
87
- } else {
88
- // PostgreSQL: Build the JSON path
89
- let jsonPath = sql`${column}`;
90
-
91
- // Navigate through all path elements except the last
92
- for (let i = 0; i < path.length - 1; i++) {
93
- jsonPath = sql`${jsonPath}->${path[i]}`;
94
- }
95
-
96
- // For the last element:
97
- // - Use -> to keep as JSONB for array operators
98
- // - Use ->> to extract as text for other operators
99
- const lastPath = path[path.length - 1];
100
- if (isArrayOperator) {
101
- jsonValue = sql`${jsonPath}->${lastPath}`;
102
- } else {
103
- jsonValue = sql`${jsonPath}->>${lastPath}`;
104
- }
105
- }
106
-
107
- // Get field type for smart casting
108
- const fieldType = columnSchema
109
- ? this.getFieldType(columnSchema, path)
110
- : undefined;
111
-
112
- // Apply the operator
113
- return this.applyOperatorToJsonValue(
114
- jsonValue,
115
- operator,
116
- dialect,
117
- fieldType,
118
- );
119
- }
120
-
121
- /**
122
- * Build JSONB array query conditions.
123
- * Supports queries like: { addresses: { city: { eq: "Wonderland" } } }
124
- * which translates to: EXISTS (SELECT 1 FROM jsonb_array_elements(addresses) elem WHERE elem->>'city' = 'Wonderland')
125
- *
126
- * @param dialect Database dialect (postgresql or sqlite)
127
- * Note: SQLite array queries are not yet supported
128
- */
129
- public buildJsonbArrayCondition(
130
- column: PgColumn,
131
- path: string[],
132
- arrayPath: string,
133
- operator: FilterOperators<any>,
134
- dialect: "postgresql" | "sqlite",
135
- ): SQL | undefined {
136
- if (dialect === "sqlite") {
137
- throw new Error(
138
- "Array queries in JSON columns are not yet supported for SQLite. " +
139
- "Please use PostgreSQL for complex JSON array queries, or restructure your data.",
140
- );
141
- }
142
-
143
- if (path.length === 0) {
144
- return undefined;
145
- }
146
-
147
- // Build the base JSONB path to the array
148
- let jsonPath = sql`${column}`;
149
- if (arrayPath) {
150
- jsonPath = sql`${jsonPath}->${arrayPath}`;
151
- }
152
-
153
- // Build the condition for array elements
154
- const lastPath = path[0];
155
- const elemCondition = sql`elem->>${lastPath}`;
156
- const condition = this.applyOperatorToJsonValue(
157
- elemCondition,
158
- operator,
159
- dialect,
160
- );
161
-
162
- if (!condition) {
163
- return undefined;
164
- }
165
-
166
- // Wrap in EXISTS with jsonb_array_elements
167
- return sql`EXISTS (SELECT 1 FROM jsonb_array_elements(${jsonPath}) AS elem WHERE ${condition})`;
168
- }
169
-
170
- /**
171
- * Apply a filter operator to a JSONB value.
172
- * @param dialect Database dialect for appropriate casting syntax
173
- * @param fieldType Optional field type from schema for smart casting
174
- */
175
- private applyOperatorToJsonValue(
176
- jsonValue: SQL,
177
- operator: FilterOperators<any>,
178
- dialect: "postgresql" | "sqlite",
179
- fieldType?: string,
180
- ): SQL | undefined {
181
- // Helper to cast for numeric comparisons based on dialect and field type
182
- const castForNumeric = (value: SQL): SQL => {
183
- if (dialect === "sqlite") {
184
- // Use INTEGER for int types, REAL for number types
185
- if (fieldType === "integer" || fieldType === "int") {
186
- return sql`CAST(${value} AS INTEGER)`;
187
- }
188
- // Default to REAL for numeric comparisons
189
- return sql`CAST(${value} AS REAL)`;
190
- }
191
- // PostgreSQL
192
- return sql`(${value})::numeric`;
193
- };
194
-
195
- if (typeof operator !== "object") {
196
- // Direct value comparison
197
- return sql`${jsonValue} = ${operator}`;
198
- }
199
-
200
- const conditions: SQL[] = [];
201
-
202
- if (operator.eq !== undefined) {
203
- conditions.push(sql`${jsonValue} = ${operator.eq}`);
204
- }
205
-
206
- if (operator.ne !== undefined) {
207
- conditions.push(sql`${jsonValue} != ${operator.ne}`);
208
- }
209
-
210
- if (operator.gt !== undefined) {
211
- // Cast to numeric for comparison
212
- conditions.push(sql`${castForNumeric(jsonValue)} > ${operator.gt}`);
213
- }
214
-
215
- if (operator.gte !== undefined) {
216
- conditions.push(sql`${castForNumeric(jsonValue)} >= ${operator.gte}`);
217
- }
218
-
219
- if (operator.lt !== undefined) {
220
- conditions.push(sql`${castForNumeric(jsonValue)} < ${operator.lt}`);
221
- }
222
-
223
- if (operator.lte !== undefined) {
224
- conditions.push(sql`${castForNumeric(jsonValue)} <= ${operator.lte}`);
225
- }
226
-
227
- if (operator.like !== undefined) {
228
- conditions.push(sql`${jsonValue} LIKE ${operator.like}`);
229
- }
230
-
231
- if (operator.ilike !== undefined) {
232
- // SQLite: LIKE is case-insensitive by default, so use LIKE
233
- // PostgreSQL: Use ILIKE
234
- if (dialect === "sqlite") {
235
- conditions.push(sql`${jsonValue} LIKE ${operator.ilike}`);
236
- } else {
237
- conditions.push(sql`${jsonValue} ILIKE ${operator.ilike}`);
238
- }
239
- }
240
-
241
- if (operator.notLike !== undefined) {
242
- conditions.push(sql`${jsonValue} NOT LIKE ${operator.notLike}`);
243
- }
244
-
245
- if (operator.notIlike !== undefined) {
246
- // SQLite: LIKE is case-insensitive by default, so use NOT LIKE
247
- // PostgreSQL: Use NOT ILIKE
248
- if (dialect === "sqlite") {
249
- conditions.push(sql`${jsonValue} NOT LIKE ${operator.notIlike}`);
250
- } else {
251
- conditions.push(sql`${jsonValue} NOT ILIKE ${operator.notIlike}`);
252
- }
253
- }
254
-
255
- if (operator.isNull !== undefined) {
256
- conditions.push(sql`${jsonValue} IS NULL`);
257
- }
258
-
259
- if (operator.isNotNull !== undefined) {
260
- conditions.push(sql`${jsonValue} IS NOT NULL`);
261
- }
262
-
263
- if (operator.inArray !== undefined && Array.isArray(operator.inArray)) {
264
- conditions.push(
265
- sql`${jsonValue} IN (${sql.join(
266
- operator.inArray.map((v) => sql`${v}`),
267
- sql`, `,
268
- )})`,
269
- );
270
- }
271
-
272
- if (
273
- operator.notInArray !== undefined &&
274
- Array.isArray(operator.notInArray)
275
- ) {
276
- conditions.push(
277
- sql`${jsonValue} NOT IN (${sql.join(
278
- operator.notInArray.map((v) => sql`${v}`),
279
- sql`, `,
280
- )})`,
281
- );
282
- }
283
-
284
- // Handle array operators for JSONB arrays
285
- // When these operators are used, jsonValue will be a JSONB value (not text extracted)
286
- if (operator.arrayContains !== undefined) {
287
- if (dialect === "postgresql") {
288
- // PostgreSQL @> operator: checks if left JSONB contains right JSONB
289
- // JSON.stringify ensures the value is properly escaped as a JSON string
290
- const jsonArray = JSON.stringify(
291
- Array.isArray(operator.arrayContains)
292
- ? operator.arrayContains
293
- : [operator.arrayContains],
294
- );
295
- // The value is safely parameterized via sql template, preventing SQL injection
296
- conditions.push(sql`${jsonValue} @> ${jsonArray}::jsonb`);
297
- }
298
- }
299
-
300
- if (operator.arrayContained !== undefined) {
301
- if (dialect === "postgresql") {
302
- // PostgreSQL <@ operator: checks if left JSONB is contained in right JSONB
303
- const jsonArray = JSON.stringify(
304
- Array.isArray(operator.arrayContained)
305
- ? operator.arrayContained
306
- : [operator.arrayContained],
307
- );
308
- conditions.push(sql`${jsonValue} <@ ${jsonArray}::jsonb`);
309
- }
310
- }
311
-
312
- if (operator.arrayOverlaps !== undefined) {
313
- if (dialect === "postgresql") {
314
- // PostgreSQL ?| operator: checks if any of the array elements exist as top-level keys
315
- // Note: For JSONB arrays, we need to use a different approach
316
- // Convert the JSONB array to text array for the ?| operator
317
- const values = Array.isArray(operator.arrayOverlaps)
318
- ? operator.arrayOverlaps
319
- : [operator.arrayOverlaps];
320
-
321
- // Build an OR condition to check if any value exists in the array
322
- // This is safer than using ?| with dynamic values
323
- const overlapConditions = values.map((val) => {
324
- const jsonVal = JSON.stringify(val);
325
- return sql`${jsonValue} @> ${jsonVal}::jsonb`;
326
- });
327
-
328
- if (overlapConditions.length > 0) {
329
- conditions.push(sql`(${sql.join(overlapConditions, sql` OR `)})`);
330
- }
331
- }
332
- }
333
-
334
- if (conditions.length === 0) {
335
- return undefined;
336
- }
337
-
338
- if (conditions.length === 1) {
339
- return conditions[0];
340
- }
341
-
342
- // Multiple conditions - AND them together
343
- return sql.join(conditions, sql` AND `);
344
- }
345
-
346
- /**
347
- * Parse a nested query object and extract the path and operator.
348
- * For example: { profile: { contact: { email: { eq: "test@example.com" } } } }
349
- * Returns: { path: ['profile', 'contact', 'email'], operator: { eq: "test@example.com" } }
350
- */
351
- public parseNestedQuery(
352
- nestedQuery: any,
353
- currentPath: string[] = [],
354
- ): Array<{ path: string[]; operator: FilterOperators<any> }> {
355
- const results: Array<{ path: string[]; operator: FilterOperators<any> }> =
356
- [];
357
-
358
- for (const [key, value] of Object.entries(nestedQuery)) {
359
- if (value && typeof value === "object" && !Array.isArray(value)) {
360
- // Check if this is an operator object
361
- const keys = Object.keys(value);
362
- const hasOperators = keys.some((k) =>
363
- [
364
- "eq",
365
- "ne",
366
- "gt",
367
- "gte",
368
- "lt",
369
- "lte",
370
- "like",
371
- "ilike",
372
- "notLike",
373
- "notIlike",
374
- "isNull",
375
- "isNotNull",
376
- "inArray",
377
- "notInArray",
378
- "arrayContains",
379
- "arrayContained",
380
- "arrayOverlaps",
381
- ].includes(k),
382
- );
383
-
384
- if (hasOperators) {
385
- // This is an operator, add to results
386
- results.push({
387
- path: [...currentPath, key],
388
- operator: value as FilterOperators<any>,
389
- });
390
- } else {
391
- // This is a nested object, recurse
392
- const nestedResults = this.parseNestedQuery(value, [
393
- ...currentPath,
394
- key,
395
- ]);
396
- results.push(...nestedResults);
397
- }
398
- }
399
- }
400
-
401
- return results;
402
- }
403
-
404
- /**
405
- * Determine if a property is a JSONB column based on the schema.
406
- * A column is JSONB if it's defined as an object or array in the TypeBox schema.
407
- */
408
- public isJsonbColumn(schema: TObject, columnName: string): boolean {
409
- const property = schema.properties[columnName];
410
- if (!property) {
411
- return false;
412
- }
413
-
414
- // Check if it's an object or array type
415
- return (
416
- (property as any).type === "object" || (property as any).type === "array"
417
- );
418
- }
419
-
420
- /**
421
- * Check if an array property contains primitive types (string, number, boolean, etc.)
422
- * rather than objects. Primitive arrays should use native Drizzle operators.
423
- * @returns true if the array contains primitives, false if it contains objects
424
- */
425
- public isPrimitiveArray(schema: TObject, columnName: string): boolean {
426
- const property = schema.properties[columnName];
427
- if (!property || (property as any).type !== "array") {
428
- return false;
429
- }
430
-
431
- // Check the items type
432
- const items = (property as any).items;
433
- if (!items) {
434
- return false;
435
- }
436
-
437
- // If items is an object type, it's not a primitive array
438
- // Primitive types are: string, number, integer, boolean, null
439
- const itemType = items.type;
440
- return (
441
- itemType === "string" ||
442
- itemType === "number" ||
443
- itemType === "integer" ||
444
- itemType === "boolean" ||
445
- itemType === "null"
446
- );
447
- }
448
-
449
- /**
450
- * Get the type of a field by navigating through a schema path.
451
- * Used for smart type casting in SQL queries.
452
- *
453
- * @param columnSchema The schema of the JSON column (e.g., t.object({ age: t.integer() }))
454
- * @param path The path to navigate (e.g., ['contact', 'email'])
455
- * @returns The type string (e.g., 'integer', 'number', 'string') or undefined if not found
456
- */
457
- private getFieldType(columnSchema: any, path: string[]): string | undefined {
458
- let current = columnSchema;
459
-
460
- for (const segment of path) {
461
- // Navigate into object properties
462
- if (current.type === "object" && current.properties) {
463
- current = current.properties[segment];
464
- if (!current) {
465
- return undefined;
466
- }
467
- } else {
468
- // Path segment doesn't exist or current is not an object
469
- return undefined;
470
- }
471
- }
472
-
473
- return current.type;
474
- }
475
-
476
- /**
477
- * Check if a nested path points to an array property.
478
- */
479
- public isArrayProperty(schema: TObject, path: string[]): boolean {
480
- if (path.length === 0) {
481
- return false;
482
- }
483
-
484
- let currentSchema: any = schema.properties[path[0]];
485
- if (!currentSchema) {
486
- return false;
487
- }
488
-
489
- // If first element is an array, return true
490
- if (currentSchema.type === "array") {
491
- return true;
492
- }
493
-
494
- // Navigate through nested objects
495
- for (let i = 1; i < path.length; i++) {
496
- if (currentSchema.type === "object" && currentSchema.properties) {
497
- currentSchema = currentSchema.properties[path[i]];
498
- if (!currentSchema) {
499
- return false;
500
- }
501
- if (currentSchema.type === "array") {
502
- return true;
503
- }
504
- } else {
505
- return false;
506
- }
507
- }
508
-
509
- return false;
510
- }
511
- }