@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 +15 -5
- package/dist/index.js +188 -112
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
1197
|
-
* @
|
|
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
|
|
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
|
-
|
|
1921
|
-
|
|
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:
|
|
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
|
-
|
|
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}
|
|
2362
|
-
/ text:"endsWith" { return function(column, value) { return \`\${column}
|
|
2363
|
-
/ text:"contains" { return function(column, value) { return \`\${column}
|
|
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:
|
|
2401
|
-
{ return (negate ? 'NOT ' : '') + '(' + col + ' = ' +
|
|
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
|
-
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
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
|
-
|
|
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:
|
|
2438
|
-
/ "ne"i _ "," _ val:
|
|
2439
|
-
/ "gte"i _ "," _ val:
|
|
2440
|
-
/ "gt"i _ "," _ val:
|
|
2441
|
-
/ "lte"i _ "," _ val:
|
|
2442
|
-
/ "lt"i _ "," _ val:
|
|
2443
|
-
/ "has"i _ "," _ val:
|
|
2444
|
-
/ "sw"i _ "," _ val:
|
|
2445
|
-
/ "ew"i _ "," _ 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, {
|