@restura/core 1.1.1 → 1.3.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 +17 -6
- package/dist/index.js +93 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SerializerFn } from 'pino';
|
|
1
|
+
import { TransportTargetOptions, SerializerFn, DestinationStream } from 'pino';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { UUID } from 'crypto';
|
|
4
4
|
import * as express from 'express';
|
|
@@ -32,14 +32,11 @@ declare const loggerConfigSchema: z.ZodObject<{
|
|
|
32
32
|
silly: "silly";
|
|
33
33
|
trace: "trace";
|
|
34
34
|
}>>;
|
|
35
|
-
transports: z.ZodOptional<z.ZodArray<z.
|
|
36
|
-
target: z.ZodString;
|
|
37
|
-
level: z.ZodOptional<z.ZodString>;
|
|
38
|
-
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
39
|
-
}, z.core.$strip>>>;
|
|
35
|
+
transports: z.ZodOptional<z.ZodArray<z.ZodCustom<TransportTargetOptions<Record<string, any>>, TransportTargetOptions<Record<string, any>>>>>;
|
|
40
36
|
serializers: z.ZodOptional<z.ZodObject<{
|
|
41
37
|
err: z.ZodOptional<z.ZodCustom<ErrorSerializerFactory, ErrorSerializerFactory>>;
|
|
42
38
|
}, z.core.$strip>>;
|
|
39
|
+
stream: z.ZodOptional<z.ZodCustom<DestinationStream, DestinationStream>>;
|
|
43
40
|
}, z.core.$strip>;
|
|
44
41
|
type LoggerConfigSchema = z.infer<typeof loggerConfigSchema>;
|
|
45
42
|
|
|
@@ -1073,6 +1070,20 @@ declare abstract class SqlEngine {
|
|
|
1073
1070
|
protected abstract generateOrderBy(req: RsRequest<unknown>, routeData: StandardRouteData): string;
|
|
1074
1071
|
protected abstract generateWhereClause(req: RsRequest<unknown>, where: WhereData[], routeData: StandardRouteData, sqlParams: string[]): string;
|
|
1075
1072
|
protected replaceParamKeywords(value: string | number, routeData: RouteData, req: RsRequest<unknown>, sqlParams: string[]): string | number;
|
|
1073
|
+
/**
|
|
1074
|
+
* Replaces local parameter keywords (e.g., $paramName) in SQL query strings with '?' placeholders
|
|
1075
|
+
* and adds the corresponding parameter values to the sqlParams array for parameterized queries.
|
|
1076
|
+
*
|
|
1077
|
+
* Validates that each parameter keyword exists in the route's request schema before processing.
|
|
1078
|
+
* If the value is not a string or routeData has no request schema, returns the value unchanged.
|
|
1079
|
+
*
|
|
1080
|
+
* @param value - The string or number value that may contain parameter keywords (e.g., "$userId")
|
|
1081
|
+
* @param routeData - The route data containing the request schema for parameter validation
|
|
1082
|
+
* @param req - The request object containing the actual parameter values in req.data
|
|
1083
|
+
* @param sqlParams - Array to which parameter values are appended (modified by reference)
|
|
1084
|
+
* @returns The value with parameter keywords replaced by '?' placeholders, or the original value if unchanged
|
|
1085
|
+
* @throws {RsError} If a parameter keyword is found that doesn't exist in the route's request schema
|
|
1086
|
+
*/
|
|
1076
1087
|
protected replaceLocalParamKeywords(value: string | number, routeData: RouteData, req: RsRequest<unknown>, sqlParams: string[]): string | number;
|
|
1077
1088
|
protected replaceGlobalParamKeywords(value: string | number, routeData: RouteData, req: RsRequest<unknown>, sqlParams: string[]): string | number;
|
|
1078
1089
|
abstract generateDatabaseSchemaFromSchema(schema: ResturaSchema): string;
|
package/dist/index.js
CHANGED
|
@@ -12,21 +12,20 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
12
12
|
// src/logger/logger.ts
|
|
13
13
|
import { config } from "@restura/internal";
|
|
14
14
|
import pino from "pino";
|
|
15
|
+
import pinoPretty from "pino-pretty";
|
|
15
16
|
|
|
16
17
|
// src/logger/loggerConfigSchema.ts
|
|
17
18
|
import { z } from "zod";
|
|
18
19
|
var loggerConfigSchema = z.object({
|
|
19
20
|
level: z.enum(["fatal", "error", "warn", "info", "debug", "silly", "trace"]).default("info"),
|
|
20
|
-
transports: z.array(
|
|
21
|
-
z.object({
|
|
22
|
-
target: z.string(),
|
|
23
|
-
level: z.string().optional(),
|
|
24
|
-
options: z.record(z.string(), z.unknown()).optional()
|
|
25
|
-
})
|
|
26
|
-
).optional(),
|
|
21
|
+
transports: z.array(z.custom()).optional(),
|
|
27
22
|
serializers: z.object({
|
|
28
23
|
err: z.custom().optional()
|
|
29
|
-
}).optional()
|
|
24
|
+
}).optional(),
|
|
25
|
+
stream: z.custom().optional()
|
|
26
|
+
}).refine((data) => !(data.transports && data.stream), {
|
|
27
|
+
message: "You must provide either a transports array or a stream object, but not both",
|
|
28
|
+
path: ["transports"]
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
// src/logger/logger.ts
|
|
@@ -41,38 +40,54 @@ var logLevelMap = {
|
|
|
41
40
|
trace: "trace"
|
|
42
41
|
};
|
|
43
42
|
var currentLogLevel = logLevelMap[loggerConfig.level];
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
var
|
|
43
|
+
var defaultStream = pinoPretty({
|
|
44
|
+
colorize: true,
|
|
45
|
+
translateTime: "yyyy-mm-dd HH:MM:ss.l",
|
|
46
|
+
ignore: "pid,hostname,_meta",
|
|
47
|
+
// _meta allows a user to pass in metadata for JSON but not print it to the console
|
|
48
|
+
messageFormat: "{msg}",
|
|
49
|
+
levelFirst: true,
|
|
50
|
+
customColors: "error:red,warn:yellow,info:green,debug:blue,trace:magenta",
|
|
51
|
+
destination: process.stdout
|
|
52
|
+
});
|
|
53
|
+
function isAxiosError(error) {
|
|
54
|
+
const isObject = (error2) => error2 !== null && typeof error2 === "object";
|
|
55
|
+
return isObject(error) && "isAxiosError" in error && error.isAxiosError === true;
|
|
56
|
+
}
|
|
57
|
+
var baseSerializer = pino.stdSerializers.err;
|
|
58
|
+
var defaultSerializer = (error) => {
|
|
59
|
+
if (isAxiosError(error)) {
|
|
60
|
+
const err = error;
|
|
61
|
+
return {
|
|
62
|
+
type: "AxiosError",
|
|
63
|
+
message: err.message,
|
|
64
|
+
stack: err.stack,
|
|
65
|
+
url: err.config?.url,
|
|
66
|
+
method: err.config?.method?.toUpperCase(),
|
|
67
|
+
status: err.response?.status,
|
|
68
|
+
responseData: err.response?.data
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return baseSerializer(error);
|
|
72
|
+
};
|
|
59
73
|
var errorSerializer = (() => {
|
|
60
74
|
try {
|
|
61
|
-
return loggerConfig.serializers?.err ? loggerConfig.serializers.err(
|
|
75
|
+
return loggerConfig.serializers?.err ? loggerConfig.serializers.err(baseSerializer) : defaultSerializer;
|
|
62
76
|
} catch (error) {
|
|
63
77
|
console.error("Failed to initialize custom error serializer, falling back to default", error);
|
|
64
|
-
return
|
|
78
|
+
return defaultSerializer;
|
|
65
79
|
}
|
|
66
80
|
})();
|
|
67
|
-
var pinoLogger = pino(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
targets: loggerConfig.transports
|
|
81
|
+
var pinoLogger = pino(
|
|
82
|
+
{
|
|
83
|
+
level: currentLogLevel,
|
|
84
|
+
...loggerConfig.transports ? { transport: { targets: loggerConfig.transports } } : {},
|
|
85
|
+
serializers: {
|
|
86
|
+
err: errorSerializer
|
|
87
|
+
}
|
|
71
88
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
});
|
|
89
|
+
loggerConfig.stream ? loggerConfig.stream : defaultStream
|
|
90
|
+
);
|
|
76
91
|
function buildContext(args) {
|
|
77
92
|
const ctx = {};
|
|
78
93
|
const prims = [];
|
|
@@ -2027,6 +2042,20 @@ var SqlEngine = class {
|
|
|
2027
2042
|
returnValue = this.replaceGlobalParamKeywords(returnValue, routeData, req, sqlParams);
|
|
2028
2043
|
return returnValue;
|
|
2029
2044
|
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Replaces local parameter keywords (e.g., $paramName) in SQL query strings with '?' placeholders
|
|
2047
|
+
* and adds the corresponding parameter values to the sqlParams array for parameterized queries.
|
|
2048
|
+
*
|
|
2049
|
+
* Validates that each parameter keyword exists in the route's request schema before processing.
|
|
2050
|
+
* If the value is not a string or routeData has no request schema, returns the value unchanged.
|
|
2051
|
+
*
|
|
2052
|
+
* @param value - The string or number value that may contain parameter keywords (e.g., "$userId")
|
|
2053
|
+
* @param routeData - The route data containing the request schema for parameter validation
|
|
2054
|
+
* @param req - The request object containing the actual parameter values in req.data
|
|
2055
|
+
* @param sqlParams - Array to which parameter values are appended (modified by reference)
|
|
2056
|
+
* @returns The value with parameter keywords replaced by '?' placeholders, or the original value if unchanged
|
|
2057
|
+
* @throws {RsError} If a parameter keyword is found that doesn't exist in the route's request schema
|
|
2058
|
+
*/
|
|
2030
2059
|
replaceLocalParamKeywords(value, routeData, req, sqlParams) {
|
|
2031
2060
|
if (!routeData.request) return value;
|
|
2032
2061
|
const data = req.data;
|
|
@@ -2605,7 +2634,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2605
2634
|
}
|
|
2606
2635
|
async executeUpdateRequest(req, routeData, schema) {
|
|
2607
2636
|
const sqlParams = [];
|
|
2608
|
-
const { id, ...bodyNoId } = req.body;
|
|
2637
|
+
const { id, baseModifiedOn, ...bodyNoId } = req.body;
|
|
2609
2638
|
const table = schema.database.find((item) => {
|
|
2610
2639
|
return item.name === routeData.table;
|
|
2611
2640
|
});
|
|
@@ -2621,9 +2650,37 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
2621
2650
|
bodyNoId[assignmentEscaped] = Number(assignment.value);
|
|
2622
2651
|
else bodyNoId[assignmentEscaped] = assignment.value;
|
|
2623
2652
|
}
|
|
2624
|
-
|
|
2653
|
+
let whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
|
|
2654
|
+
const originalWhereClause = whereClause;
|
|
2655
|
+
const originalSqlParams = [...sqlParams];
|
|
2656
|
+
if (baseModifiedOn) {
|
|
2657
|
+
const replacedBaseModifiedOn = this.replaceParamKeywords(baseModifiedOn, routeData, req, sqlParams);
|
|
2658
|
+
const modifiedOnCheck = whereClause ? `${whereClause} AND "modifiedOn" = ?` : `"modifiedOn" = ?`;
|
|
2659
|
+
sqlParams.push(replacedBaseModifiedOn.toString());
|
|
2660
|
+
whereClause = modifiedOnCheck;
|
|
2661
|
+
}
|
|
2625
2662
|
const query = updateObjectQuery(routeData.table, bodyNoId, whereClause);
|
|
2626
|
-
|
|
2663
|
+
try {
|
|
2664
|
+
await this.psqlConnectionPool.queryOne(query, [...sqlParams], req.requesterDetails);
|
|
2665
|
+
} catch (error) {
|
|
2666
|
+
if (!baseModifiedOn || !(error instanceof RsError) || error.err !== "NOT_FOUND") throw error;
|
|
2667
|
+
let isConflict = false;
|
|
2668
|
+
try {
|
|
2669
|
+
await this.psqlConnectionPool.queryOne(
|
|
2670
|
+
`SELECT 1 FROM "${routeData.table}" ${originalWhereClause};`,
|
|
2671
|
+
originalSqlParams,
|
|
2672
|
+
req.requesterDetails
|
|
2673
|
+
);
|
|
2674
|
+
isConflict = true;
|
|
2675
|
+
} catch {
|
|
2676
|
+
}
|
|
2677
|
+
if (isConflict)
|
|
2678
|
+
throw new RsError(
|
|
2679
|
+
"CONFLICT",
|
|
2680
|
+
"The record has been modified since the baseModifiedOn value was provided."
|
|
2681
|
+
);
|
|
2682
|
+
throw error;
|
|
2683
|
+
}
|
|
2627
2684
|
return this.executeGetRequest(req, routeData, schema);
|
|
2628
2685
|
}
|
|
2629
2686
|
async executeDeleteRequest(req, routeData, schema) {
|