From ab289186f1e4a2356d4451ef0c6ab4e7937c194c Mon Sep 17 00:00:00 2001 From: tone <3341154833@qq.com> Date: Sat, 15 Nov 2025 18:07:21 +0800 Subject: [PATCH] feat: add public method utilities and corresponding tests --- __tests__/unit/utils/utils.test.ts | 27 +++++++++++++++++++- src/index.ts | 2 ++ src/utils/utils.ts | 40 ++++++++++++++++++++++++++++++ tsconfig.json | 4 +-- 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/__tests__/unit/utils/utils.test.ts b/__tests__/unit/utils/utils.test.ts index a7bd9d0..557ac63 100644 --- a/__tests__/unit/utils/utils.test.ts +++ b/__tests__/unit/utils/utils.test.ts @@ -1,4 +1,4 @@ -import { getRandomAvailablePort, isObject, isString, makeId } from "@/utils/utils" +import { getRandomAvailablePort, isObject, isPublicMethod, isString, makeId, markAsPublicMethod } from "@/utils/utils" test('makeId', () => { const id = makeId(); @@ -26,4 +26,29 @@ test('getRandomAvailablePort', async () => { const port = await getRandomAvailablePort(); expect(port).toBeGreaterThanOrEqual(1); expect(port).toBeLessThanOrEqual(65535); +}) + +test('markAsPublick', () => { + const shallowObj = { + fn1() { }, + l1: { + fn1() { }, + } + }; + + const deepObj = { + fn1() { }, + l1: { + fn1() { }, + } + }; + + markAsPublicMethod(shallowObj); + markAsPublicMethod(deepObj, { deep: true }); + + expect(isPublicMethod(shallowObj.fn1)).toBeTruthy(); + expect(isPublicMethod(shallowObj.l1.fn1)).toBeUndefined(); + + expect(isPublicMethod(deepObj.fn1)).toBeTruthy(); + expect(isPublicMethod(deepObj.l1.fn1)).toBeTruthy(); }) \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index a38a571..5781f33 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,8 @@ export { injectSocketClient } from "./core/SocketClient"; export { injectSocketServer } from "./core/SocketServer"; import { injectSocketIOImplements } from "./implements/socket.io"; +export { publicMethod, isPublicMethod, markAsPublicMethod } from './utils/utils'; + injectSocketIOImplements(); export { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 625c35b..b164b99 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -4,8 +4,12 @@ export const makeId = () => md5(`${Date.now()}${Math.random()}`); export const isObject = (v: unknown): v is Record => typeof v === 'object' && v !== null; +export const isArray = (v: unknown): v is Array => Array.isArray(v); + export const isString = (v: unknown): v is string => typeof v === 'string'; +export const isFunction = (v: unknown): v is Function => typeof v === 'function'; + export type ObjectType = Record; export type ToDeepPromise = { @@ -41,4 +45,40 @@ export async function getRandomAvailablePort() { server.listen(0); }) +} + +const publicMethodMap = new WeakMap(); +export function publicMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) { + publicMethodMap.set(descriptor.value, true); +}; +export function isPublicMethod(target: Function) { + return publicMethodMap.get(target); +}; +export function markAsPublicMethod | unknown>(obj: T, options?: { + deep?: boolean +}): T { + const accessed = new Set(); + function markAs(obj: Function | Record | unknown) { + if (accessed.has(obj)) { + return; + } + + if (isFunction(obj)) { + publicMethodMap.set(obj, true); + } else if (isObject(obj)) { + accessed.add(obj); + + Object.values(obj).forEach(subObj => { + if (isFunction(subObj)) { + publicMethodMap.set(subObj, true); + } + if (options?.deep && isObject(subObj)) { + markAs(subObj); + } + }); + } + } + + markAs(obj); + return obj; } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 40cbb97..3ffdf2f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,8 +12,8 @@ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */