@cyberskill/shared 3.13.0 → 3.15.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/config/env/env.util.d.ts +5 -0
- package/dist/config/env/env.util.js +20 -16
- package/dist/config/env/env.util.js.map +1 -1
- package/dist/config/env/index.js +2 -2
- package/dist/config/vitest/vitest.e2e.js +4 -4
- package/dist/config/vitest/vitest.e2e.js.map +1 -1
- package/dist/config/vitest/vitest.unit.js +5 -5
- package/dist/config/vitest/vitest.unit.js.map +1 -1
- package/dist/config/vitest/vitest.unit.setup.js +10 -0
- package/dist/config/vitest/vitest.unit.setup.js.map +1 -0
- package/dist/node/apollo-server/apollo-server.type.d.ts +13 -1
- package/dist/node/apollo-server/apollo-server.util.d.ts +1 -0
- package/dist/node/apollo-server/apollo-server.util.js +40 -16
- package/dist/node/apollo-server/apollo-server.util.js.map +1 -1
- package/dist/node/cli/index.js +26 -28
- package/dist/node/cli/index.js.map +1 -1
- package/dist/node/command/command.util.d.ts +5 -0
- package/dist/node/command/command.util.js +49 -48
- package/dist/node/command/command.util.js.map +1 -1
- package/dist/node/command/index.js +2 -2
- package/dist/node/express/express.type.d.ts +11 -0
- package/dist/node/express/express.util.d.ts +34 -6
- package/dist/node/express/express.util.js +81 -56
- package/dist/node/express/express.util.js.map +1 -1
- package/dist/node/express/index.js +2 -2
- package/dist/node/log/log.type.d.ts +17 -0
- package/dist/node/log/log.type.js.map +1 -1
- package/dist/node/log/log.util.js +25 -11
- package/dist/node/log/log.util.js.map +1 -1
- package/dist/node/mongo/index.d.ts +2 -1
- package/dist/node/mongo/index.js +7 -8
- package/dist/node/mongo/mongo.constant.d.ts +5 -0
- package/dist/node/mongo/mongo.constant.js +2 -2
- package/dist/node/mongo/mongo.constant.js.map +1 -1
- package/dist/node/mongo/mongo.controller.mongoose.d.ts +3 -0
- package/dist/node/mongo/mongo.controller.mongoose.js +41 -55
- package/dist/node/mongo/mongo.controller.mongoose.js.map +1 -1
- package/dist/node/mongo/mongo.controller.native.d.ts +29 -2
- package/dist/node/mongo/mongo.controller.native.js +31 -14
- package/dist/node/mongo/mongo.controller.native.js.map +1 -1
- package/dist/node/mongo/mongo.type.d.ts +3 -1
- package/dist/node/mongo/mongo.util.d.ts +1 -0
- package/dist/node/mongo/mongo.util.js +38 -17
- package/dist/node/mongo/mongo.util.js.map +1 -1
- package/dist/node/package/package.util.js +47 -47
- package/dist/node/path/index.js +2 -2
- package/dist/node/path/path.constant.d.ts +4 -0
- package/dist/node/path/path.constant.js +75 -72
- package/dist/node/path/path.constant.js.map +1 -1
- package/dist/node/storage/storage.util.d.ts +50 -1
- package/dist/node/storage/storage.util.js +79 -54
- package/dist/node/storage/storage.util.js.map +1 -1
- package/dist/node/upload/upload.type.d.ts +2 -0
- package/dist/node/upload/upload.type.js.map +1 -1
- package/dist/node/upload/upload.util.d.ts +1 -0
- package/dist/node/upload/upload.util.js +62 -52
- package/dist/node/upload/upload.util.js.map +1 -1
- package/dist/node/ws/ws.util.d.ts +7 -0
- package/dist/node/ws/ws.util.js +20 -19
- package/dist/node/ws/ws.util.js.map +1 -1
- package/dist/react/apollo-client/apollo-client.component.js.map +1 -1
- package/dist/react/apollo-client/apollo-client.type.d.ts +2 -0
- package/dist/react/apollo-client/apollo-client.util.js +6 -6
- package/dist/react/apollo-client/apollo-client.util.js.map +1 -1
- package/dist/react/apollo-error/apollo-error.component.js +1 -1
- package/dist/react/apollo-error/apollo-error.component.js.map +1 -1
- package/dist/react/apollo-error/apollo-error.util.js.map +1 -1
- package/dist/react/i18next/i18next.server.d.ts +17 -0
- package/dist/react/i18next/i18next.server.js +9 -0
- package/dist/react/i18next/i18next.server.js.map +1 -0
- package/dist/react/next-intl/next-intl.hoc.d.ts +4 -8
- package/dist/react/next-intl/next-intl.hoc.js +14 -10
- package/dist/react/next-intl/next-intl.hoc.js.map +1 -1
- package/dist/react/next-intl/next-intl.server.d.ts +10 -0
- package/dist/react/next-intl/next-intl.server.js +7 -0
- package/dist/react/next-intl/next-intl.server.js.map +1 -0
- package/dist/react/storage/storage.util.d.ts +34 -1
- package/dist/react/storage/storage.util.js +30 -5
- package/dist/react/storage/storage.util.js.map +1 -1
- package/dist/react/userback/userback.component.js.map +1 -1
- package/dist/typescript/common.type.d.ts +4 -0
- package/dist/typescript/common.type.js +2 -2
- package/dist/typescript/common.type.js.map +1 -1
- package/dist/typescript/index.js +2 -2
- package/dist/util/object/object.util.js +29 -18
- package/dist/util/object/object.util.js.map +1 -1
- package/dist/util/serializer/serializer.util.d.ts +8 -0
- package/dist/util/serializer/serializer.util.js +51 -64
- package/dist/util/serializer/serializer.util.js.map +1 -1
- package/dist/util/storage/storage-envelope.d.ts +25 -0
- package/dist/util/storage/storage-envelope.js +18 -0
- package/dist/util/storage/storage-envelope.js.map +1 -0
- package/package.json +33 -12
- package/dist/node/mongo/mongo.type.js +0 -8
- package/dist/node/mongo/mongo.type.js.map +0 -1
- package/dist/node_modules/.pnpm/vitest@4.1.2_@types_node@25.5.0_jsdom@29.0.1_@noble_hashes@1.8.0__vite@8.0.3_@types_nod_0827261ede788764a5d99ac6bdf44bde/node_modules/vitest/dist/config.js +0 -8
- package/dist/node_modules/.pnpm/vitest@4.1.2_@types_node@25.5.0_jsdom@29.0.1_@noble_hashes@1.8.0__vite@8.0.3_@types_nod_0827261ede788764a5d99ac6bdf44bde/node_modules/vitest/dist/config.js.map +0 -1
|
@@ -5,17 +5,21 @@ import * as r from "react";
|
|
|
5
5
|
import { NextIntlClientProvider as i } from "next-intl";
|
|
6
6
|
//#region src/react/next-intl/next-intl.hoc.tsx
|
|
7
7
|
function a(a) {
|
|
8
|
-
let o = (o) =>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
let { currentLanguage: o } = n(), { messages: s, languages: c, TargetComponent: l } = a, u = s[o?.value || "en"], d = c.find((e) => e.value === o?.value)?.timezone ?? t.timezone;
|
|
9
|
+
return s ? /* @__PURE__ */ r.createElement(i, {
|
|
10
|
+
locale: o?.value || "en",
|
|
11
|
+
messages: u || null,
|
|
12
|
+
timeZone: d
|
|
13
|
+
}, /* @__PURE__ */ r.createElement(l, a)) : (e.warn(`Missing messages for language: ${o?.value || "en"}`), null);
|
|
14
|
+
}
|
|
15
|
+
function o(e) {
|
|
16
|
+
let t = (t) => /* @__PURE__ */ r.createElement(a, {
|
|
17
|
+
...t,
|
|
18
|
+
TargetComponent: e
|
|
19
|
+
});
|
|
20
|
+
return t.displayName = `withNextIntl(${e.displayName || e.name || "Component"})`, t;
|
|
17
21
|
}
|
|
18
22
|
//#endregion
|
|
19
|
-
export {
|
|
23
|
+
export { o as withNextIntl };
|
|
20
24
|
|
|
21
25
|
//# sourceMappingURL=next-intl.hoc.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-intl.hoc.js","names":[],"sources":["../../../src/react/next-intl/next-intl.hoc.tsx"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport { NextIntlClientProvider } from 'next-intl';\nimport * as React from 'react';\n\nimport type { I_Children } from '#typescript/index.js';\n\nimport type { I_NextIntlLanguage, T_NextIntlMessageList } from './next-intl.type.js';\n\nimport { log } from '../log/index.js';\nimport { NEXT_INTL_DEFAULT_LANGUAGE } from './next-intl.constant.js';\nimport { useNextIntl } from './next-intl.hook.js';\n\n/**\n * Higher-Order Component (HOC) that wraps components with Next.js internationalization support.\n * This HOC provides internationalization capabilities to React components by wrapping them\n * with the NextIntlClientProvider. It automatically handles language detection, message\n * loading, and timezone configuration based on the current language settings.\n *\n * Features:\n * - Automatic language detection and message loading\n * - Timezone configuration based on language settings\n * - Fallback to default language when messages are missing\n * - Warning logging for missing message configurations\n * - Proper display name preservation for debugging\n *\n * @param Component - The React component to wrap with internationalization support.\n * @returns A new component with internationalization capabilities and additional props for languages and messages.\n */\nexport function withNextIntl<T extends I_Children>(Component: ComponentType<T>) {\n const
|
|
1
|
+
{"version":3,"file":"next-intl.hoc.js","names":[],"sources":["../../../src/react/next-intl/next-intl.hoc.tsx"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport { NextIntlClientProvider } from 'next-intl';\nimport * as React from 'react';\n\nimport type { I_Children } from '#typescript/index.js';\n\nimport type { I_NextIntlLanguage, T_NextIntlMessageList } from './next-intl.type.js';\n\nimport { log } from '../log/index.js';\nimport { NEXT_INTL_DEFAULT_LANGUAGE } from './next-intl.constant.js';\nimport { useNextIntl } from './next-intl.hook.js';\n\n/**\n * Internal component wrapper for the HOC\n */\n// eslint-disable-next-line react-refresh/only-export-components -- HOC is an allowed export\nfunction PageWithI18nInternal<T extends I_Children>(\n props: T & { languages: I_NextIntlLanguage[]; messages: T_NextIntlMessageList; TargetComponent: ComponentType<T> },\n) {\n const { currentLanguage } = useNextIntl();\n const { messages, languages, TargetComponent } = props;\n const defaultLang = 'en';\n\n const defaultMessages = messages[currentLanguage?.value || defaultLang];\n const timeZone = languages.find(lang => lang.value === currentLanguage?.value)?.timezone ?? NEXT_INTL_DEFAULT_LANGUAGE.timezone;\n\n if (!messages) {\n log.warn(`Missing messages for language: ${currentLanguage?.value || defaultLang}`);\n return null;\n }\n\n return (\n <NextIntlClientProvider\n locale={currentLanguage?.value || defaultLang}\n messages={defaultMessages || null}\n timeZone={timeZone}\n >\n <TargetComponent {...(props as T)} />\n </NextIntlClientProvider>\n );\n}\n\n/**\n * Higher-Order Component (HOC) that wraps components with Next.js internationalization support.\n * This HOC provides internationalization capabilities to React components by wrapping them\n * with the NextIntlClientProvider. It automatically handles language detection, message\n * loading, and timezone configuration based on the current language settings.\n *\n * Features:\n * - Automatic language detection and message loading\n * - Timezone configuration based on language settings\n * - Fallback to default language when messages are missing\n * - Warning logging for missing message configurations\n * - Proper display name preservation for debugging\n *\n * @param Component - The React component to wrap with internationalization support.\n * @returns A new component with internationalization capabilities and additional props for languages and messages.\n */\nexport function withNextIntl<T extends I_Children>(Component: ComponentType<T>) {\n const hoc = (props: T & { languages: I_NextIntlLanguage[]; messages: T_NextIntlMessageList }) => {\n return <PageWithI18nInternal {...props} TargetComponent={Component} />;\n };\n\n hoc.displayName = `withNextIntl(${Component.displayName || Component.name || 'Component'})`;\n\n return hoc as unknown as ComponentType<T & { languages: I_NextIntlLanguage[]; messages: T_NextIntlMessageList }>;\n}\n"],"mappings":";;;;;;AAiBA,SAAS,EACL,GACF;CACE,IAAM,EAAE,uBAAoB,GAAa,EACnC,EAAE,aAAU,cAAW,uBAAoB,GAG3C,IAAkB,EAAS,GAAiB,SAAS,OACrD,IAAW,EAAU,MAAK,MAAQ,EAAK,UAAU,GAAiB,MAAM,EAAE,YAAY,EAA2B;AAOvH,QALK,IAMD,kBAAA,cAAC,GAAD;EACI,QAAQ,GAAiB,SAAS;EAClC,UAAU,KAAmB;EACnB;EAGW,EADrB,kBAAA,cAAC,GAAqB,EAAe,CAChB,IAXzB,EAAI,KAAK,kCAAkC,GAAiB,SAAS,OAAc,EAC5E;;AA8Bf,SAAgB,EAAmC,GAA6B;CAC5E,IAAM,KAAO,MACF,kBAAA,cAAC,GAAD;EAAsB,GAAI;EAAO,iBAAiB;EAAa,CAAA;AAK1E,QAFA,EAAI,cAAc,gBAAgB,EAAU,eAAe,EAAU,QAAQ,YAAY,IAElF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getTranslations } from 'next-intl/server';
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves the translation function from Next.js internationalization context on the server.
|
|
4
|
+
* This function wraps next-intl/server's getTranslations, returning a Promise that resolves
|
|
5
|
+
* to the translation function. It enables asynchronous translation resolution for server components
|
|
6
|
+
* and server actions.
|
|
7
|
+
*
|
|
8
|
+
* @returns A Promise that resolves to the getTranslations function.
|
|
9
|
+
*/
|
|
10
|
+
export declare const getTranslationsNextIntl: typeof getTranslations;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-intl.server.js","names":[],"sources":["../../../src/react/next-intl/next-intl.server.ts"],"sourcesContent":["import { getTranslations } from 'next-intl/server';\n\n/**\n * Retrieves the translation function from Next.js internationalization context on the server.\n * This function wraps next-intl/server's getTranslations, returning a Promise that resolves\n * to the translation function. It enables asynchronous translation resolution for server components\n * and server actions.\n *\n * @returns A Promise that resolves to the getTranslations function.\n */\nexport const getTranslationsNextIntl: typeof getTranslations = getTranslations;\n"],"mappings":";;AAUA,IAAa,IAAkD"}
|
|
@@ -21,9 +21,13 @@ export declare const storage: {
|
|
|
21
21
|
*
|
|
22
22
|
* @param key - The unique identifier for the value to store.
|
|
23
23
|
* @param value - The data to store (will be automatically serialized to JSON).
|
|
24
|
+
* @param options - Optional settings.
|
|
25
|
+
* @param options.ttlMs - The time-to-live in milliseconds.
|
|
24
26
|
* @returns A promise that resolves when the storage operation is complete.
|
|
25
27
|
*/
|
|
26
|
-
set<T = unknown>(key: string, value: T
|
|
28
|
+
set<T = unknown>(key: string, value: T, options?: {
|
|
29
|
+
ttlMs?: number;
|
|
30
|
+
}): Promise<void>;
|
|
27
31
|
/**
|
|
28
32
|
* Removes a value from browser storage by key.
|
|
29
33
|
* This method permanently deletes the stored data associated with the specified key.
|
|
@@ -41,4 +45,33 @@ export declare const storage: {
|
|
|
41
45
|
* @returns A promise that resolves to an array of storage keys.
|
|
42
46
|
*/
|
|
43
47
|
keys(): Promise<string[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Checks if a key exists in browser storage.
|
|
50
|
+
* This method efficiently checks for key existence without deserializing the value.
|
|
51
|
+
* It also respects TTL — returns false if the key exists but has expired.
|
|
52
|
+
*
|
|
53
|
+
* @param key - The unique identifier to check.
|
|
54
|
+
* @returns A promise that resolves to true if the key exists and has not expired.
|
|
55
|
+
*/
|
|
56
|
+
has(key: string): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Clears all values from browser storage.
|
|
59
|
+
* This method permanently removes all stored data.
|
|
60
|
+
*
|
|
61
|
+
* @returns A promise that resolves when the clear operation is complete.
|
|
62
|
+
*/
|
|
63
|
+
clear(): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves a value from browser storage, or creates and stores it if it doesn't exist.
|
|
66
|
+
* This method combines check, creation, and storage into a single convenient operation.
|
|
67
|
+
*
|
|
68
|
+
* @param key - The unique identifier for the value.
|
|
69
|
+
* @param factory - A function (sync or async) that generates the value if it's missing or expired.
|
|
70
|
+
* @param options - Optional storage options.
|
|
71
|
+
* @param options.ttlMs - The time-to-live in milliseconds.
|
|
72
|
+
* @returns A promise that resolves to the retrieved or newly created value.
|
|
73
|
+
*/
|
|
74
|
+
getOrSet<T = unknown>(key: string, factory: () => T | Promise<T>, options?: {
|
|
75
|
+
ttlMs?: number;
|
|
76
|
+
}): Promise<T>;
|
|
44
77
|
};
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { catchError as e } from "../log/log.util.js";
|
|
2
|
+
import { createTtlEnvelope as t, isExpiredEnvelope as n, isTtlEnvelope as r } from "../../util/storage/storage-envelope.js";
|
|
2
3
|
//#region src/react/storage/storage.util.ts
|
|
3
|
-
var
|
|
4
|
+
var i = {
|
|
4
5
|
async get(t) {
|
|
5
6
|
try {
|
|
6
7
|
let e = localStorage.getItem(t);
|
|
7
|
-
|
|
8
|
+
if (e === null) return null;
|
|
9
|
+
let i = JSON.parse(e);
|
|
10
|
+
return r(i) ? n(i) ? (localStorage.removeItem(t), null) : i.value : i;
|
|
8
11
|
} catch (t) {
|
|
9
12
|
return e(t, { returnValue: null });
|
|
10
13
|
}
|
|
11
14
|
},
|
|
12
|
-
async set(
|
|
15
|
+
async set(n, r, i) {
|
|
13
16
|
try {
|
|
14
|
-
|
|
17
|
+
let e = r;
|
|
18
|
+
i?.ttlMs && (e = t(r, i.ttlMs)), localStorage.setItem(n, JSON.stringify(e));
|
|
15
19
|
} catch (t) {
|
|
16
20
|
e(t);
|
|
17
21
|
}
|
|
@@ -34,9 +38,30 @@ var t = {
|
|
|
34
38
|
} catch (t) {
|
|
35
39
|
return e(t, { returnValue: [] });
|
|
36
40
|
}
|
|
41
|
+
},
|
|
42
|
+
async has(t) {
|
|
43
|
+
try {
|
|
44
|
+
let e = localStorage.getItem(t);
|
|
45
|
+
if (e === null) return !1;
|
|
46
|
+
let i = JSON.parse(e);
|
|
47
|
+
return r(i) && n(i) ? (localStorage.removeItem(t), !1) : !0;
|
|
48
|
+
} catch (t) {
|
|
49
|
+
return e(t, { returnValue: !1 });
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async clear() {
|
|
53
|
+
try {
|
|
54
|
+
localStorage.clear();
|
|
55
|
+
} catch (t) {
|
|
56
|
+
e(t);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async getOrSet(e, t, n) {
|
|
60
|
+
let r = await this.get(e);
|
|
61
|
+
return r === null && (r = await t(), await this.set(e, r, n)), r;
|
|
37
62
|
}
|
|
38
63
|
};
|
|
39
64
|
//#endregion
|
|
40
|
-
export {
|
|
65
|
+
export { i as storage };
|
|
41
66
|
|
|
42
67
|
//# sourceMappingURL=storage.util.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.util.js","names":[],"sources":["../../../src/react/storage/storage.util.ts"],"sourcesContent":["import { catchError } from '../log/index.js';\n\n/**\n * Browser storage utility object using native localStorage.\n * This object provides a unified interface for browser storage operations\n * with comprehensive error handling and type safety.\n * Values are stored as JSON strings for consistent serialization.\n */\nexport const storage = {\n /**\n * Retrieves a value from browser storage by key.\n * This method fetches data that was previously stored using the set method.\n * Returns null if the key doesn't exist or if an error occurs during retrieval.\n *\n * @param key - The unique identifier for the stored value.\n * @returns A promise that resolves to the stored value or null if not found.\n */\n async get<T = unknown>(key: string): Promise<T | null> {\n try {\n const raw = localStorage.getItem(key);\n\n if (raw === null) {\n return null;\n }\n\n
|
|
1
|
+
{"version":3,"file":"storage.util.js","names":[],"sources":["../../../src/react/storage/storage.util.ts"],"sourcesContent":["import { createTtlEnvelope, isExpiredEnvelope, isTtlEnvelope } from '../../util/storage/storage-envelope.js';\nimport { catchError } from '../log/index.js';\n\n/**\n * Browser storage utility object using native localStorage.\n * This object provides a unified interface for browser storage operations\n * with comprehensive error handling and type safety.\n * Values are stored as JSON strings for consistent serialization.\n */\nexport const storage = {\n /**\n * Retrieves a value from browser storage by key.\n * This method fetches data that was previously stored using the set method.\n * Returns null if the key doesn't exist or if an error occurs during retrieval.\n *\n * @param key - The unique identifier for the stored value.\n * @returns A promise that resolves to the stored value or null if not found.\n */\n async get<T = unknown>(key: string): Promise<T | null> {\n try {\n const raw = localStorage.getItem(key);\n\n if (raw === null) {\n return null;\n }\n\n const obj = JSON.parse(raw);\n\n if (isTtlEnvelope<T>(obj)) {\n if (isExpiredEnvelope(obj)) {\n localStorage.removeItem(key);\n\n return null;\n }\n\n return obj.value;\n }\n\n return obj as T;\n }\n catch (error) {\n return catchError(error, { returnValue: null });\n }\n },\n /**\n * Stores a value in browser storage with a unique key.\n * This method saves data that can be retrieved later using the get method.\n * The data is automatically serialized to JSON.\n *\n * @param key - The unique identifier for the value to store.\n * @param value - The data to store (will be automatically serialized to JSON).\n * @param options - Optional settings.\n * @param options.ttlMs - The time-to-live in milliseconds.\n * @returns A promise that resolves when the storage operation is complete.\n */\n async set<T = unknown>(key: string, value: T, options?: { ttlMs?: number }): Promise<void> {\n try {\n let payloadToStore: unknown = value;\n\n if (options?.ttlMs) {\n payloadToStore = createTtlEnvelope(value, options.ttlMs);\n }\n\n localStorage.setItem(key, JSON.stringify(payloadToStore));\n }\n catch (error) {\n catchError(error);\n }\n },\n /**\n * Removes a value from browser storage by key.\n * This method permanently deletes the stored data associated with the specified key.\n * If the key doesn't exist, the operation completes successfully without error.\n *\n * @param key - The unique identifier of the value to remove.\n * @returns A promise that resolves when the removal operation is complete.\n */\n async remove(key: string): Promise<void> {\n try {\n localStorage.removeItem(key);\n }\n catch (error) {\n catchError(error);\n }\n },\n /**\n * Retrieves all storage keys.\n * This method returns an array of all keys that currently have stored values.\n * Returns an empty array if no keys exist or if an error occurs during retrieval.\n *\n * @returns A promise that resolves to an array of storage keys.\n */\n async keys(): Promise<string[]> {\n try {\n const keys: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n\n if (key !== null) {\n keys.push(key);\n }\n }\n\n return keys;\n }\n catch (error) {\n return catchError(error, { returnValue: [] });\n }\n },\n /**\n * Checks if a key exists in browser storage.\n * This method efficiently checks for key existence without deserializing the value.\n * It also respects TTL — returns false if the key exists but has expired.\n *\n * @param key - The unique identifier to check.\n * @returns A promise that resolves to true if the key exists and has not expired.\n */\n async has(key: string): Promise<boolean> {\n try {\n const raw = localStorage.getItem(key);\n\n if (raw === null) {\n return false;\n }\n\n const obj = JSON.parse(raw);\n\n if (isTtlEnvelope<unknown>(obj)) {\n if (isExpiredEnvelope(obj)) {\n localStorage.removeItem(key);\n\n return false;\n }\n }\n\n return true;\n }\n catch (error) {\n return catchError(error, { returnValue: false });\n }\n },\n /**\n * Clears all values from browser storage.\n * This method permanently removes all stored data.\n *\n * @returns A promise that resolves when the clear operation is complete.\n */\n async clear(): Promise<void> {\n try {\n localStorage.clear();\n }\n catch (error) {\n catchError(error);\n }\n },\n /**\n * Retrieves a value from browser storage, or creates and stores it if it doesn't exist.\n * This method combines check, creation, and storage into a single convenient operation.\n *\n * @param key - The unique identifier for the value.\n * @param factory - A function (sync or async) that generates the value if it's missing or expired.\n * @param options - Optional storage options.\n * @param options.ttlMs - The time-to-live in milliseconds.\n * @returns A promise that resolves to the retrieved or newly created value.\n */\n async getOrSet<T = unknown>(key: string, factory: () => T | Promise<T>, options?: { ttlMs?: number }): Promise<T> {\n let value = await this.get<T>(key);\n\n if (value === null) {\n value = await factory();\n await this.set(key, value, options);\n }\n\n return value;\n },\n};\n"],"mappings":";;;AASA,IAAa,IAAU;CASnB,MAAM,IAAiB,GAAgC;AACnD,MAAI;GACA,IAAM,IAAM,aAAa,QAAQ,EAAI;AAErC,OAAI,MAAQ,KACR,QAAO;GAGX,IAAM,IAAM,KAAK,MAAM,EAAI;AAY3B,UAVI,EAAiB,EAAI,GACjB,EAAkB,EAAI,IACtB,aAAa,WAAW,EAAI,EAErB,QAGJ,EAAI,QAGR;WAEJ,GAAO;AACV,UAAO,EAAW,GAAO,EAAE,aAAa,MAAM,CAAC;;;CAcvD,MAAM,IAAiB,GAAa,GAAU,GAA6C;AACvF,MAAI;GACA,IAAI,IAA0B;AAM9B,GAJI,GAAS,UACT,IAAiB,EAAkB,GAAO,EAAQ,MAAM,GAG5D,aAAa,QAAQ,GAAK,KAAK,UAAU,EAAe,CAAC;WAEtD,GAAO;AACV,KAAW,EAAM;;;CAWzB,MAAM,OAAO,GAA4B;AACrC,MAAI;AACA,gBAAa,WAAW,EAAI;WAEzB,GAAO;AACV,KAAW,EAAM;;;CAUzB,MAAM,OAA0B;AAC5B,MAAI;GACA,IAAM,IAAiB,EAAE;AAEzB,QAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;IAC1C,IAAM,IAAM,aAAa,IAAI,EAAE;AAE/B,IAAI,MAAQ,QACR,EAAK,KAAK,EAAI;;AAItB,UAAO;WAEJ,GAAO;AACV,UAAO,EAAW,GAAO,EAAE,aAAa,EAAE,EAAE,CAAC;;;CAWrD,MAAM,IAAI,GAA+B;AACrC,MAAI;GACA,IAAM,IAAM,aAAa,QAAQ,EAAI;AAErC,OAAI,MAAQ,KACR,QAAO;GAGX,IAAM,IAAM,KAAK,MAAM,EAAI;AAU3B,UARI,EAAuB,EAAI,IACvB,EAAkB,EAAI,IACtB,aAAa,WAAW,EAAI,EAErB,MAIR;WAEJ,GAAO;AACV,UAAO,EAAW,GAAO,EAAE,aAAa,IAAO,CAAC;;;CASxD,MAAM,QAAuB;AACzB,MAAI;AACA,gBAAa,OAAO;WAEjB,GAAO;AACV,KAAW,EAAM;;;CAazB,MAAM,SAAsB,GAAa,GAA+B,GAA0C;EAC9G,IAAI,IAAQ,MAAM,KAAK,IAAO,EAAI;AAOlC,SALI,MAAU,SACV,IAAQ,MAAM,GAAS,EACvB,MAAM,KAAK,IAAI,GAAK,GAAO,EAAQ,GAGhC;;CAEd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userback.component.js","names":[],"sources":["../../../src/react/userback/userback.component.tsx"],"sourcesContent":["import UserbackWidget from '@userback/widget';\nimport { useEffect } from 'react';\n\nimport type { I_UserBackProps } from './userback.type.js';\n\n/**\n * Userback feedback widget component for collecting user feedback.\n * This component integrates the Userback feedback widget into React applications,\n * providing a customizable feedback collection interface for users to submit\n * bug reports, feature requests, and general feedback.\n *\n * Features:\n * - Userback widget integration\n * - Customizable feedback collection\n * - Automatic widget initialization\n * - Token-based configuration\n * - Responsive design support\n *\n * @param props - Component props containing token and options.\n * @param props.token - The Userback token for widget authentication.\n * @param props.options - Optional configuration options for the Userback widget.\n * @returns A React component that renders the Userback feedback widget.\n */\nexport function Userback({ token, options }: I_UserBackProps) {\n useEffect(() => {\n if (!token) {\n return;\n }\n\n let observer: MutationObserver;\n\n const loadUserback = async () => {\n const { hide, ...rest } = options || {};\n\n await UserbackWidget(token, rest);\n\n if (hide && hide.length > 0) {\n hide.forEach((selector: string) => {\n document.querySelectorAll(selector).forEach(el => el.remove());\n });\n }\n\n observer = new MutationObserver(() => {\n if (hide && hide.length > 0) {\n hide.forEach((selector: string) => {\n document.querySelectorAll(selector).forEach(el => el.remove());\n });\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n };\n\n loadUserback();\n\n return () => {\n observer?.disconnect();\n };\n
|
|
1
|
+
{"version":3,"file":"userback.component.js","names":[],"sources":["../../../src/react/userback/userback.component.tsx"],"sourcesContent":["import UserbackWidget from '@userback/widget';\nimport { useEffect } from 'react';\n\nimport type { I_UserBackProps } from './userback.type.js';\n\n/**\n * Userback feedback widget component for collecting user feedback.\n * This component integrates the Userback feedback widget into React applications,\n * providing a customizable feedback collection interface for users to submit\n * bug reports, feature requests, and general feedback.\n *\n * Features:\n * - Userback widget integration\n * - Customizable feedback collection\n * - Automatic widget initialization\n * - Token-based configuration\n * - Responsive design support\n *\n * @param props - Component props containing token and options.\n * @param props.token - The Userback token for widget authentication.\n * @param props.options - Optional configuration options for the Userback widget.\n * @returns A React component that renders the Userback feedback widget.\n */\nexport function Userback({ token, options }: I_UserBackProps) {\n useEffect(() => {\n if (!token) {\n return;\n }\n\n let observer: MutationObserver;\n\n const loadUserback = async () => {\n const { hide, ...rest } = options || {};\n\n await UserbackWidget(token, rest);\n\n if (hide && hide.length > 0) {\n hide.forEach((selector: string) => {\n document.querySelectorAll(selector).forEach(el => el.remove());\n });\n }\n\n observer = new MutationObserver(() => {\n if (hide && hide.length > 0) {\n hide.forEach((selector: string) => {\n document.querySelectorAll(selector).forEach(el => el.remove());\n });\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n };\n\n loadUserback();\n\n return () => {\n observer?.disconnect();\n };\n // eslint-disable-next-line react/exhaustive-deps -- intended to only load Userback on token change\n }, [token]);\n\n return null;\n}\n"],"mappings":";;;AAuBA,SAAgB,EAAS,EAAE,UAAO,cAA4B;AAyC1D,QAxCA,QAAgB;AACZ,MAAI,CAAC,EACD;EAGJ,IAAI;AA6BJ,UA3BqB,YAAY;GAC7B,IAAM,EAAE,SAAM,GAAG,MAAS,KAAW,EAAE;AAkBvC,GAhBA,MAAM,EAAe,GAAO,EAAK,EAE7B,KAAQ,EAAK,SAAS,KACtB,EAAK,SAAS,MAAqB;AAC/B,aAAS,iBAAiB,EAAS,CAAC,SAAQ,MAAM,EAAG,QAAQ,CAAC;KAChE,EAGN,IAAW,IAAI,uBAAuB;AAClC,IAAI,KAAQ,EAAK,SAAS,KACtB,EAAK,SAAS,MAAqB;AAC/B,cAAS,iBAAiB,EAAS,CAAC,SAAQ,MAAM,EAAG,QAAQ,CAAC;MAChE;KAER,EAEF,EAAS,QAAQ,SAAS,MAAM;IAC5B,WAAW;IACX,SAAS;IACZ,CAAC;MAGQ,QAED;AACT,MAAU,YAAY;;IAG3B,CAAC,EAAM,CAAC,EAEJ"}
|
|
@@ -85,6 +85,10 @@ export declare function isSuccess<T, E = unknown>(result: I_Return<T, E>): resul
|
|
|
85
85
|
* @throws {Error} When the result is a failure.
|
|
86
86
|
*/
|
|
87
87
|
export declare function unwrapResult<T, E = unknown>(result: I_Return<T, E>): T & E;
|
|
88
|
+
/**
|
|
89
|
+
* Alias for `unwrapResult` to improve discoverability.
|
|
90
|
+
*/
|
|
91
|
+
export declare const unwrapOrThrow: typeof unwrapResult;
|
|
88
92
|
export declare enum E_Environment {
|
|
89
93
|
PRODUCTION = "production",
|
|
90
94
|
STAGING = "staging",
|
|
@@ -6,10 +6,10 @@ function t(e) {
|
|
|
6
6
|
if (!e.success) throw Error(e.message);
|
|
7
7
|
return e.result;
|
|
8
8
|
}
|
|
9
|
-
var n = /* @__PURE__ */ function(e) {
|
|
9
|
+
var n = t, r = /* @__PURE__ */ function(e) {
|
|
10
10
|
return e.PRODUCTION = "production", e.STAGING = "staging", e.DEVELOPMENT = "development", e;
|
|
11
11
|
}({});
|
|
12
12
|
//#endregion
|
|
13
|
-
export {
|
|
13
|
+
export { r as E_Environment, e as isSuccess, n as unwrapOrThrow, t as unwrapResult };
|
|
14
14
|
|
|
15
15
|
//# sourceMappingURL=common.type.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.type.js","names":[],"sources":["../../src/typescript/common.type.ts"],"sourcesContent":["import type consola from 'consola';\n\n/**\n * Generic object type with string keys and values of type T (defaults to unknown).\n */\nexport type T_Object<T = unknown> = Record<string, T>;\n\n/**\n * Logging interface for browser and Node.js environments, compatible with consola.\n */\nexport interface I_Log {\n silent: typeof consola['silent'];\n level: typeof consola['level'];\n fatal: typeof consola['fatal'];\n error: typeof consola['error'];\n warn: typeof consola['warn'];\n log: typeof consola['log'];\n info: typeof consola['info'];\n success: typeof consola['success'];\n ready: typeof consola['ready'];\n start: typeof consola['start'];\n box: typeof consola['box'];\n debug: typeof consola['debug'];\n trace: typeof consola['trace'];\n verbose: typeof consola['verbose'];\n}\n\n/**\n * Base interface for return types with common properties.\n */\nexport interface I_ReturnBase {\n success: boolean;\n message?: string;\n code?: number | string;\n}\n\n/**\n * Success return type with result data.\n * @template T - The main result type\n * @template E - Additional properties to merge with the result (defaults to unknown)\n */\nexport interface I_ReturnSuccess<T, E = unknown> extends I_ReturnBase {\n success: true;\n result: T & E;\n /** True when results were limited by a default cap and may not include all matching documents. */\n truncated?: boolean;\n}\n\n/**\n * Failure return type with error information.\n */\nexport interface I_ReturnFailure extends I_ReturnBase {\n success: false;\n message: string;\n code: number | string;\n}\n\n/**\n * Discriminated union type for function return values.\n * Provides type-safe handling of success and failure cases.\n *\n * @template T - The success result type (defaults to void)\n * @template E - Additional properties to merge with the result (defaults to unknown)\n *\n * @example\n * ```typescript\n * function fetchUser(id: string): I_Return<User> {\n * try {\n * const user = await getUser(id);\n * return { success: true, result: user };\n * } catch (error) {\n * return { success: false, message: error.message, code: 'USER_NOT_FOUND' };\n * }\n * }\n * ```\n */\nexport type I_Return<T = void, E = unknown> = I_ReturnSuccess<T, E> | I_ReturnFailure;\n\n/**\n * Type guard that narrows an `I_Return` to `I_ReturnSuccess`.\n *\n * @param result - The result to check.\n * @returns True if the result is a success.\n */\nexport function isSuccess<T, E = unknown>(result: I_Return<T, E>): result is I_ReturnSuccess<T, E> {\n return result.success;\n}\n\n/**\n * Unwraps a successful `I_Return`, throwing an error for failure cases.\n * Useful when a failure is unexpected and should abort execution.\n *\n * @param result - The result to unwrap.\n * @returns The success result value.\n * @throws {Error} When the result is a failure.\n */\nexport function unwrapResult<T, E = unknown>(result: I_Return<T, E>): T & E {\n if (!result.success) {\n throw new Error(result.message);\n }\n\n return result.result;\n}\n\nexport enum E_Environment {\n PRODUCTION = 'production',\n STAGING = 'staging',\n DEVELOPMENT = 'development',\n}\n"],"mappings":";AAoFA,SAAgB,EAA0B,GAAyD;AAC/F,QAAO,EAAO;;AAWlB,SAAgB,EAA6B,GAA+B;AACxE,KAAI,CAAC,EAAO,QACR,OAAU,MAAM,EAAO,QAAQ;AAGnC,QAAO,EAAO;;
|
|
1
|
+
{"version":3,"file":"common.type.js","names":[],"sources":["../../src/typescript/common.type.ts"],"sourcesContent":["import type consola from 'consola';\n\n/**\n * Generic object type with string keys and values of type T (defaults to unknown).\n */\nexport type T_Object<T = unknown> = Record<string, T>;\n\n/**\n * Logging interface for browser and Node.js environments, compatible with consola.\n */\nexport interface I_Log {\n silent: typeof consola['silent'];\n level: typeof consola['level'];\n fatal: typeof consola['fatal'];\n error: typeof consola['error'];\n warn: typeof consola['warn'];\n log: typeof consola['log'];\n info: typeof consola['info'];\n success: typeof consola['success'];\n ready: typeof consola['ready'];\n start: typeof consola['start'];\n box: typeof consola['box'];\n debug: typeof consola['debug'];\n trace: typeof consola['trace'];\n verbose: typeof consola['verbose'];\n}\n\n/**\n * Base interface for return types with common properties.\n */\nexport interface I_ReturnBase {\n success: boolean;\n message?: string;\n code?: number | string;\n}\n\n/**\n * Success return type with result data.\n * @template T - The main result type\n * @template E - Additional properties to merge with the result (defaults to unknown)\n */\nexport interface I_ReturnSuccess<T, E = unknown> extends I_ReturnBase {\n success: true;\n result: T & E;\n /** True when results were limited by a default cap and may not include all matching documents. */\n truncated?: boolean;\n}\n\n/**\n * Failure return type with error information.\n */\nexport interface I_ReturnFailure extends I_ReturnBase {\n success: false;\n message: string;\n code: number | string;\n}\n\n/**\n * Discriminated union type for function return values.\n * Provides type-safe handling of success and failure cases.\n *\n * @template T - The success result type (defaults to void)\n * @template E - Additional properties to merge with the result (defaults to unknown)\n *\n * @example\n * ```typescript\n * function fetchUser(id: string): I_Return<User> {\n * try {\n * const user = await getUser(id);\n * return { success: true, result: user };\n * } catch (error) {\n * return { success: false, message: error.message, code: 'USER_NOT_FOUND' };\n * }\n * }\n * ```\n */\nexport type I_Return<T = void, E = unknown> = I_ReturnSuccess<T, E> | I_ReturnFailure;\n\n/**\n * Type guard that narrows an `I_Return` to `I_ReturnSuccess`.\n *\n * @param result - The result to check.\n * @returns True if the result is a success.\n */\nexport function isSuccess<T, E = unknown>(result: I_Return<T, E>): result is I_ReturnSuccess<T, E> {\n return result.success;\n}\n\n/**\n * Unwraps a successful `I_Return`, throwing an error for failure cases.\n * Useful when a failure is unexpected and should abort execution.\n *\n * @param result - The result to unwrap.\n * @returns The success result value.\n * @throws {Error} When the result is a failure.\n */\nexport function unwrapResult<T, E = unknown>(result: I_Return<T, E>): T & E {\n if (!result.success) {\n throw new Error(result.message);\n }\n\n return result.result;\n}\n\n/**\n * Alias for `unwrapResult` to improve discoverability.\n */\nexport const unwrapOrThrow = unwrapResult;\n\nexport enum E_Environment {\n PRODUCTION = 'production',\n STAGING = 'staging',\n DEVELOPMENT = 'development',\n}\n"],"mappings":";AAoFA,SAAgB,EAA0B,GAAyD;AAC/F,QAAO,EAAO;;AAWlB,SAAgB,EAA6B,GAA+B;AACxE,KAAI,CAAC,EAAO,QACR,OAAU,MAAM,EAAO,QAAQ;AAGnC,QAAO,EAAO;;AAMlB,IAAa,IAAgB,GAEjB,IAAL,yBAAA,GAAA;QACH,EAAA,aAAA,cACA,EAAA,UAAA,WACA,EAAA,cAAA;KACH"}
|
package/dist/typescript/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { E_Environment as e, isSuccess as t,
|
|
2
|
-
export { e as E_Environment, t as isSuccess, n as unwrapResult };
|
|
1
|
+
import { E_Environment as e, isSuccess as t, unwrapOrThrow as n, unwrapResult as r } from "./common.type.js";
|
|
2
|
+
export { e as E_Environment, t as isSuccess, n as unwrapOrThrow, r as unwrapResult };
|
|
@@ -64,12 +64,12 @@ function o(...e) {
|
|
|
64
64
|
for (let a of e) {
|
|
65
65
|
if (r.has(a)) throw Error("deepMerge: Circular reference detected.");
|
|
66
66
|
let e = a;
|
|
67
|
-
for (let
|
|
68
|
-
let
|
|
69
|
-
if (Object.hasOwn(i,
|
|
70
|
-
let e = i[
|
|
71
|
-
typeof
|
|
72
|
-
} else i[
|
|
67
|
+
for (let a in e) if (Object.hasOwn(e, a)) {
|
|
68
|
+
let o = e[a];
|
|
69
|
+
if (Object.hasOwn(i, a)) {
|
|
70
|
+
let e = i[a];
|
|
71
|
+
typeof o == "object" && o && typeof e == "object" && e ? Array.isArray(o) && Array.isArray(e) ? i[a] = [...e, ...o] : !Array.isArray(o) && !Array.isArray(e) ? i[a] = t([e, o], n + 1, r) : i[a] = o : i[a] = o;
|
|
72
|
+
} else i[a] = o;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
return i;
|
|
@@ -82,26 +82,37 @@ function o(...e) {
|
|
|
82
82
|
}
|
|
83
83
|
function s(e) {
|
|
84
84
|
if (!e || typeof e != "object") return e;
|
|
85
|
-
let t =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
let t = !0;
|
|
86
|
+
for (let n in e) {
|
|
87
|
+
if (!Object.hasOwn(e, n)) continue;
|
|
88
|
+
let r = e[n];
|
|
89
|
+
if (r && typeof r == "object" && !Array.isArray(r)) {
|
|
90
|
+
t = !1;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (t) return e;
|
|
95
|
+
let n = {};
|
|
96
|
+
function r(e, t, i) {
|
|
97
|
+
if (i > 10) throw Error("normalizeMongoFilter: Maximum depth of 10 exceeded. Possible circular reference or excessively nested filter.");
|
|
98
|
+
for (let a in e) {
|
|
99
|
+
if (!Object.hasOwn(e, a)) continue;
|
|
100
|
+
let o = e[a], s = t ? `${t}.${a}` : a;
|
|
101
|
+
if (o && typeof o == "object" && !Array.isArray(o)) {
|
|
102
|
+
if (!(o.constructor === Object || Object.getPrototypeOf(o) === null)) {
|
|
103
|
+
n[s] = o;
|
|
93
104
|
continue;
|
|
94
105
|
}
|
|
95
106
|
let e = !1;
|
|
96
|
-
for (let t in
|
|
107
|
+
for (let t in o) if (Object.hasOwn(o, t) && t.startsWith("$")) {
|
|
97
108
|
e = !0;
|
|
98
109
|
break;
|
|
99
110
|
}
|
|
100
|
-
e ?
|
|
101
|
-
} else
|
|
111
|
+
e ? n[s] = o : r(o, s, i + 1);
|
|
112
|
+
} else n[s] = o;
|
|
102
113
|
}
|
|
103
114
|
}
|
|
104
|
-
return
|
|
115
|
+
return r(e, "", 0), n;
|
|
105
116
|
}
|
|
106
117
|
//#endregion
|
|
107
118
|
export { i as deepClone, o as deepMerge, t as getNestedValue, e as isJSON, s as normalizeMongoFilter, r as setNestedValue };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.util.js","names":[],"sources":["../../../src/util/object/object.util.ts"],"sourcesContent":["/**\n * Check if a string is a valid JSON string.\n * This function attempts to parse the string as JSON and returns true if successful,\n * false if the string is not valid JSON.\n *\n * @param str - The string to check for valid JSON format.\n * @returns True if the string is a valid JSON string, false otherwise.\n */\nexport function isJSON(str: string): boolean {\n try {\n JSON.parse(str);\n return true;\n }\n catch {\n return false;\n }\n}\n\n/**\n * Gets a nested value from an object using a path array.\n * This function traverses the object following the provided path and returns\n * the value at the specified location, or undefined if the path doesn't exist.\n *\n * @param obj - The object to get the value from.\n * @param path - An array of keys representing the path to the desired value.\n * @returns The value at the specified path, or undefined if the path doesn't exist.\n */\nexport function getNestedValue<T>(obj: T, path: (string | number)[]): unknown {\n // Optimization: Loop is faster than reduce and allows early exit\n let current: unknown = obj;\n const len = path.length;\n\n for (let i = 0; i < len; i++) {\n // Optimization: Early return if current value is null/undefined or not an object\n // This avoids unnecessary key lookups and type checks\n if (current == null || typeof current !== 'object') {\n return undefined;\n }\n\n const key = path[i];\n\n if (key !== undefined && key in (current as Record<string | number, unknown>)) {\n current = (current as Record<string | number, unknown>)[key];\n }\n else {\n return undefined;\n }\n }\n\n return current;\n}\n\n/**\n * Recursively sets a value at a nested path within an object, creating intermediate objects as needed.\n *\n * @param obj - The source object.\n * @param path - Array of keys forming the path.\n * @param value - The value to set.\n * @param index - Current recursion depth.\n * @returns A new object with the value set at the specified path.\n */\nfunction setNestedValueHelper<T>(obj: T, path: (string | number)[], value: unknown, index: number): T {\n if (index >= path.length)\n return obj;\n\n const head = path[index];\n\n if (index === path.length - 1) {\n return {\n ...(obj as Record<string | number, unknown>),\n [head as string | number]: value,\n } as T;\n }\n\n const current = (obj as Record<string | number, unknown>)[head as string | number];\n\n return {\n ...(obj as Record<string | number, unknown>),\n [head as string | number | symbol]: setNestedValueHelper(\n typeof current === 'object' && current !== null\n ? (current as object)\n : {},\n path,\n value,\n index + 1,\n ),\n } as T;\n}\n\n/**\n * Sets a nested value in an object using a path array.\n * This function creates the path if it doesn't exist and sets the value at the specified location.\n * The function returns a new object with the updated value, maintaining immutability.\n *\n * @param obj - The object to set the value in.\n * @param path - An array of keys representing the path to the desired location.\n * @param value - The value to set at the specified path.\n * @returns A new object with the updated value at the specified path.\n */\nexport function setNestedValue<T>(obj: T, path: (string | number)[], value: unknown): T {\n if (path.length === 0)\n return obj;\n\n return setNestedValueHelper(obj, path, value, 0);\n}\n\n/**\n * Deep clones an object or array.\n * This function creates a deep copy of the input, recursively cloning objects and arrays.\n * Primitive values, dates, and other non-plain objects are returned as is (or cloned if supported).\n *\n * @remarks\n * **Non-POJO objects are NOT cloned.** Objects with custom prototypes (e.g., Mongoose ObjectId,\n * class instances, Map, Set, Buffer) are returned **by reference** to preserve driver\n * compatibility. Mutations to these nested references will affect the original.\n * If you need full deep cloning of complex types, use `structuredClone()` or a dedicated library.\n *\n * @param obj - The object to clone.\n * @returns A deep copy of the object (with non-POJO objects returned by reference).\n */\nexport function deepClone<T>(obj: T): T {\n return deepCloneInternal(obj, new WeakMap<object, unknown>());\n}\n\n/**\n * Internal recursive implementation of deepClone with shared-reference and circular-reference handling.\n *\n * Uses a WeakMap to track already-cloned objects. This correctly handles:\n * - **Shared references**: The same object appearing in multiple places returns\n * the same clone (preserving the shared-reference topology).\n * - **Circular references**: Detected because the object is added to the map\n * before* its children are recursed into; a back-edge will find itself in\n * the map and return the (partially constructed) clone rather than recursing\n * infinitely.\n *\n * @param obj - The value to clone.\n * @param seen - A WeakMap mapping original objects to their clones.\n * @returns A deep copy of the value.\n */\nfunction deepCloneInternal<T>(obj: T, seen: WeakMap<object, unknown>): T {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n // Return the already-cloned copy for shared (or circular) references\n if (seen.has(obj as object)) {\n return seen.get(obj as object) as T;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T;\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T;\n }\n\n if (Array.isArray(obj)) {\n // Optimization: `new Array(len)` + `for` loop is ~10-15% faster than `Array.map` or `Array.from`\n // for large arrays since it avoids callback overhead and pre-allocates memory.\n const len = obj.length;\n // eslint-disable-next-line unicorn/no-new-array -- Pre-allocating array size for performance\n const arr = new Array(len);\n // Register before recursing to handle circular references within arrays\n seen.set(obj as object, arr);\n\n for (let i = 0; i < len; i++) {\n arr[i] = deepCloneInternal(obj[i], seen);\n }\n\n return arr as unknown as T;\n }\n\n // Handle Mongoose ObjectId and other custom classes by returning reference.\n // structuredClone is not used here because it silently corrupts nested non-POJO\n // types (e.g., ObjectId → plain object) and Date instances in jsdom environments.\n const proto = Object.getPrototypeOf(obj);\n\n if (proto !== Object.prototype && proto !== null) {\n return obj;\n }\n\n const result = {} as Record<string, unknown>;\n // Register before recursing to handle circular references within objects\n seen.set(obj as object, result);\n\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n result[key] = deepCloneInternal((obj as Record<string, unknown>)[key], seen);\n }\n }\n\n return result as T;\n}\n\n/**\n * Deep merges multiple objects into a single object.\n * @param args - The objects to merge. Can be empty, in which case returns an empty object.\n * @returns The merged object.\n */\nexport function deepMerge<T = Record<string, unknown>>(\n ...args: (object | null | undefined)[]\n): T;\n\n/**\n * Deep merges multiple arrays into a single array.\n * @param args - The arrays to merge. Can be empty, in which case returns an empty array.\n * @returns The merged array.\n */\nexport function deepMerge<T = unknown[]>(\n ...args: (unknown[] | null | undefined)[]\n): T;\n\n/**\n * Implementation of deepMerge function.\n * @param args - The objects or arrays to merge.\n * @returns The merged result.\n */\nexport function deepMerge<T = Record<string, unknown> | unknown[]>(\n ...args: (object | unknown[] | null | undefined)[]\n): T {\n const MAX_DEPTH = 20;\n\n /** Recursively merges an array of objects with depth and circular-reference tracking. */\n function mergeRecursive(\n validArgs: object[],\n depth: number,\n seen: WeakSet<object>,\n ): unknown {\n // Depth guard\n if (depth > MAX_DEPTH) {\n throw new Error(`deepMerge: Maximum depth of ${MAX_DEPTH} exceeded. Possible circular reference or excessively nested objects.`);\n }\n\n // Handle empty arguments\n if (validArgs.length === 0) {\n return {};\n }\n\n // If only one argument, return it directly\n if (validArgs.length === 1) {\n return validArgs[0];\n }\n\n // Check if all arguments are arrays\n if (validArgs.every(Array.isArray)) {\n return (validArgs as unknown[][]).flat();\n }\n\n // Check if all arguments are objects (but not arrays)\n if (validArgs.every(arg => typeof arg === 'object' && arg !== null && !Array.isArray(arg))) {\n const result = {} as Record<string, unknown>;\n\n for (const arg of validArgs) {\n // Circular reference protection (per-arg scope prevents false\n // positives when the same object appears in multiple branches)\n if (seen.has(arg)) {\n throw new Error('deepMerge: Circular reference detected.');\n }\n\n const obj = arg as Record<string, unknown>;\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n const value = obj[key];\n if (Object.hasOwn(result, key)) {\n const existingValue = result[key];\n if (\n typeof value === 'object' && value !== null\n && typeof existingValue === 'object' && existingValue !== null\n ) {\n if (Array.isArray(value) && Array.isArray(existingValue)) {\n result[key] = [...existingValue, ...value];\n }\n else if (!Array.isArray(value) && !Array.isArray(existingValue)) {\n result[key] = mergeRecursive(\n [existingValue as Record<string, unknown>, value as Record<string, unknown>],\n depth + 1,\n new WeakSet<object>(),\n );\n }\n else {\n // One is array, other is object — overwrite\n result[key] = value;\n }\n }\n else {\n result[key] = value;\n }\n }\n else {\n result[key] = value;\n }\n }\n }\n }\n return result;\n }\n\n // Check if all arguments are primitive values\n if (validArgs.every(arg => typeof arg !== 'object' || arg === null)) {\n throw new Error(\n 'deepMerge: Cannot merge primitive values. All arguments must be objects or arrays.',\n );\n }\n\n // Mixed types error\n const hasArrays = validArgs.some(Array.isArray);\n const hasObjects = validArgs.some(arg =>\n typeof arg === 'object' && arg !== null && !Array.isArray(arg),\n );\n\n if (hasArrays && hasObjects) {\n throw new Error(\n 'deepMerge: Cannot mix arrays and objects. All arguments must be either arrays or objects.',\n );\n }\n\n // Fallback for unexpected cases\n throw new Error(\n 'deepMerge: Invalid arguments provided. All arguments must be objects or arrays of the same type.',\n );\n }\n\n // Filter out null/undefined\n const validArgs = args.filter((arg): arg is object => arg !== null && arg !== undefined);\n\n return mergeRecursive(validArgs, 0, new WeakSet<object>()) as T;\n}\n\n/**\n * Normalizes MongoDB filters to support both dot notation strings and nested objects.\n * This function converts nested object filters to dot notation format while preserving\n * MongoDB operators to ensure consistent behavior across different filter input formats.\n *\n * @param filter - The filter object to normalize.\n * @returns A normalized filter object with nested objects converted to dot notation,\n * while preserving MongoDB operators as nested objects.\n *\n * @example\n * ```typescript\n * // Both of these will work the same way:\n * normalizeMongoFilter({ \"location.countryId\": \"240\" })\n * normalizeMongoFilter({ location: { countryId: \"240\" } })\n * // Both return: { \"location.countryId\": \"240\" }\n *\n * // MongoDB operators are preserved:\n * normalizeMongoFilter({ id: { $in: [\"240\", \"59\"] } })\n * // Returns: { id: { $in: [\"240\", \"59\"] } }\n * ```\n */\nexport function normalizeMongoFilter<T extends Record<string, unknown>>(filter: T): T {\n if (!filter || typeof filter !== 'object') {\n return filter;\n }\n\n const normalized: Record<string, unknown> = {};\n\n /**\n * Recursively flattens nested objects into dot-notation keys, preserving MongoDB operators.\n */\n function flatten(current: Record<string, unknown>, prefix: string) {\n for (const key in current) {\n if (!Object.hasOwn(current, key))\n continue;\n\n const value = current[key];\n const newKey = prefix ? `${prefix}.${key}` : key;\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n // Fast-path POJO check: `.constructor` is safe even on null-prototype objects\n // (returns undefined, so the === Object check simply fails and falls through\n // to the getPrototypeOf check which correctly identifies it as a POJO).\n const isPojo = (value as object).constructor === Object\n || Object.getPrototypeOf(value) === null;\n\n if (!isPojo) {\n normalized[newKey] = value;\n continue;\n }\n\n // Check for Mongo operator\n let hasMongoOperator = false;\n for (const subKey in value as Record<string, unknown>) {\n if (Object.hasOwn(value, subKey) && subKey.startsWith('$')) {\n hasMongoOperator = true;\n break;\n }\n }\n\n if (hasMongoOperator) {\n normalized[newKey] = value;\n }\n else {\n flatten(value as Record<string, unknown>, newKey);\n }\n }\n else {\n normalized[newKey] = value;\n }\n }\n }\n\n flatten(filter, '');\n\n return normalized as T;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"object.util.js","names":[],"sources":["../../../src/util/object/object.util.ts"],"sourcesContent":["/**\n * Check if a string is a valid JSON string.\n * This function attempts to parse the string as JSON and returns true if successful,\n * false if the string is not valid JSON.\n *\n * @param str - The string to check for valid JSON format.\n * @returns True if the string is a valid JSON string, false otherwise.\n */\nexport function isJSON(str: string): boolean {\n try {\n JSON.parse(str);\n return true;\n }\n catch {\n /* Intentionally empty — invalid JSON returns false */\n return false;\n }\n}\n\n/**\n * Gets a nested value from an object using a path array.\n * This function traverses the object following the provided path and returns\n * the value at the specified location, or undefined if the path doesn't exist.\n *\n * @param obj - The object to get the value from.\n * @param path - An array of keys representing the path to the desired value.\n * @returns The value at the specified path, or undefined if the path doesn't exist.\n */\nexport function getNestedValue<T>(obj: T, path: (string | number)[]): unknown {\n // Optimization: Loop is faster than reduce and allows early exit\n let current: unknown = obj;\n const len = path.length;\n\n for (let i = 0; i < len; i++) {\n // Optimization: Early return if current value is null/undefined or not an object\n // This avoids unnecessary key lookups and type checks\n if (current == null || typeof current !== 'object') {\n return undefined;\n }\n\n const key = path[i];\n\n if (key !== undefined && key in (current as Record<string | number, unknown>)) {\n current = (current as Record<string | number, unknown>)[key];\n }\n else {\n return undefined;\n }\n }\n\n return current;\n}\n\n/**\n * Recursively sets a value at a nested path within an object, creating intermediate objects as needed.\n *\n * @param obj - The source object.\n * @param path - Array of keys forming the path.\n * @param value - The value to set.\n * @param index - Current recursion depth.\n * @returns A new object with the value set at the specified path.\n */\nfunction setNestedValueHelper<T>(obj: T, path: (string | number)[], value: unknown, index: number): T {\n if (index >= path.length)\n return obj;\n\n const head = path[index];\n\n if (index === path.length - 1) {\n return {\n ...(obj as Record<string | number, unknown>),\n [head as string | number]: value,\n } as T;\n }\n\n const current = (obj as Record<string | number, unknown>)[head as string | number];\n\n return {\n ...(obj as Record<string | number, unknown>),\n [head as string | number | symbol]: setNestedValueHelper(\n typeof current === 'object' && current !== null\n ? (current as object)\n : {},\n path,\n value,\n index + 1,\n ),\n } as T;\n}\n\n/**\n * Sets a nested value in an object using a path array.\n * This function creates the path if it doesn't exist and sets the value at the specified location.\n * The function returns a new object with the updated value, maintaining immutability.\n *\n * @param obj - The object to set the value in.\n * @param path - An array of keys representing the path to the desired location.\n * @param value - The value to set at the specified path.\n * @returns A new object with the updated value at the specified path.\n */\nexport function setNestedValue<T>(obj: T, path: (string | number)[], value: unknown): T {\n if (path.length === 0)\n return obj;\n\n return setNestedValueHelper(obj, path, value, 0);\n}\n\n/**\n * Deep clones an object or array.\n * This function creates a deep copy of the input, recursively cloning objects and arrays.\n * Primitive values, dates, and other non-plain objects are returned as is (or cloned if supported).\n *\n * @remarks\n * **Non-POJO objects are NOT cloned.** Objects with custom prototypes (e.g., Mongoose ObjectId,\n * class instances, Map, Set, Buffer) are returned **by reference** to preserve driver\n * compatibility. Mutations to these nested references will affect the original.\n * If you need full deep cloning of complex types, use `structuredClone()` or a dedicated library.\n *\n * @param obj - The object to clone.\n * @returns A deep copy of the object (with non-POJO objects returned by reference).\n */\nexport function deepClone<T>(obj: T): T {\n return deepCloneInternal(obj, new WeakMap<object, unknown>());\n}\n\n/**\n * Internal recursive implementation of deepClone with shared-reference and circular-reference handling.\n *\n * Uses a WeakMap to track already-cloned objects. This correctly handles:\n * - **Shared references**: The same object appearing in multiple places returns\n * the same clone (preserving the shared-reference topology).\n * - **Circular references**: Detected because the object is added to the map\n * before* its children are recursed into; a back-edge will find itself in\n * the map and return the (partially constructed) clone rather than recursing\n * infinitely.\n *\n * @param obj - The value to clone.\n * @param seen - A WeakMap mapping original objects to their clones.\n * @returns A deep copy of the value.\n */\nfunction deepCloneInternal<T>(obj: T, seen: WeakMap<object, unknown>): T {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n // Return the already-cloned copy for shared (or circular) references\n if (seen.has(obj as object)) {\n return seen.get(obj as object) as T;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T;\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T;\n }\n\n if (Array.isArray(obj)) {\n // Optimization: `new Array(len)` + `for` loop is ~10-15% faster than `Array.map` or `Array.from`\n // for large arrays since it avoids callback overhead and pre-allocates memory.\n const len = obj.length;\n // eslint-disable-next-line unicorn/no-new-array -- Pre-allocating array size for performance\n const arr = new Array(len);\n // Register before recursing to handle circular references within arrays\n seen.set(obj as object, arr);\n\n for (let i = 0; i < len; i++) {\n arr[i] = deepCloneInternal(obj[i], seen);\n }\n\n return arr as unknown as T;\n }\n\n // Handle Mongoose ObjectId and other custom classes by returning reference.\n // structuredClone is not used here because it silently corrupts nested non-POJO\n // types (e.g., ObjectId → plain object) and Date instances in jsdom environments.\n const proto = Object.getPrototypeOf(obj);\n\n if (proto !== Object.prototype && proto !== null) {\n return obj;\n }\n\n const result = {} as Record<string, unknown>;\n // Register before recursing to handle circular references within objects\n seen.set(obj as object, result);\n\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n result[key] = deepCloneInternal((obj as Record<string, unknown>)[key], seen);\n }\n }\n\n return result as T;\n}\n\n/**\n * Deep merges multiple objects into a single object.\n * @param args - The objects to merge. Can be empty, in which case returns an empty object.\n * @returns The merged object.\n */\nexport function deepMerge<T = Record<string, unknown>>(\n ...args: (object | null | undefined)[]\n): T;\n\n/**\n * Deep merges multiple arrays into a single array.\n * @param args - The arrays to merge. Can be empty, in which case returns an empty array.\n * @returns The merged array.\n */\nexport function deepMerge<T = unknown[]>(\n ...args: (unknown[] | null | undefined)[]\n): T;\n\n/**\n * Implementation of deepMerge function.\n * @param args - The objects or arrays to merge.\n * @returns The merged result.\n */\nexport function deepMerge<T = Record<string, unknown> | unknown[]>(\n ...args: (object | unknown[] | null | undefined)[]\n): T {\n const MAX_DEPTH = 20;\n\n /** Recursively merges an array of objects with depth and circular-reference tracking. */\n function mergeRecursive(\n validArgs: object[],\n depth: number,\n seen: WeakSet<object>,\n ): unknown {\n // Depth guard\n if (depth > MAX_DEPTH) {\n throw new Error(`deepMerge: Maximum depth of ${MAX_DEPTH} exceeded. Possible circular reference or excessively nested objects.`);\n }\n\n // Handle empty arguments\n if (validArgs.length === 0) {\n return {};\n }\n\n // If only one argument, return it directly\n if (validArgs.length === 1) {\n return validArgs[0];\n }\n\n // Check if all arguments are arrays\n if (validArgs.every(Array.isArray)) {\n return (validArgs as unknown[][]).flat();\n }\n\n // Check if all arguments are objects (but not arrays)\n if (validArgs.every(arg => typeof arg === 'object' && arg !== null && !Array.isArray(arg))) {\n const result = {} as Record<string, unknown>;\n\n for (const arg of validArgs) {\n // Circular reference protection (per-arg scope prevents false\n // positives when the same object appears in multiple branches)\n if (seen.has(arg)) {\n throw new Error('deepMerge: Circular reference detected.');\n }\n\n const obj = arg as Record<string, unknown>;\n\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n const value = obj[key];\n\n if (Object.hasOwn(result, key)) {\n const existingValue = result[key];\n\n if (\n typeof value === 'object' && value !== null\n && typeof existingValue === 'object' && existingValue !== null\n ) {\n if (Array.isArray(value) && Array.isArray(existingValue)) {\n result[key] = [...existingValue, ...value];\n }\n else if (!Array.isArray(value) && !Array.isArray(existingValue)) {\n result[key] = mergeRecursive(\n [existingValue as Record<string, unknown>, value as Record<string, unknown>],\n depth + 1,\n seen,\n );\n }\n else {\n // One is array, other is object — overwrite\n result[key] = value;\n }\n }\n else {\n result[key] = value;\n }\n }\n else {\n result[key] = value;\n }\n }\n }\n }\n\n return result;\n }\n\n // Check if all arguments are primitive values\n if (validArgs.every(arg => typeof arg !== 'object' || arg === null)) {\n throw new Error(\n 'deepMerge: Cannot merge primitive values. All arguments must be objects or arrays.',\n );\n }\n\n // Mixed types error\n const hasArrays = validArgs.some(Array.isArray);\n const hasObjects = validArgs.some(arg =>\n typeof arg === 'object' && arg !== null && !Array.isArray(arg),\n );\n\n if (hasArrays && hasObjects) {\n throw new Error(\n 'deepMerge: Cannot mix arrays and objects. All arguments must be either arrays or objects.',\n );\n }\n\n // Fallback for unexpected cases\n throw new Error(\n 'deepMerge: Invalid arguments provided. All arguments must be objects or arrays of the same type.',\n );\n }\n\n // Filter out null/undefined\n const validArgs = args.filter((arg): arg is object => arg !== null && arg !== undefined);\n\n return mergeRecursive(validArgs, 0, new WeakSet<object>()) as T;\n}\n\n/**\n * Normalizes MongoDB filters to support both dot notation strings and nested objects.\n * This function converts nested object filters to dot notation format while preserving\n * MongoDB operators to ensure consistent behavior across different filter input formats.\n *\n * @param filter - The filter object to normalize.\n * @returns A normalized filter object with nested objects converted to dot notation,\n * while preserving MongoDB operators as nested objects.\n *\n * @example\n * ```typescript\n * // Both of these will work the same way:\n * normalizeMongoFilter({ \"location.countryId\": \"240\" })\n * normalizeMongoFilter({ location: { countryId: \"240\" } })\n * // Both return: { \"location.countryId\": \"240\" }\n *\n * // MongoDB operators are preserved:\n * normalizeMongoFilter({ id: { $in: [\"240\", \"59\"] } })\n * // Returns: { id: { $in: [\"240\", \"59\"] } }\n * ```\n */\nexport function normalizeMongoFilter<T extends Record<string, unknown>>(filter: T): T {\n if (!filter || typeof filter !== 'object') {\n return filter;\n }\n\n let isFlat = true;\n\n for (const key in filter) {\n if (!Object.hasOwn(filter, key)) {\n continue;\n }\n const value = filter[key];\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n isFlat = false;\n break;\n }\n }\n\n if (isFlat) {\n return filter;\n }\n\n const normalized: Record<string, unknown> = {};\n const MAX_DEPTH = 10;\n\n /**\n * Recursively flattens nested objects into dot-notation keys, preserving MongoDB operators.\n */\n function flatten(current: Record<string, unknown>, prefix: string, depth: number) {\n if (depth > MAX_DEPTH) {\n throw new Error(`normalizeMongoFilter: Maximum depth of ${MAX_DEPTH} exceeded. Possible circular reference or excessively nested filter.`);\n }\n\n for (const key in current) {\n if (!Object.hasOwn(current, key)) {\n continue;\n }\n const value = current[key];\n const newKey = prefix ? `${prefix}.${key}` : key;\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n // Fast-path POJO check: `.constructor` is safe even on null-prototype objects\n // (returns undefined, so the === Object check simply fails and falls through\n // to the getPrototypeOf check which correctly identifies it as a POJO).\n const isPojo = (value as object).constructor === Object\n || Object.getPrototypeOf(value) === null;\n\n if (!isPojo) {\n normalized[newKey] = value;\n continue;\n }\n\n // Check for Mongo operator\n let hasMongoOperator = false;\n\n for (const subKey in value as Record<string, unknown>) {\n if (Object.hasOwn(value, subKey) && subKey.startsWith('$')) {\n hasMongoOperator = true;\n break;\n }\n }\n\n if (hasMongoOperator) {\n normalized[newKey] = value;\n }\n else {\n flatten(value as Record<string, unknown>, newKey, depth + 1);\n }\n }\n else {\n normalized[newKey] = value;\n }\n }\n }\n\n flatten(filter, '', 0);\n\n return normalized as T;\n}\n"],"mappings":"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"}
|
|
@@ -16,6 +16,14 @@ import { I_Serializer } from './serializer.type.js';
|
|
|
16
16
|
* - RegExp → `{ __type: 'RegExp', value: { source: '...', flags: '...' } }`
|
|
17
17
|
* - BigInt → `{ __type: 'BigInt', value: '12345' }`
|
|
18
18
|
*
|
|
19
|
+
* **Security:** Only types in the `ALLOWED_TYPES` allowlist are reconstructed during
|
|
20
|
+
* deserialization. Unknown `__type` values are returned as-is to prevent prototype pollution.
|
|
21
|
+
* RegExp sources are length-limited (1000 chars) and flags are validated to mitigate ReDoS.
|
|
22
|
+
*
|
|
23
|
+
* **⚠️ NOT for untrusted input:** This serializer is designed for internal service-to-service
|
|
24
|
+
* communication. Do NOT use it to deserialize user-controlled payloads (e.g., raw WebSocket
|
|
25
|
+
* messages, API request bodies from external clients) without additional validation.
|
|
26
|
+
*
|
|
19
27
|
* **Cross-service compatibility:** Any service that deserializes data produced by this serializer
|
|
20
28
|
* must use the same `__type` protocol. Plain `JSON.parse` will return the wrapper objects as-is
|
|
21
29
|
* without reconstructing the original types.
|
|
@@ -1,74 +1,61 @@
|
|
|
1
1
|
//#region src/util/serializer/serializer.util.ts
|
|
2
|
-
var e =
|
|
3
|
-
Date
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let { source: t, flags: n } = e;
|
|
38
|
-
return new RegExp(t, n);
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
BigInt: {
|
|
42
|
-
is: (e) => typeof e == "bigint",
|
|
43
|
-
serialize: (e) => ({
|
|
44
|
-
__type: "BigInt",
|
|
45
|
-
value: e.toString()
|
|
46
|
-
}),
|
|
47
|
-
deserialize: (e) => BigInt(e)
|
|
48
|
-
}
|
|
49
|
-
}, t = Object.entries(e).filter(([e]) => e !== "Date").map(([, e]) => e), n = {
|
|
50
|
-
serialize(n) {
|
|
51
|
-
return JSON.stringify(n, function(n, r) {
|
|
52
|
-
let i = this[n];
|
|
53
|
-
if (i instanceof Date) return e.Date.serialize(i);
|
|
54
|
-
for (let e = 0; e < t.length; e++) {
|
|
55
|
-
let n = t[e];
|
|
56
|
-
if (n.is(r)) return n.serialize(r);
|
|
57
|
-
}
|
|
58
|
-
return r;
|
|
2
|
+
var e = new Set([
|
|
3
|
+
"Date",
|
|
4
|
+
"Map",
|
|
5
|
+
"Set",
|
|
6
|
+
"RegExp",
|
|
7
|
+
"BigInt"
|
|
8
|
+
]), t = 1e3, n = /^[dgimsuvy]*$/, r = {
|
|
9
|
+
serialize(e) {
|
|
10
|
+
return JSON.stringify(e, function(e, t) {
|
|
11
|
+
let n = this[e];
|
|
12
|
+
if (n instanceof Date) return {
|
|
13
|
+
__type: "Date",
|
|
14
|
+
value: n.toISOString()
|
|
15
|
+
};
|
|
16
|
+
if (typeof t == "object" && t) {
|
|
17
|
+
if (t instanceof Map) return {
|
|
18
|
+
__type: "Map",
|
|
19
|
+
value: Array.from(t.entries())
|
|
20
|
+
};
|
|
21
|
+
if (t instanceof Set) return {
|
|
22
|
+
__type: "Set",
|
|
23
|
+
value: Array.from(t)
|
|
24
|
+
};
|
|
25
|
+
if (t instanceof RegExp) return {
|
|
26
|
+
__type: "RegExp",
|
|
27
|
+
value: {
|
|
28
|
+
source: t.source,
|
|
29
|
+
flags: t.flags
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
} else if (typeof t == "bigint") return {
|
|
33
|
+
__type: "BigInt",
|
|
34
|
+
value: t.toString()
|
|
35
|
+
};
|
|
36
|
+
return t;
|
|
59
37
|
});
|
|
60
38
|
},
|
|
61
|
-
deserialize(
|
|
62
|
-
return JSON.parse(
|
|
63
|
-
if (
|
|
64
|
-
let
|
|
65
|
-
if (
|
|
39
|
+
deserialize(r) {
|
|
40
|
+
return JSON.parse(r, (r, i) => {
|
|
41
|
+
if (typeof i == "object" && i && typeof i.__type == "string") {
|
|
42
|
+
let r = i.__type;
|
|
43
|
+
if (!e.has(r)) return i;
|
|
44
|
+
let a = i.value;
|
|
45
|
+
if (r === "Date") return new Date(a);
|
|
46
|
+
if (r === "Map") return new Map(a);
|
|
47
|
+
if (r === "Set") return new Set(a);
|
|
48
|
+
if (r === "RegExp") {
|
|
49
|
+
let { source: e, flags: r } = a;
|
|
50
|
+
return typeof e != "string" || typeof r != "string" || e.length > t || !n.test(r) ? i : new RegExp(e, r);
|
|
51
|
+
}
|
|
52
|
+
if (r === "BigInt") return BigInt(a);
|
|
66
53
|
}
|
|
67
|
-
return
|
|
54
|
+
return i;
|
|
68
55
|
});
|
|
69
56
|
}
|
|
70
57
|
};
|
|
71
58
|
//#endregion
|
|
72
|
-
export {
|
|
59
|
+
export { r as serializer };
|
|
73
60
|
|
|
74
61
|
//# sourceMappingURL=serializer.util.js.map
|