@uityu/uityu-shared 1.0.2 → 1.0.5
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/common/domain/index.d.ts +1 -0
- package/dist/common/domain/index.js +1 -0
- package/dist/common/domain/math/clamp-limit.d.ts +12 -0
- package/dist/common/domain/math/clamp-limit.js +15 -0
- package/dist/common/domain/math/index.d.ts +1 -0
- package/dist/common/domain/math/index.js +1 -0
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.js +2 -0
- package/dist/common/infrastructure/index.d.ts +1 -0
- package/dist/common/infrastructure/index.js +1 -0
- package/dist/common/infrastructure/logging/index.d.ts +1 -0
- package/dist/common/infrastructure/logging/index.js +1 -0
- package/dist/common/infrastructure/logging/serialize-log-payload.d.ts +11 -0
- package/dist/common/infrastructure/logging/serialize-log-payload.js +82 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +34 -1
- package/src/common/domain/index.ts +1 -0
- package/src/common/domain/math/clamp-limit.ts +27 -0
- package/src/common/domain/math/index.ts +1 -0
- package/src/common/index.ts +2 -0
- package/src/common/infrastructure/index.ts +1 -0
- package/src/common/infrastructure/logging/index.ts +1 -0
- package/src/common/infrastructure/logging/serialize-log-payload.ts +116 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./math";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./math";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ClampOptions {
|
|
2
|
+
/** Value to use when the input is undefined or not finite */
|
|
3
|
+
defaultValue: number;
|
|
4
|
+
/** Upper bound; must be a finite number */
|
|
5
|
+
max: number;
|
|
6
|
+
/** Optional lower bound; defaults to 1 */
|
|
7
|
+
min?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Domain-level guard to clamp numeric limits while providing sane defaults.
|
|
11
|
+
*/
|
|
12
|
+
export declare function clampLimit(value: number | undefined, options: ClampOptions): number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-level guard to clamp numeric limits while providing sane defaults.
|
|
3
|
+
*/
|
|
4
|
+
export function clampLimit(value, options) {
|
|
5
|
+
const normalized = toFiniteNumber(value, options.defaultValue);
|
|
6
|
+
const min = toFiniteNumber(options.min, 1);
|
|
7
|
+
const max = toFiniteNumber(options.max, min);
|
|
8
|
+
if (max < min) {
|
|
9
|
+
throw new Error("Clamp max must be greater than or equal to min");
|
|
10
|
+
}
|
|
11
|
+
return Math.min(max, Math.max(min, normalized));
|
|
12
|
+
}
|
|
13
|
+
function toFiniteNumber(value, fallback) {
|
|
14
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./clamp-limit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./clamp-limit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./logging";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./logging";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./serialize-log-payload";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./serialize-log-payload";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type LogLevel = "INFO" | "ERROR";
|
|
2
|
+
export type LogPayload = {
|
|
3
|
+
namespace: string;
|
|
4
|
+
message: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
error?: unknown;
|
|
7
|
+
};
|
|
8
|
+
export declare function serializeLogPayload(payload: unknown): Record<string, unknown>;
|
|
9
|
+
export declare function logInfo(payload: LogPayload): void;
|
|
10
|
+
export declare function logError(payload: LogPayload): void;
|
|
11
|
+
export declare function logFirestoreUsage(collection: string, operation: string, metadata?: Record<string, unknown>, hit?: boolean): void;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export function serializeLogPayload(payload) {
|
|
2
|
+
if (!isSerializableObject(payload)) {
|
|
3
|
+
return {};
|
|
4
|
+
}
|
|
5
|
+
try {
|
|
6
|
+
const serialized = JSON.parse(JSON.stringify(payload, (_key, value) => normalizeValue(value)));
|
|
7
|
+
if (isSerializableObject(serialized)) {
|
|
8
|
+
return serialized;
|
|
9
|
+
}
|
|
10
|
+
return { value: serialized };
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return { value: "[unserializable]" };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function logInfo(payload) {
|
|
17
|
+
log("INFO", payload);
|
|
18
|
+
}
|
|
19
|
+
export function logError(payload) {
|
|
20
|
+
log("ERROR", payload);
|
|
21
|
+
}
|
|
22
|
+
export function logFirestoreUsage(collection, operation, metadata = {}, hit) {
|
|
23
|
+
logInfo({
|
|
24
|
+
namespace: "FirestoreUsage",
|
|
25
|
+
message: `${collection}.${operation}`,
|
|
26
|
+
metadata: { ...metadata, hit },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function log(level, payload) {
|
|
30
|
+
const entry = {
|
|
31
|
+
level,
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
namespace: payload.namespace,
|
|
34
|
+
message: payload.message,
|
|
35
|
+
metadata: serializeLogPayload(payload.metadata ?? {}),
|
|
36
|
+
error: normalizeError(payload.error),
|
|
37
|
+
};
|
|
38
|
+
const serialized = JSON.stringify(entry);
|
|
39
|
+
if (level === "ERROR") {
|
|
40
|
+
console.error(serialized);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log(serialized);
|
|
44
|
+
}
|
|
45
|
+
function normalizeError(error) {
|
|
46
|
+
if (!error) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
if (error instanceof Error) {
|
|
50
|
+
return {
|
|
51
|
+
name: error.name,
|
|
52
|
+
message: error.message,
|
|
53
|
+
stack: error.stack,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return serializeLogPayload(error);
|
|
57
|
+
}
|
|
58
|
+
function normalizeValue(value) {
|
|
59
|
+
if (value instanceof Date) {
|
|
60
|
+
return value.toISOString();
|
|
61
|
+
}
|
|
62
|
+
if (typeof value === "bigint") {
|
|
63
|
+
return value.toString();
|
|
64
|
+
}
|
|
65
|
+
if (hasToJSON(value)) {
|
|
66
|
+
try {
|
|
67
|
+
return value.toJSON();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return "[unserializable]";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
function hasToJSON(value) {
|
|
76
|
+
return Boolean(value && typeof value === "object" &&
|
|
77
|
+
"toJSON" in value &&
|
|
78
|
+
typeof value.toJSON === "function");
|
|
79
|
+
}
|
|
80
|
+
function isSerializableObject(value) {
|
|
81
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
82
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { toApiError } from "./http/error-mapper";
|
|
|
5
5
|
export type { ApiErrorResponse } from "./http/error-mapper";
|
|
6
6
|
export { createEmptySearchIndex, normalizeSearchText, buildSearchPrefixes, toSearchPrefix, normalizePhoneDigits, } from "./search/search-index";
|
|
7
7
|
export type { SearchIndex } from "./search/search-index";
|
|
8
|
+
export * from "./common";
|
package/dist/index.js
CHANGED
|
@@ -3,3 +3,4 @@ export { AppError } from "./errors/app-error";
|
|
|
3
3
|
export { AppErrorFactory } from "./errors/app-error.factory";
|
|
4
4
|
export { toApiError } from "./http/error-mapper";
|
|
5
5
|
export { createEmptySearchIndex, normalizeSearchText, buildSearchPrefixes, toSearchPrefix, normalizePhoneDigits, } from "./search/search-index";
|
|
6
|
+
export * from "./common";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uityu/uityu-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,8 +18,41 @@
|
|
|
18
18
|
"types": "./dist/search/search-index.d.ts",
|
|
19
19
|
"import": "./dist/search/search-index.js"
|
|
20
20
|
},
|
|
21
|
+
"./common": {
|
|
22
|
+
"types": "./dist/common/index.d.ts",
|
|
23
|
+
"import": "./dist/common/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./common/*": {
|
|
26
|
+
"types": "./dist/common/*/index.d.ts",
|
|
27
|
+
"import": "./dist/common/*/index.js"
|
|
28
|
+
},
|
|
21
29
|
"./package.json": "./package.json"
|
|
22
30
|
},
|
|
31
|
+
"typesVersions": {
|
|
32
|
+
"*": {
|
|
33
|
+
"common": [
|
|
34
|
+
"dist/common/index.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"common/*": [
|
|
37
|
+
"dist/common/*/index.d.ts"
|
|
38
|
+
],
|
|
39
|
+
"errors": [
|
|
40
|
+
"dist/errors/index.d.ts"
|
|
41
|
+
],
|
|
42
|
+
"errors/*": [
|
|
43
|
+
"dist/errors/*/index.d.ts"
|
|
44
|
+
],
|
|
45
|
+
"search-index": [
|
|
46
|
+
"dist/search/search-index.d.ts"
|
|
47
|
+
],
|
|
48
|
+
"search-index/*": [
|
|
49
|
+
"dist/search/*/index.d.ts"
|
|
50
|
+
],
|
|
51
|
+
"*": [
|
|
52
|
+
"dist/index.d.ts"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
23
56
|
"scripts": {
|
|
24
57
|
"build": "tsc -p tsconfig.json"
|
|
25
58
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./math";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ClampOptions {
|
|
2
|
+
/** Value to use when the input is undefined or not finite */
|
|
3
|
+
defaultValue: number;
|
|
4
|
+
/** Upper bound; must be a finite number */
|
|
5
|
+
max: number;
|
|
6
|
+
/** Optional lower bound; defaults to 1 */
|
|
7
|
+
min?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Domain-level guard to clamp numeric limits while providing sane defaults.
|
|
12
|
+
*/
|
|
13
|
+
export function clampLimit(value: number | undefined, options: ClampOptions): number {
|
|
14
|
+
const normalized = toFiniteNumber(value, options.defaultValue);
|
|
15
|
+
const min = toFiniteNumber(options.min, 1);
|
|
16
|
+
const max = toFiniteNumber(options.max, min);
|
|
17
|
+
|
|
18
|
+
if (max < min) {
|
|
19
|
+
throw new Error("Clamp max must be greater than or equal to min");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return Math.min(max, Math.max(min, normalized));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function toFiniteNumber(value: number | undefined, fallback: number): number {
|
|
26
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./clamp-limit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./logging";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./serialize-log-payload";
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export type LogLevel = "INFO" | "ERROR";
|
|
2
|
+
|
|
3
|
+
export type LogPayload = {
|
|
4
|
+
namespace: string;
|
|
5
|
+
message: string;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
error?: unknown;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function serializeLogPayload(payload: unknown): Record<string, unknown> {
|
|
11
|
+
if (!isSerializableObject(payload)) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const serialized = JSON.parse(
|
|
17
|
+
JSON.stringify(payload, (_key, value) => normalizeValue(value)),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (isSerializableObject(serialized)) {
|
|
21
|
+
return serialized;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { value: serialized };
|
|
25
|
+
} catch {
|
|
26
|
+
return { value: "[unserializable]" };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function logInfo(payload: LogPayload): void {
|
|
31
|
+
log("INFO", payload);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function logError(payload: LogPayload): void {
|
|
35
|
+
log("ERROR", payload);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function logFirestoreUsage(
|
|
39
|
+
collection: string,
|
|
40
|
+
operation: string,
|
|
41
|
+
metadata: Record<string, unknown> = {},
|
|
42
|
+
hit?: boolean,
|
|
43
|
+
): void {
|
|
44
|
+
logInfo({
|
|
45
|
+
namespace: "FirestoreUsage",
|
|
46
|
+
message: `${collection}.${operation}`,
|
|
47
|
+
metadata: { ...metadata, hit },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function log(level: LogLevel, payload: LogPayload): void {
|
|
52
|
+
const entry = {
|
|
53
|
+
level,
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
namespace: payload.namespace,
|
|
56
|
+
message: payload.message,
|
|
57
|
+
metadata: serializeLogPayload(payload.metadata ?? {}),
|
|
58
|
+
error: normalizeError(payload.error),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const serialized = JSON.stringify(entry);
|
|
62
|
+
if (level === "ERROR") {
|
|
63
|
+
console.error(serialized);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(serialized);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeError(error: unknown): unknown {
|
|
71
|
+
if (!error) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (error instanceof Error) {
|
|
76
|
+
return {
|
|
77
|
+
name: error.name,
|
|
78
|
+
message: error.message,
|
|
79
|
+
stack: error.stack,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return serializeLogPayload(error);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalizeValue(value: unknown): unknown {
|
|
87
|
+
if (value instanceof Date) {
|
|
88
|
+
return value.toISOString();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof value === "bigint") {
|
|
92
|
+
return value.toString();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (hasToJSON(value)) {
|
|
96
|
+
try {
|
|
97
|
+
return value.toJSON();
|
|
98
|
+
} catch {
|
|
99
|
+
return "[unserializable]";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function hasToJSON(value: unknown): value is { toJSON: () => unknown } {
|
|
107
|
+
return Boolean(
|
|
108
|
+
value && typeof value === "object" &&
|
|
109
|
+
"toJSON" in value &&
|
|
110
|
+
typeof (value as { toJSON?: unknown }).toJSON === "function",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isSerializableObject(value: unknown): value is Record<string, unknown> {
|
|
115
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
116
|
+
}
|
package/src/index.ts
CHANGED