@simplysm/service-common 14.0.25 → 14.0.27

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.
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=app-structure.types.js.map
@@ -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/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
@@ -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;AAE1D,cAAc,SAAS,CAAC;AAGxB,cAAc,gBAAgB,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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=app-structure-service.types.js.map
@@ -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.25",
3
+ "version": "14.0.27",
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",
25
- "@simplysm/orm-common": "14.0.25"
24
+ "@simplysm/core-common": "14.0.27",
25
+ "@simplysm/orm-common": "14.0.27"
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/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";
@@ -0,0 +1,10 @@
1
+ import type { AppStructureItem } from "../app-structure/app-structure.types";
2
+
3
+ /**
4
+ * AppStructure 서비스 인터페이스
5
+ *
6
+ * 서버에 등록된 앱 구조 항목을 클라이언트명 기준 맵으로 조회한다.
7
+ */
8
+ export interface AppStructureService {
9
+ getItems(): Record<string, AppStructureItem[]>;
10
+ }