@simplysm/service-common 14.0.26 → 14.0.28
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/app-structure/app-structure.types.d.ts +33 -0
- package/dist/app-structure/app-structure.types.d.ts.map +1 -0
- package/dist/app-structure/app-structure.types.js +2 -0
- package/dist/app-structure/app-structure.types.js.map +1 -0
- package/dist/app-structure/app-structure.utils.d.ts +5 -0
- package/dist/app-structure/app-structure.utils.d.ts.map +1 -0
- package/dist/app-structure/app-structure.utils.js +85 -0
- package/dist/app-structure/app-structure.utils.js.map +1 -0
- package/dist/define-event.d.ts +6 -4
- package/dist/define-event.d.ts.map +1 -1
- package/dist/define-event.js +6 -4
- package/dist/define-event.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/service-types/app-structure-service.types.d.ts +10 -0
- package/dist/service-types/app-structure-service.types.d.ts.map +1 -0
- package/dist/service-types/app-structure-service.types.js +2 -0
- package/dist/service-types/app-structure-service.types.js.map +1 -0
- package/package.json +3 -3
- package/src/app-structure/app-structure.types.ts +38 -0
- package/src/app-structure/app-structure.utils.ts +122 -0
- package/src/define-event.ts +6 -4
- package/src/index.ts +5 -0
- package/src/service-types/app-structure-service.types.ts +10 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type AppStructureItem<TModule = unknown> = AppStructureGroupItem<TModule> | AppStructureLeafItem<TModule>;
|
|
2
|
+
export interface AppStructureGroupItem<TModule> {
|
|
3
|
+
code: string;
|
|
4
|
+
title: string;
|
|
5
|
+
modules?: TModule[];
|
|
6
|
+
requiredModules?: TModule[];
|
|
7
|
+
icon?: string;
|
|
8
|
+
children: AppStructureItem<TModule>[];
|
|
9
|
+
}
|
|
10
|
+
export interface AppStructureLeafItem<TModule> {
|
|
11
|
+
code: string;
|
|
12
|
+
title: string;
|
|
13
|
+
modules?: TModule[];
|
|
14
|
+
requiredModules?: TModule[];
|
|
15
|
+
perms?: ("use" | "edit")[];
|
|
16
|
+
subPerms?: AppStructureSubPermission<TModule>[];
|
|
17
|
+
icon?: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
isNotMenu?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface AppStructureSubPermission<TModule> {
|
|
22
|
+
code: string;
|
|
23
|
+
title: string;
|
|
24
|
+
modules?: TModule[];
|
|
25
|
+
requiredModules?: TModule[];
|
|
26
|
+
perms: ("use" | "edit")[];
|
|
27
|
+
}
|
|
28
|
+
export interface FlatPermission<TModule = unknown> {
|
|
29
|
+
titleChain: string[];
|
|
30
|
+
codeChain: string[];
|
|
31
|
+
modulesChain: TModule[][];
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=app-structure.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-structure.types.d.ts","sourceRoot":"","sources":["../../src/app-structure/app-structure.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,CAAC,OAAO,GAAG,OAAO,IAC1C,qBAAqB,CAAC,OAAO,CAAC,GAC9B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAElC,MAAM,WAAW,qBAAqB,CAAC,OAAO;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB,CAAC,OAAO;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB,CAAC,OAAO;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc,CAAC,OAAO,GAAG,OAAO;IAC/C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-structure.types.js","sourceRoot":"","sources":["../../src/app-structure/app-structure.types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AppStructureItem, FlatPermission } from "./app-structure.types";
|
|
2
|
+
export declare function isUsableModules<TModule>(modules: TModule[] | undefined, requiredModules: TModule[] | undefined, usableModules: TModule[] | undefined): boolean;
|
|
3
|
+
export declare function isUsableModulesChain<TModule>(modulesChain: TModule[][], requiredModulesChain: TModule[][], usableModules: TModule[] | undefined): boolean;
|
|
4
|
+
export declare function getFlatPermissions<TModule>(items: AppStructureItem<TModule>[], usableModules: TModule[] | undefined): FlatPermission<TModule>[];
|
|
5
|
+
//# sourceMappingURL=app-structure.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-structure.utils.d.ts","sourceRoot":"","sources":["../../src/app-structure/app-structure.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACf,MAAM,uBAAuB,CAAC;AAE/B,wBAAgB,eAAe,CAAC,OAAO,EACrC,OAAO,EAAE,OAAO,EAAE,GAAG,SAAS,EAC9B,eAAe,EAAE,OAAO,EAAE,GAAG,SAAS,EACtC,aAAa,EAAE,OAAO,EAAE,GAAG,SAAS,GACnC,OAAO,CAYT;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAC1C,YAAY,EAAE,OAAO,EAAE,EAAE,EACzB,oBAAoB,EAAE,OAAO,EAAE,EAAE,EACjC,aAAa,EAAE,OAAO,EAAE,GAAG,SAAS,GACnC,OAAO,CAgBT;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EACxC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAClC,aAAa,EAAE,OAAO,EAAE,GAAG,SAAS,GACnC,cAAc,CAAC,OAAO,CAAC,EAAE,CAyE3B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function isUsableModules(modules, requiredModules, usableModules) {
|
|
2
|
+
// 1. requiredModules: 모두 있어야 함 (AND)
|
|
3
|
+
if (requiredModules && requiredModules.length > 0) {
|
|
4
|
+
if (!requiredModules.every((m) => usableModules?.includes(m))) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
// 2. modules: 하나라도 있으면 됨 (OR)
|
|
9
|
+
return (modules == null || modules.length === 0 || modules.some((m) => usableModules?.includes(m)));
|
|
10
|
+
}
|
|
11
|
+
export function isUsableModulesChain(modulesChain, requiredModulesChain, usableModules) {
|
|
12
|
+
// 각 레벨의 modules (OR) 체크
|
|
13
|
+
for (const modules of modulesChain) {
|
|
14
|
+
if (!isUsableModules(modules, undefined, usableModules)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// 각 레벨의 requiredModules (AND) 체크
|
|
19
|
+
for (const requiredModules of requiredModulesChain) {
|
|
20
|
+
if (!isUsableModules(undefined, requiredModules, usableModules)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
export function getFlatPermissions(items, usableModules) {
|
|
27
|
+
const results = [];
|
|
28
|
+
const queue = items.map((item) => ({
|
|
29
|
+
item,
|
|
30
|
+
titleChain: [],
|
|
31
|
+
codeChain: [],
|
|
32
|
+
modulesChain: [],
|
|
33
|
+
requiredModulesChain: [],
|
|
34
|
+
}));
|
|
35
|
+
while (queue.length > 0) {
|
|
36
|
+
const { item, titleChain, codeChain, modulesChain, requiredModulesChain } = queue.shift();
|
|
37
|
+
const currTitleChain = [...titleChain, item.title];
|
|
38
|
+
const currCodeChain = [...codeChain, item.code];
|
|
39
|
+
const currModulesChain = item.modules ? [...modulesChain, item.modules] : modulesChain;
|
|
40
|
+
const currRequiredModulesChain = item.requiredModules
|
|
41
|
+
? [...requiredModulesChain, item.requiredModules]
|
|
42
|
+
: requiredModulesChain;
|
|
43
|
+
if (!isUsableModulesChain(currModulesChain, currRequiredModulesChain, usableModules))
|
|
44
|
+
continue;
|
|
45
|
+
// 1. 자식 enqueue
|
|
46
|
+
if ("children" in item) {
|
|
47
|
+
for (const child of item.children) {
|
|
48
|
+
queue.push({
|
|
49
|
+
item: child,
|
|
50
|
+
titleChain: currTitleChain,
|
|
51
|
+
codeChain: currCodeChain,
|
|
52
|
+
modulesChain: currModulesChain,
|
|
53
|
+
requiredModulesChain: currRequiredModulesChain,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 1. 직접 perms 처리
|
|
58
|
+
if ("perms" in item) {
|
|
59
|
+
for (const perm of item.perms ?? []) {
|
|
60
|
+
results.push({
|
|
61
|
+
titleChain: currTitleChain,
|
|
62
|
+
codeChain: [...currCodeChain, perm],
|
|
63
|
+
modulesChain: currModulesChain,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 2. subPerms 처리
|
|
68
|
+
if ("subPerms" in item) {
|
|
69
|
+
for (const subPerm of item.subPerms ?? []) {
|
|
70
|
+
// subPerm도 모듈 체크
|
|
71
|
+
if (!isUsableModules(subPerm.modules, subPerm.requiredModules, usableModules))
|
|
72
|
+
continue;
|
|
73
|
+
for (const perm of subPerm.perms) {
|
|
74
|
+
results.push({
|
|
75
|
+
titleChain: currTitleChain,
|
|
76
|
+
codeChain: [...currCodeChain, subPerm.code, perm],
|
|
77
|
+
modulesChain: [...currModulesChain, subPerm.modules ?? []],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=app-structure.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-structure.utils.js","sourceRoot":"","sources":["../../src/app-structure/app-structure.utils.ts"],"names":[],"mappings":"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"}
|
package/dist/define-event.d.ts
CHANGED
|
@@ -13,13 +13,15 @@ export interface ServiceEventDef<TInfo = unknown, TData = unknown> {
|
|
|
13
13
|
* 타입 안전한 info와 data를 가진 서비스 이벤트를 정의한다.
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
|
-
*
|
|
16
|
+
* // 서버에서 이벤트 정의 + 타입 export
|
|
17
|
+
* export const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
|
|
17
18
|
*
|
|
18
19
|
* // 서버에서 이벤트 발생
|
|
19
|
-
*
|
|
20
|
+
* await server.emitEvent<typeof OrderUpdated>("OrderUpdated", (info) => info.orderId === 123, { status: "shipped" });
|
|
20
21
|
*
|
|
21
|
-
* // 클라이언트에서 구독
|
|
22
|
-
*
|
|
22
|
+
* // 클라이언트에서 구독 (import type으로 타입만 가져옴)
|
|
23
|
+
* import type { OrderUpdated } from "@server-package";
|
|
24
|
+
* await client.addListener<typeof OrderUpdated>("OrderUpdated", { orderId: 123 }, async (data) => {
|
|
23
25
|
* console.log(data.status); // typed
|
|
24
26
|
* });
|
|
25
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-event.d.ts","sourceRoot":"","sources":["../src/define-event.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED
|
|
1
|
+
{"version":3,"file":"define-event.d.ts","sourceRoot":"","sources":["../src/define-event.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAC1D,SAAS,EAAE,MAAM,GAChB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAM/B"}
|
package/dist/define-event.js
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* 타입 안전한 info와 data를 가진 서비스 이벤트를 정의한다.
|
|
3
3
|
*
|
|
4
4
|
* @example
|
|
5
|
-
*
|
|
5
|
+
* // 서버에서 이벤트 정의 + 타입 export
|
|
6
|
+
* export const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
|
|
6
7
|
*
|
|
7
8
|
* // 서버에서 이벤트 발생
|
|
8
|
-
*
|
|
9
|
+
* await server.emitEvent<typeof OrderUpdated>("OrderUpdated", (info) => info.orderId === 123, { status: "shipped" });
|
|
9
10
|
*
|
|
10
|
-
* // 클라이언트에서 구독
|
|
11
|
-
*
|
|
11
|
+
* // 클라이언트에서 구독 (import type으로 타입만 가져옴)
|
|
12
|
+
* import type { OrderUpdated } from "@server-package";
|
|
13
|
+
* await client.addListener<typeof OrderUpdated>("OrderUpdated", { orderId: 123 }, async (data) => {
|
|
12
14
|
* console.log(data.status); // typed
|
|
13
15
|
* });
|
|
14
16
|
*/
|
package/dist/define-event.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-event.js","sourceRoot":"","sources":["../src/define-event.ts"],"names":[],"mappings":"AAYA
|
|
1
|
+
{"version":3,"file":"define-event.js","sourceRoot":"","sources":["../src/define-event.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CACzB,SAAiB;IAEjB,OAAO;QACL,SAAS;QACT,KAAK,EAAE,SAA6B;QACpC,KAAK,EAAE,SAA6B;KACrC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export * from "./protocol/protocol.types";
|
|
|
2
2
|
export * from "./protocol/create-service-protocol";
|
|
3
3
|
export * from "./service-types/orm-service.types";
|
|
4
4
|
export * from "./service-types/auto-update-service.types";
|
|
5
|
+
export * from "./service-types/app-structure-service.types";
|
|
5
6
|
export * from "./types";
|
|
7
|
+
export * from "./app-structure/app-structure.types";
|
|
8
|
+
export * from "./app-structure/app-structure.utils";
|
|
6
9
|
export * from "./define-event";
|
|
7
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAGnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAGnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAE5D,cAAc,SAAS,CAAC;AAGxB,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AAGpD,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,12 @@ export * from "./protocol/create-service-protocol.js";
|
|
|
4
4
|
// 서비스 타입
|
|
5
5
|
export * from "./service-types/orm-service.types.js";
|
|
6
6
|
export * from "./service-types/auto-update-service.types.js";
|
|
7
|
+
export * from "./service-types/app-structure-service.types.js";
|
|
7
8
|
// 타입
|
|
8
9
|
export * from "./types.js";
|
|
10
|
+
// App Structure
|
|
11
|
+
export * from "./app-structure/app-structure.types.js";
|
|
12
|
+
export * from "./app-structure/app-structure.utils.js";
|
|
9
13
|
// 정의
|
|
10
14
|
export * from "./define-event.js";
|
|
11
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAEnD,SAAS;AACT,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,KAAK;AACL,cAAc,SAAS,CAAC;AAExB,KAAK;AACL,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AAEnD,SAAS;AACT,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,KAAK;AACL,cAAc,SAAS,CAAC;AAExB,gBAAgB;AAChB,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AAEpD,KAAK;AACL,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AppStructureItem } from "../app-structure/app-structure.types";
|
|
2
|
+
/**
|
|
3
|
+
* AppStructure 서비스 인터페이스
|
|
4
|
+
*
|
|
5
|
+
* 서버에 등록된 앱 구조 항목을 클라이언트명 기준 맵으로 조회한다.
|
|
6
|
+
*/
|
|
7
|
+
export interface AppStructureService {
|
|
8
|
+
getItems(): Record<string, AppStructureItem[]>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=app-structure-service.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-structure-service.types.d.ts","sourceRoot":"","sources":["../../src/service-types/app-structure-service.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;CAChD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-structure-service.types.js","sourceRoot":"","sources":["../../src/service-types/app-structure-service.types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/service-common",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.28",
|
|
4
4
|
"description": "심플리즘 패키지 - 서비스 (common)",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@types/node": "^20.19.39"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@simplysm/core-common": "14.0.
|
|
25
|
-
"@simplysm/orm-common": "14.0.
|
|
24
|
+
"@simplysm/core-common": "14.0.28",
|
|
25
|
+
"@simplysm/orm-common": "14.0.28"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type AppStructureItem<TModule = unknown> =
|
|
2
|
+
| AppStructureGroupItem<TModule>
|
|
3
|
+
| AppStructureLeafItem<TModule>;
|
|
4
|
+
|
|
5
|
+
export interface AppStructureGroupItem<TModule> {
|
|
6
|
+
code: string;
|
|
7
|
+
title: string;
|
|
8
|
+
modules?: TModule[];
|
|
9
|
+
requiredModules?: TModule[];
|
|
10
|
+
icon?: string;
|
|
11
|
+
children: AppStructureItem<TModule>[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AppStructureLeafItem<TModule> {
|
|
15
|
+
code: string;
|
|
16
|
+
title: string;
|
|
17
|
+
modules?: TModule[];
|
|
18
|
+
requiredModules?: TModule[];
|
|
19
|
+
perms?: ("use" | "edit")[];
|
|
20
|
+
subPerms?: AppStructureSubPermission<TModule>[];
|
|
21
|
+
icon?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
isNotMenu?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AppStructureSubPermission<TModule> {
|
|
27
|
+
code: string;
|
|
28
|
+
title: string;
|
|
29
|
+
modules?: TModule[];
|
|
30
|
+
requiredModules?: TModule[];
|
|
31
|
+
perms: ("use" | "edit")[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface FlatPermission<TModule = unknown> {
|
|
35
|
+
titleChain: string[];
|
|
36
|
+
codeChain: string[];
|
|
37
|
+
modulesChain: TModule[][];
|
|
38
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AppStructureItem,
|
|
3
|
+
FlatPermission,
|
|
4
|
+
} from "./app-structure.types";
|
|
5
|
+
|
|
6
|
+
export function isUsableModules<TModule>(
|
|
7
|
+
modules: TModule[] | undefined,
|
|
8
|
+
requiredModules: TModule[] | undefined,
|
|
9
|
+
usableModules: TModule[] | undefined,
|
|
10
|
+
): boolean {
|
|
11
|
+
// 1. requiredModules: 모두 있어야 함 (AND)
|
|
12
|
+
if (requiredModules && requiredModules.length > 0) {
|
|
13
|
+
if (!requiredModules.every((m) => usableModules?.includes(m))) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 2. modules: 하나라도 있으면 됨 (OR)
|
|
19
|
+
return (
|
|
20
|
+
modules == null || modules.length === 0 || modules.some((m) => usableModules?.includes(m))
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isUsableModulesChain<TModule>(
|
|
25
|
+
modulesChain: TModule[][],
|
|
26
|
+
requiredModulesChain: TModule[][],
|
|
27
|
+
usableModules: TModule[] | undefined,
|
|
28
|
+
): boolean {
|
|
29
|
+
// 각 레벨의 modules (OR) 체크
|
|
30
|
+
for (const modules of modulesChain) {
|
|
31
|
+
if (!isUsableModules(modules, undefined, usableModules)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 각 레벨의 requiredModules (AND) 체크
|
|
37
|
+
for (const requiredModules of requiredModulesChain) {
|
|
38
|
+
if (!isUsableModules(undefined, requiredModules, usableModules)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getFlatPermissions<TModule>(
|
|
47
|
+
items: AppStructureItem<TModule>[],
|
|
48
|
+
usableModules: TModule[] | undefined,
|
|
49
|
+
): FlatPermission<TModule>[] {
|
|
50
|
+
const results: FlatPermission<TModule>[] = [];
|
|
51
|
+
|
|
52
|
+
type QueueItem = {
|
|
53
|
+
item: AppStructureItem<TModule>;
|
|
54
|
+
titleChain: string[];
|
|
55
|
+
codeChain: string[];
|
|
56
|
+
modulesChain: TModule[][];
|
|
57
|
+
requiredModulesChain: TModule[][];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const queue: QueueItem[] = items.map((item) => ({
|
|
61
|
+
item,
|
|
62
|
+
titleChain: [],
|
|
63
|
+
codeChain: [],
|
|
64
|
+
modulesChain: [],
|
|
65
|
+
requiredModulesChain: [],
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
while (queue.length > 0) {
|
|
69
|
+
const { item, titleChain, codeChain, modulesChain, requiredModulesChain } = queue.shift()!;
|
|
70
|
+
|
|
71
|
+
const currTitleChain = [...titleChain, item.title];
|
|
72
|
+
const currCodeChain = [...codeChain, item.code];
|
|
73
|
+
const currModulesChain = item.modules ? [...modulesChain, item.modules] : modulesChain;
|
|
74
|
+
const currRequiredModulesChain = item.requiredModules
|
|
75
|
+
? [...requiredModulesChain, item.requiredModules]
|
|
76
|
+
: requiredModulesChain;
|
|
77
|
+
|
|
78
|
+
if (!isUsableModulesChain(currModulesChain, currRequiredModulesChain, usableModules)) continue;
|
|
79
|
+
|
|
80
|
+
// 1. 자식 enqueue
|
|
81
|
+
if ("children" in item) {
|
|
82
|
+
for (const child of item.children) {
|
|
83
|
+
queue.push({
|
|
84
|
+
item: child,
|
|
85
|
+
titleChain: currTitleChain,
|
|
86
|
+
codeChain: currCodeChain,
|
|
87
|
+
modulesChain: currModulesChain,
|
|
88
|
+
requiredModulesChain: currRequiredModulesChain,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 1. 직접 perms 처리
|
|
94
|
+
if ("perms" in item) {
|
|
95
|
+
for (const perm of item.perms ?? []) {
|
|
96
|
+
results.push({
|
|
97
|
+
titleChain: currTitleChain,
|
|
98
|
+
codeChain: [...currCodeChain, perm],
|
|
99
|
+
modulesChain: currModulesChain,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 2. subPerms 처리
|
|
105
|
+
if ("subPerms" in item) {
|
|
106
|
+
for (const subPerm of item.subPerms ?? []) {
|
|
107
|
+
// subPerm도 모듈 체크
|
|
108
|
+
if (!isUsableModules(subPerm.modules, subPerm.requiredModules, usableModules)) continue;
|
|
109
|
+
|
|
110
|
+
for (const perm of subPerm.perms) {
|
|
111
|
+
results.push({
|
|
112
|
+
titleChain: currTitleChain,
|
|
113
|
+
codeChain: [...currCodeChain, subPerm.code, perm],
|
|
114
|
+
modulesChain: [...currModulesChain, subPerm.modules ?? []],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return results;
|
|
122
|
+
}
|
package/src/define-event.ts
CHANGED
|
@@ -14,13 +14,15 @@ export interface ServiceEventDef<TInfo = unknown, TData = unknown> {
|
|
|
14
14
|
* 타입 안전한 info와 data를 가진 서비스 이벤트를 정의한다.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
|
-
*
|
|
17
|
+
* // 서버에서 이벤트 정의 + 타입 export
|
|
18
|
+
* export const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
|
|
18
19
|
*
|
|
19
20
|
* // 서버에서 이벤트 발생
|
|
20
|
-
*
|
|
21
|
+
* await server.emitEvent<typeof OrderUpdated>("OrderUpdated", (info) => info.orderId === 123, { status: "shipped" });
|
|
21
22
|
*
|
|
22
|
-
* // 클라이언트에서 구독
|
|
23
|
-
*
|
|
23
|
+
* // 클라이언트에서 구독 (import type으로 타입만 가져옴)
|
|
24
|
+
* import type { OrderUpdated } from "@server-package";
|
|
25
|
+
* await client.addListener<typeof OrderUpdated>("OrderUpdated", { orderId: 123 }, async (data) => {
|
|
24
26
|
* console.log(data.status); // typed
|
|
25
27
|
* });
|
|
26
28
|
*/
|
package/src/index.ts
CHANGED
|
@@ -5,8 +5,13 @@ export * from "./protocol/create-service-protocol";
|
|
|
5
5
|
// 서비스 타입
|
|
6
6
|
export * from "./service-types/orm-service.types";
|
|
7
7
|
export * from "./service-types/auto-update-service.types";
|
|
8
|
+
export * from "./service-types/app-structure-service.types";
|
|
8
9
|
// 타입
|
|
9
10
|
export * from "./types";
|
|
10
11
|
|
|
12
|
+
// App Structure
|
|
13
|
+
export * from "./app-structure/app-structure.types";
|
|
14
|
+
export * from "./app-structure/app-structure.utils";
|
|
15
|
+
|
|
11
16
|
// 정의
|
|
12
17
|
export * from "./define-event";
|