@restura/core 1.7.0 → 1.9.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.
package/dist/index.d.ts CHANGED
@@ -40,6 +40,10 @@ declare const loggerConfigSchema: z.ZodObject<{
40
40
  }, z.core.$strip>;
41
41
  type LoggerConfigSchema = z.infer<typeof loggerConfigSchema>;
42
42
 
43
+ /**
44
+ * @deprecated This is used for passing around until we finally get to the sending externally
45
+ * // TODO: Remove once backwards compatibility is no longer needed
46
+ */
43
47
  interface RsErrorInternalData<T extends Record<string, unknown> = Record<string, unknown>> {
44
48
  err: ErrorCode;
45
49
  msg: string;
@@ -70,13 +74,13 @@ declare enum HtmlStatusCodes {
70
74
  NETWORK_CONNECT_TIMEOUT = 599
71
75
  }
72
76
  type ErrorCode = 'BAD_REQUEST' | 'UNAUTHORIZED' | 'PAYMENT_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'METHOD_NOT_ALLOWED' | 'REQUEST_TIMEOUT' | 'CONFLICT' | 'GONE' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPGRADE_REQUIRED' | 'UNPROCESSABLE_ENTITY' | 'TOO_MANY_REQUESTS' | 'SERVER_ERROR' | 'NOT_IMPLEMENTED' | 'BAD_GATEWAY' | 'SERVICE_UNAVAILABLE' | 'GATEWAY_TIMEOUT' | 'NETWORK_CONNECT_TIMEOUT' | 'UNKNOWN_ERROR' | 'RATE_LIMIT_EXCEEDED' | 'INVALID_TOKEN' | 'INCORRECT_EMAIL_OR_PASSWORD' | 'DUPLICATE' | 'CONNECTION_ERROR' | 'SCHEMA_ERROR' | 'DATABASE_ERROR';
73
- declare class RsError<T extends Record<string, unknown> = Record<string, unknown>> {
77
+ declare class RsError<T extends Record<string, unknown> = Record<string, unknown>> extends Error {
74
78
  err: ErrorCode;
75
79
  msg: string;
76
80
  options?: T;
77
81
  status?: number;
78
- stack: string;
79
82
  constructor(errCode: ErrorCode, message?: string, options?: T);
83
+ toJSON(): Record<string, unknown>;
80
84
  static htmlStatus(code: ErrorCode): number;
81
85
  static isRsError(error: unknown): error is RsError;
82
86
  }
@@ -1193,10 +1197,16 @@ declare function questionMarksToOrderedParams(query: string): string;
1193
1197
  /**
1194
1198
  * Creates a query to insert an object into a table.
1195
1199
  * @param table Table name to insert the object into
1196
- * @param obj Data to insert into the table
1197
- * @returns the query to insert the object into the table
1200
+ * @param obj Data to insert into the table
1201
+ * @param options.customSelect Optional custom SELECT clause that wraps the INSERT in a CTE.
1202
+ * When provided, the INSERT is wrapped in `WITH inserted AS (INSERT ... RETURNING *)`,
1203
+ * and your customSelect should reference the `inserted` alias.
1204
+ * Example: `{ customSelect: 'SELECT i.*, u.email FROM inserted i JOIN "user" u ON i."userId" = u.id' }`
1205
+ * @returns The query to insert the object into the table
1198
1206
  */
1199
- declare function insertObjectQuery(table: string, obj: DynamicObject): string;
1207
+ declare function insertObjectQuery(table: string, obj: DynamicObject, options?: {
1208
+ customSelect?: string;
1209
+ }): string;
1200
1210
  /**
1201
1211
  * Creates a query to update an object in a table.
1202
1212
  * @param table Table name to update the object in
package/dist/index.js CHANGED
@@ -14,6 +14,94 @@ import { config } from "@restura/internal";
14
14
  import pino from "pino";
15
15
  import pinoPretty from "pino-pretty";
16
16
 
17
+ // src/restura/RsError.ts
18
+ var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
19
+ HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
20
+ HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
21
+ HtmlStatusCodes2[HtmlStatusCodes2["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED";
22
+ HtmlStatusCodes2[HtmlStatusCodes2["FORBIDDEN"] = 403] = "FORBIDDEN";
23
+ HtmlStatusCodes2[HtmlStatusCodes2["NOT_FOUND"] = 404] = "NOT_FOUND";
24
+ HtmlStatusCodes2[HtmlStatusCodes2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
25
+ HtmlStatusCodes2[HtmlStatusCodes2["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
26
+ HtmlStatusCodes2[HtmlStatusCodes2["CONFLICT"] = 409] = "CONFLICT";
27
+ HtmlStatusCodes2[HtmlStatusCodes2["GONE"] = 410] = "GONE";
28
+ HtmlStatusCodes2[HtmlStatusCodes2["PAYLOAD_TOO_LARGE"] = 413] = "PAYLOAD_TOO_LARGE";
29
+ HtmlStatusCodes2[HtmlStatusCodes2["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
30
+ HtmlStatusCodes2[HtmlStatusCodes2["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED";
31
+ HtmlStatusCodes2[HtmlStatusCodes2["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
32
+ HtmlStatusCodes2[HtmlStatusCodes2["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
33
+ HtmlStatusCodes2[HtmlStatusCodes2["SERVER_ERROR"] = 500] = "SERVER_ERROR";
34
+ HtmlStatusCodes2[HtmlStatusCodes2["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
35
+ HtmlStatusCodes2[HtmlStatusCodes2["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
36
+ HtmlStatusCodes2[HtmlStatusCodes2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
37
+ HtmlStatusCodes2[HtmlStatusCodes2["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
38
+ HtmlStatusCodes2[HtmlStatusCodes2["NETWORK_CONNECT_TIMEOUT"] = 599] = "NETWORK_CONNECT_TIMEOUT";
39
+ return HtmlStatusCodes2;
40
+ })(HtmlStatusCodes || {});
41
+ var RsError = class _RsError extends Error {
42
+ err;
43
+ msg;
44
+ options;
45
+ status;
46
+ constructor(errCode, message, options) {
47
+ super(message);
48
+ this.name = "RsError";
49
+ this.err = errCode;
50
+ this.msg = message || "";
51
+ this.status = _RsError.htmlStatus(errCode);
52
+ this.options = options;
53
+ }
54
+ toJSON() {
55
+ return {
56
+ type: this.name,
57
+ err: this.err,
58
+ message: this.message,
59
+ msg: this.msg,
60
+ status: this.status ?? 500,
61
+ stack: this.stack ?? "",
62
+ options: this.options
63
+ };
64
+ }
65
+ static htmlStatus(code) {
66
+ return htmlStatusMap[code];
67
+ }
68
+ static isRsError(error) {
69
+ return error instanceof _RsError;
70
+ }
71
+ };
72
+ var htmlStatusMap = {
73
+ // 1:1 mappings to HTTP status codes
74
+ BAD_REQUEST: 400 /* BAD_REQUEST */,
75
+ UNAUTHORIZED: 401 /* UNAUTHORIZED */,
76
+ PAYMENT_REQUIRED: 402 /* PAYMENT_REQUIRED */,
77
+ FORBIDDEN: 403 /* FORBIDDEN */,
78
+ NOT_FOUND: 404 /* NOT_FOUND */,
79
+ METHOD_NOT_ALLOWED: 405 /* METHOD_NOT_ALLOWED */,
80
+ REQUEST_TIMEOUT: 408 /* REQUEST_TIMEOUT */,
81
+ CONFLICT: 409 /* CONFLICT */,
82
+ GONE: 410 /* GONE */,
83
+ PAYLOAD_TOO_LARGE: 413 /* PAYLOAD_TOO_LARGE */,
84
+ UNSUPPORTED_MEDIA_TYPE: 415 /* UNSUPPORTED_MEDIA_TYPE */,
85
+ UPGRADE_REQUIRED: 426 /* UPGRADE_REQUIRED */,
86
+ UNPROCESSABLE_ENTITY: 422 /* UNPROCESSABLE_ENTITY */,
87
+ TOO_MANY_REQUESTS: 429 /* TOO_MANY_REQUESTS */,
88
+ SERVER_ERROR: 500 /* SERVER_ERROR */,
89
+ NOT_IMPLEMENTED: 501 /* NOT_IMPLEMENTED */,
90
+ BAD_GATEWAY: 502 /* BAD_GATEWAY */,
91
+ SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
92
+ GATEWAY_TIMEOUT: 504 /* GATEWAY_TIMEOUT */,
93
+ NETWORK_CONNECT_TIMEOUT: 599 /* NETWORK_CONNECT_TIMEOUT */,
94
+ // Specific business errors mapped to appropriate HTTP codes
95
+ UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
96
+ RATE_LIMIT_EXCEEDED: 429 /* TOO_MANY_REQUESTS */,
97
+ INVALID_TOKEN: 401 /* UNAUTHORIZED */,
98
+ INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
99
+ DUPLICATE: 409 /* CONFLICT */,
100
+ CONNECTION_ERROR: 504 /* GATEWAY_TIMEOUT */,
101
+ SCHEMA_ERROR: 500 /* SERVER_ERROR */,
102
+ DATABASE_ERROR: 500 /* SERVER_ERROR */
103
+ };
104
+
17
105
  // src/logger/loggerConfigSchema.ts
18
106
  import { z } from "zod";
19
107
  var loggerConfigSchema = z.object({
@@ -68,6 +156,9 @@ var defaultSerializer = (error) => {
68
156
  responseData: err.response?.data
69
157
  };
70
158
  }
159
+ if (RsError.isRsError(error)) {
160
+ return error.toJSON();
161
+ }
71
162
  return baseSerializer(error);
72
163
  };
73
164
  var errorSerializer = (() => {
@@ -332,83 +423,6 @@ var SqlUtils = class _SqlUtils {
332
423
  }
333
424
  };
334
425
 
335
- // src/restura/RsError.ts
336
- var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
337
- HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
338
- HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
339
- HtmlStatusCodes2[HtmlStatusCodes2["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED";
340
- HtmlStatusCodes2[HtmlStatusCodes2["FORBIDDEN"] = 403] = "FORBIDDEN";
341
- HtmlStatusCodes2[HtmlStatusCodes2["NOT_FOUND"] = 404] = "NOT_FOUND";
342
- HtmlStatusCodes2[HtmlStatusCodes2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
343
- HtmlStatusCodes2[HtmlStatusCodes2["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
344
- HtmlStatusCodes2[HtmlStatusCodes2["CONFLICT"] = 409] = "CONFLICT";
345
- HtmlStatusCodes2[HtmlStatusCodes2["GONE"] = 410] = "GONE";
346
- HtmlStatusCodes2[HtmlStatusCodes2["PAYLOAD_TOO_LARGE"] = 413] = "PAYLOAD_TOO_LARGE";
347
- HtmlStatusCodes2[HtmlStatusCodes2["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
348
- HtmlStatusCodes2[HtmlStatusCodes2["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED";
349
- HtmlStatusCodes2[HtmlStatusCodes2["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
350
- HtmlStatusCodes2[HtmlStatusCodes2["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
351
- HtmlStatusCodes2[HtmlStatusCodes2["SERVER_ERROR"] = 500] = "SERVER_ERROR";
352
- HtmlStatusCodes2[HtmlStatusCodes2["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
353
- HtmlStatusCodes2[HtmlStatusCodes2["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
354
- HtmlStatusCodes2[HtmlStatusCodes2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
355
- HtmlStatusCodes2[HtmlStatusCodes2["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
356
- HtmlStatusCodes2[HtmlStatusCodes2["NETWORK_CONNECT_TIMEOUT"] = 599] = "NETWORK_CONNECT_TIMEOUT";
357
- return HtmlStatusCodes2;
358
- })(HtmlStatusCodes || {});
359
- var RsError = class _RsError {
360
- err;
361
- msg;
362
- options;
363
- status;
364
- stack;
365
- constructor(errCode, message, options) {
366
- this.err = errCode;
367
- this.msg = message || "";
368
- this.status = _RsError.htmlStatus(errCode);
369
- this.stack = new Error().stack || "";
370
- this.options = options;
371
- }
372
- static htmlStatus(code) {
373
- return htmlStatusMap[code];
374
- }
375
- static isRsError(error) {
376
- return error instanceof _RsError;
377
- }
378
- };
379
- var htmlStatusMap = {
380
- // 1:1 mappings to HTTP status codes
381
- BAD_REQUEST: 400 /* BAD_REQUEST */,
382
- UNAUTHORIZED: 401 /* UNAUTHORIZED */,
383
- PAYMENT_REQUIRED: 402 /* PAYMENT_REQUIRED */,
384
- FORBIDDEN: 403 /* FORBIDDEN */,
385
- NOT_FOUND: 404 /* NOT_FOUND */,
386
- METHOD_NOT_ALLOWED: 405 /* METHOD_NOT_ALLOWED */,
387
- REQUEST_TIMEOUT: 408 /* REQUEST_TIMEOUT */,
388
- CONFLICT: 409 /* CONFLICT */,
389
- GONE: 410 /* GONE */,
390
- PAYLOAD_TOO_LARGE: 413 /* PAYLOAD_TOO_LARGE */,
391
- UNSUPPORTED_MEDIA_TYPE: 415 /* UNSUPPORTED_MEDIA_TYPE */,
392
- UPGRADE_REQUIRED: 426 /* UPGRADE_REQUIRED */,
393
- UNPROCESSABLE_ENTITY: 422 /* UNPROCESSABLE_ENTITY */,
394
- TOO_MANY_REQUESTS: 429 /* TOO_MANY_REQUESTS */,
395
- SERVER_ERROR: 500 /* SERVER_ERROR */,
396
- NOT_IMPLEMENTED: 501 /* NOT_IMPLEMENTED */,
397
- BAD_GATEWAY: 502 /* BAD_GATEWAY */,
398
- SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
399
- GATEWAY_TIMEOUT: 504 /* GATEWAY_TIMEOUT */,
400
- NETWORK_CONNECT_TIMEOUT: 599 /* NETWORK_CONNECT_TIMEOUT */,
401
- // Specific business errors mapped to appropriate HTTP codes
402
- UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
403
- RATE_LIMIT_EXCEEDED: 429 /* TOO_MANY_REQUESTS */,
404
- INVALID_TOKEN: 401 /* UNAUTHORIZED */,
405
- INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
406
- DUPLICATE: 409 /* CONFLICT */,
407
- CONNECTION_ERROR: 504 /* GATEWAY_TIMEOUT */,
408
- SCHEMA_ERROR: 500 /* SERVER_ERROR */,
409
- DATABASE_ERROR: 500 /* SERVER_ERROR */
410
- };
411
-
412
426
  // src/restura/validators/ResponseValidator.ts
413
427
  var ResponseValidator = class _ResponseValidator {
414
428
  rootMap;
@@ -1910,15 +1924,25 @@ function questionMarksToOrderedParams(query) {
1910
1924
  return char;
1911
1925
  });
1912
1926
  }
1913
- function insertObjectQuery(table, obj) {
1927
+ function insertObjectQuery(table, obj, options) {
1928
+ const { customSelect } = options ?? {};
1914
1929
  const keys = Object.keys(obj);
1915
1930
  const params = Object.values(obj);
1916
1931
  const columns = keys.map((column) => escapeColumnName(column)).join(", ");
1917
1932
  const values = params.map((value) => SQL`${value}`).join(", ");
1918
1933
  let query = `
1919
- INSERT INTO "${table}" (${columns})
1920
- VALUES (${values})
1921
- RETURNING *`;
1934
+ INSERT INTO "${table}" (${columns})
1935
+ VALUES (${values})
1936
+ RETURNING *`;
1937
+ if (customSelect) {
1938
+ query = `
1939
+ WITH inserted AS (
1940
+ INSERT INTO "${table}" (${columns})
1941
+ VALUES (${values})
1942
+ RETURNING *
1943
+ )
1944
+ ${customSelect}`;
1945
+ }
1922
1946
  query = query.replace(/'(\?)'/g, "?");
1923
1947
  return query;
1924
1948
  }
@@ -2285,6 +2309,35 @@ var initializers = `
2285
2309
  }
2286
2310
  return format.literal(value);
2287
2311
  }
2312
+
2313
+ // Format a value with optional type cast
2314
+ function formatValueWithCast(rawValue, cast) {
2315
+ var formatted = formatValue(unescapeValue(rawValue));
2316
+ return cast ? formatted + '::' + cast : formatted;
2317
+ }
2318
+
2319
+ // Build SQL IN clause from pipe-separated values with optional cast
2320
+ function buildInClauseWithCast(column, rawValue, cast) {
2321
+ var values = splitPipeValues(rawValue);
2322
+ var literals = values.map(function(v) {
2323
+ var formatted = formatValue(v);
2324
+ return cast ? formatted + '::' + cast : formatted;
2325
+ });
2326
+ return column + ' IN (' + literals.join(', ') + ')';
2327
+ }
2328
+
2329
+ // Format column with optional cast
2330
+ function formatColumn(col) {
2331
+ if (!col.cast) {
2332
+ return col.sql;
2333
+ }
2334
+ // Wrap JSON field extractions in parentheses when casting
2335
+ // because :: has higher precedence than ->>
2336
+ if (col.isJsonField) {
2337
+ return '(' + col.sql + ')::' + col.cast;
2338
+ }
2339
+ return col.sql + '::' + col.cast;
2340
+ }
2288
2341
  `;
2289
2342
  var entryGrammar = `
2290
2343
  {
@@ -2319,7 +2372,7 @@ OldOperator
2319
2372
  = "and"i / "or"i
2320
2373
 
2321
2374
  OldColumn
2322
- = first:OldText rest:("." OldText)* {
2375
+ = first:OldColumnPart rest:("." OldColumnPart)* {
2323
2376
  const partsArray = [first];
2324
2377
  if (rest && rest.length > 0) {
2325
2378
  partsArray.push(...rest.map(item => item[1]));
@@ -2347,20 +2400,25 @@ OldColumn
2347
2400
  return result;
2348
2401
  }
2349
2402
 
2350
- OldText
2403
+ OldColumnPart
2351
2404
  = text:[a-z0-9 \\t\\r\\n\\-_:@']i+ {
2352
2405
  return text.join("");
2353
2406
  }
2354
2407
 
2408
+ OldText
2409
+ = text:[a-z0-9 \\t\\r\\n\\-_:@'.]i+ {
2410
+ return text.join("");
2411
+ }
2412
+
2355
2413
  OldType
2356
2414
  = "type" _ ":" _ type:OldTypeString {
2357
2415
  return type;
2358
2416
  }
2359
2417
 
2360
2418
  OldTypeString
2361
- = text:"startsWith" { return function(column, value) { return \`\${column}::text ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } }
2362
- / text:"endsWith" { return function(column, value) { return \`\${column}::text ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } }
2363
- / text:"contains" { return function(column, value) { return \`\${column}::text ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } }
2419
+ = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } }
2420
+ / text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } }
2421
+ / text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } }
2364
2422
  / text:"exact" { return function(column, value) { return \`\${column} = \${formatValue(value)}\`; } }
2365
2423
  / text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= \${formatValue(value)}\`; } }
2366
2424
  / text:"greaterThan" { return function(column, value) { return \`\${column} > \${formatValue(value)}\`; } }
@@ -2397,11 +2455,11 @@ SimpleExpr
2397
2455
  { return (negate ? 'NOT ' : '') + '(' + op(col) + ')'; }
2398
2456
  / negate:"!"? _ "(" _ col:Column _ "," _ op:NullOperator _ ")" _
2399
2457
  { return (negate ? 'NOT ' : '') + '(' + op(col) + ')'; }
2400
- / negate:"!"? _ "(" _ col:Column _ "," _ val:Value _ ")" _
2401
- { return (negate ? 'NOT ' : '') + '(' + col + ' = ' + formatValue(unescapeValue(val)) + ')'; }
2458
+ / negate:"!"? _ "(" _ col:Column _ "," _ val:CastedValue _ ")" _
2459
+ { return (negate ? 'NOT ' : '') + '(' + formatColumn(col) + ' = ' + formatValueWithCast(val.value, val.cast) + ')'; }
2402
2460
 
2403
2461
  Column
2404
- = first:ColPart rest:("." ColPart)* {
2462
+ = first:ColPart rest:("." ColPart)* cast:TypeCast? {
2405
2463
  const partsArray = [first];
2406
2464
  if (rest && rest.length > 0) {
2407
2465
  partsArray.push(...rest.map(item => item[1]));
@@ -2411,38 +2469,54 @@ Column
2411
2469
  throw new SyntaxError('Column path cannot have more than 3 parts (table.column.jsonField)');
2412
2470
  }
2413
2471
 
2472
+ var sql;
2473
+ var isJsonField = false;
2414
2474
  if (partsArray.length === 1) {
2415
- return quoteSqlIdentity(partsArray[0]);
2416
- }
2417
- const tableName = quoteSqlIdentity(partsArray[0]);
2418
-
2419
- if (partsArray.length === 2) {
2420
- return tableName + '.' + quoteSqlIdentity(partsArray[1]);
2475
+ sql = quoteSqlIdentity(partsArray[0]);
2476
+ } else {
2477
+ const tableName = quoteSqlIdentity(partsArray[0]);
2478
+
2479
+ if (partsArray.length === 2) {
2480
+ sql = tableName + '.' + quoteSqlIdentity(partsArray[1]);
2481
+ } else {
2482
+ const jsonColumn = quoteSqlIdentity(partsArray[1]);
2483
+ const lastPart = partsArray[partsArray.length - 1];
2484
+ const escapedLast = lastPart.replace(/'/g, "''");
2485
+ sql = tableName + '.' + jsonColumn + "->>'" + escapedLast + "'";
2486
+ isJsonField = true;
2487
+ }
2421
2488
  }
2422
2489
 
2423
- const jsonColumn = quoteSqlIdentity(partsArray[1]);
2424
- const lastPart = partsArray[partsArray.length - 1];
2425
- const escapedLast = lastPart.replace(/'/g, "''");
2426
- return tableName + '.' + jsonColumn + "->>'" + escapedLast + "'";
2490
+ return { sql: sql, cast: cast, isJsonField: isJsonField };
2427
2491
  }
2428
2492
 
2429
2493
  ColPart
2430
2494
  = chars:[a-zA-Z0-9_]+ { return chars.join(''); }
2431
2495
 
2432
2496
  NullOperator
2433
- = "notnull"i { return function(col) { return col + ' IS NOT NULL'; }; }
2434
- / "null"i { return function(col) { return col + ' IS NULL'; }; }
2497
+ = "notnull"i { return function(col) { return formatColumn(col) + ' IS NOT NULL'; }; }
2498
+ / "null"i { return function(col) { return formatColumn(col) + ' IS NULL'; }; }
2435
2499
 
2436
2500
  OperatorWithValue
2437
- = "in"i _ "," _ val:ValueWithPipes { return function(col) { return buildInClause(col, val); }; }
2438
- / "ne"i _ "," _ val:Value { return function(col) { return col + ' <> ' + formatValue(unescapeValue(val)); }; }
2439
- / "gte"i _ "," _ val:Value { return function(col) { return col + ' >= ' + formatValue(unescapeValue(val)); }; }
2440
- / "gt"i _ "," _ val:Value { return function(col) { return col + ' > ' + formatValue(unescapeValue(val)); }; }
2441
- / "lte"i _ "," _ val:Value { return function(col) { return col + ' <= ' + formatValue(unescapeValue(val)); }; }
2442
- / "lt"i _ "," _ val:Value { return function(col) { return col + ' < ' + formatValue(unescapeValue(val)); }; }
2443
- / "has"i _ "," _ val:Value { return function(col) { return col + '::text ILIKE ' + format.literal('%' + unescapeValue(val) + '%'); }; }
2444
- / "sw"i _ "," _ val:Value { return function(col) { return col + '::text ILIKE ' + format.literal(unescapeValue(val) + '%'); }; }
2445
- / "ew"i _ "," _ val:Value { return function(col) { return col + '::text ILIKE ' + format.literal('%' + unescapeValue(val)); }; }
2501
+ = "in"i _ "," _ val:CastedValueWithPipes { return function(col) { return buildInClauseWithCast(formatColumn(col), val.value, val.cast); }; }
2502
+ / "ne"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' <> ' + formatValueWithCast(val.value, val.cast); }; }
2503
+ / "gte"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' >= ' + formatValueWithCast(val.value, val.cast); }; }
2504
+ / "gt"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' > ' + formatValueWithCast(val.value, val.cast); }; }
2505
+ / "lte"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' <= ' + formatValueWithCast(val.value, val.cast); }; }
2506
+ / "lt"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' < ' + formatValueWithCast(val.value, val.cast); }; }
2507
+ / "has"i _ "," _ val:CastedValue { return function(col) { var formatted = format.literal('%' + unescapeValue(val.value) + '%'); return formatColumn(col) + ' ILIKE ' + (val.cast ? formatted + '::' + val.cast : formatted); }; }
2508
+ / "sw"i _ "," _ val:CastedValue { return function(col) { var formatted = format.literal(unescapeValue(val.value) + '%'); return formatColumn(col) + ' ILIKE ' + (val.cast ? formatted + '::' + val.cast : formatted); }; }
2509
+ / "ew"i _ "," _ val:CastedValue { return function(col) { var formatted = format.literal('%' + unescapeValue(val.value)); return formatColumn(col) + ' ILIKE ' + (val.cast ? formatted + '::' + val.cast : formatted); }; }
2510
+
2511
+ CastedValue
2512
+ = val:Value cast:TypeCast? { return { value: val, cast: cast }; }
2513
+
2514
+ CastedValueWithPipes
2515
+ = val:ValueWithPipes cast:TypeCast? { return { value: val, cast: cast }; }
2516
+
2517
+ TypeCast
2518
+ = "::" type:("timestamptz"i / "timestamp"i / "boolean"i / "numeric"i / "bigint"i / "text"i / "date"i / "int"i)
2519
+ { return type.toLowerCase(); }
2446
2520
 
2447
2521
  Value
2448
2522
  = chars:ValueChar+ { return chars.join(''); }
@@ -2451,7 +2525,8 @@ ValueChar
2451
2525
  = "\\\\\\\\" { return '\\\\\\\\'; }
2452
2526
  / "\\\\," { return '\\\\,'; }
2453
2527
  / "\\\\|" { return '\\\\|'; }
2454
- / [^,()\\\\|]
2528
+ / [^,()\\\\|:]
2529
+ / c:":" !":" { return c; }
2455
2530
 
2456
2531
  ValueWithPipes
2457
2532
  = chars:ValueWithPipesChar+ { return chars.join(''); }
@@ -2460,7 +2535,8 @@ ValueWithPipesChar
2460
2535
  = "\\\\\\\\" { return '\\\\\\\\'; }
2461
2536
  / "\\\\," { return '\\\\,'; }
2462
2537
  / "\\\\|" { return '\\\\|'; }
2463
- / [^,()\\\\]
2538
+ / [^,()\\\\:]
2539
+ / c:":" !":" { return c; }
2464
2540
  `;
2465
2541
  var fullGrammar = entryGrammar + oldGrammar + newGrammar;
2466
2542
  var filterPsqlParser = peg.generate(fullGrammar, {