@wipco/sdui 0.1.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.
@@ -0,0 +1,197 @@
1
+ /**
2
+ * SDUI(Server-Driven UI) 페이지 스펙 — 백엔드와의 렌더링 계약.
3
+ *
4
+ * 백엔드는 이 형태의 JSON을 내려주고, 프런트는 레지스트리에 등록된
5
+ * 컴포넌트로 조합/렌더링만 한다. (WIPCO ERP의 SDUI 계약과 동일 형식)
6
+ */
7
+ /** JSON으로 표현 가능한 prop 값. UI 노드는 props가 아니라 slots/children으로 전달한다. */
8
+ type SduiScalar = string | number | boolean | null;
9
+ type SduiPropValue = SduiScalar | SduiPropValue[] | {
10
+ [key: string]: SduiPropValue;
11
+ };
12
+ /** children/slot 항목: 문자열이면 텍스트, 객체면 하위 노드. */
13
+ type SduiChild = string | SduiNode;
14
+ interface SduiNode {
15
+ /** 레지스트리에 등록된 컴포넌트 이름 (예: "Button", "Grid") */
16
+ component: string;
17
+ /** React key. 형제간 고유 권장 (없으면 배열 인덱스 사용) */
18
+ key?: string;
19
+ /**
20
+ * 데이터 성격의 props.
21
+ * - `on`으로 시작하는 프롭에는 {@link SduiAction}(또는 액션 배열)을 넣으면 핸들러로 변환된다.
22
+ * - 값 프롭 자리에는 {@link SduiStateRef}를 넣어 페이지 상태를 바인딩할 수 있다.
23
+ */
24
+ props?: Record<string, SduiPropValue>;
25
+ /** ReactNode를 받는 프롭(Card.title, Card.actions, Dialog.footer 등)은 여기로 전달한다. */
26
+ slots?: Record<string, SduiChild[]>;
27
+ /** 기본 슬롯(children) */
28
+ children?: SduiChild[];
29
+ }
30
+ /**
31
+ * 페이지 상태 참조. props 값 자리에 넣으면 렌더러가 현재 상태 값으로 치환한다.
32
+ * 예: { "value": { "$state": "tab", "default": "all" } }
33
+ */
34
+ interface SduiStateRef {
35
+ $state: string;
36
+ default?: SduiPropValue;
37
+ }
38
+ /** ForEach 반복 스코프 안에서 현재 행을 참조한다. true면 행 전체, 문자열이면 해당 필드. */
39
+ interface SduiItemRef {
40
+ $item: string | true;
41
+ }
42
+ /** ForEach 반복 스코프 안에서 현재 인덱스(0부터)를 참조한다. */
43
+ interface SduiIndexRef {
44
+ $index: true;
45
+ }
46
+ /**
47
+ * 파생 계산값. args의 각 항목은 숫자/문자열이거나 다른 참조($state/$item/$calc)일 수 있다.
48
+ * 예(금액=수량×단가): { "$calc": { "op": "mul", "args": [{ "$item": "qty" }, { "$item": "price" }] } }
49
+ * sum은 args가 배열 하나면 그 배열 원소를 합산하고, field를 주면 객체 배열에서 해당 필드를 합산한다.
50
+ */
51
+ interface SduiCalcRef {
52
+ $calc: {
53
+ op: 'sum' | 'sub' | 'mul' | 'div' | 'percent' | 'count' | 'concat' | 'encode'
54
+ /** 비교·논리 — Show의 when, Button의 disabled 등에 바인딩한다 */
55
+ | 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'not';
56
+ args: SduiPropValue[];
57
+ /** sum/count에서 객체 배열의 필드를 지정 */
58
+ field?: string;
59
+ };
60
+ }
61
+ /** 폼 필드 검증 규칙. */
62
+ type SduiRule = {
63
+ rule: 'required';
64
+ message?: string;
65
+ } | {
66
+ rule: 'min';
67
+ value: number;
68
+ message?: string;
69
+ } | {
70
+ rule: 'max';
71
+ value: number;
72
+ message?: string;
73
+ } | {
74
+ rule: 'minLength';
75
+ value: number;
76
+ message?: string;
77
+ } | {
78
+ rule: 'maxLength';
79
+ value: number;
80
+ message?: string;
81
+ } | {
82
+ rule: 'pattern';
83
+ value: string;
84
+ message?: string;
85
+ } | {
86
+ rule: 'notBeforeField';
87
+ field: string;
88
+ message?: string;
89
+ } | {
90
+ rule: 'custom';
91
+ name: string;
92
+ message?: string;
93
+ };
94
+ /**
95
+ * on* 프롭 자리에 넣는 선언적 액션. 배열이면 순서대로 실행된다.
96
+ */
97
+ type SduiAction = {
98
+ action: 'navigate';
99
+ to: string;
100
+ } | {
101
+ action: 'setState';
102
+ key: string;
103
+ value?: SduiPropValue;
104
+ /** true면 이벤트 값(input value, 선택된 탭 value, 페이지 번호 등)을 그대로 저장한다. */
105
+ fromEvent?: boolean;
106
+ }
107
+ /** setState(key, true) 단축 — Dialog 등 열기 */
108
+ | {
109
+ action: 'open';
110
+ key: string;
111
+ }
112
+ /** setState(key, false) 단축 — 닫기 */
113
+ | {
114
+ action: 'close';
115
+ key: string;
116
+ } | {
117
+ action: 'toast';
118
+ tone?: 'info' | 'success' | 'warning' | 'danger';
119
+ title?: string;
120
+ message?: string;
121
+ }
122
+ /**
123
+ * 폼 제출: Form(id)의 상태 슬라이스를 수집 → 규칙 검증 → 통과 시
124
+ * 어댑터 fetch로 endpoint에 전송. 실패 시 필드별 에러를
125
+ * `{formId}.$errors`에, 진행 상태를 `{formId}.$submitting`에 기록한다.
126
+ */
127
+ | {
128
+ action: 'submitForm';
129
+ form: string;
130
+ endpoint: string;
131
+ method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';
132
+ /** 성공 시 순차 실행 (이벤트 값 = 응답 JSON) */
133
+ onSuccess?: SduiAction[];
134
+ /** 실패 시 순차 실행 (이벤트 값 = 에러 메시지). 생략하면 danger 토스트 */
135
+ onError?: SduiAction[];
136
+ }
137
+ /** 클라이언트 조회 캐시 무효화 — 어댑터의 invalidate로 위임 */
138
+ | {
139
+ action: 'invalidate';
140
+ prefix: string;
141
+ exact?: boolean;
142
+ }
143
+ /** 페이지 스펙 재조회 — 어댑터의 refresh로 위임 */
144
+ | {
145
+ action: 'refresh';
146
+ }
147
+ /** 파일 다운로드 — blob 저장 */
148
+ | {
149
+ action: 'download';
150
+ endpoint: string;
151
+ filename?: string;
152
+ }
153
+ /** 브라우저 인쇄 다이얼로그 */
154
+ | {
155
+ action: 'print';
156
+ }
157
+ /** 배열 상태 조작 — ForEach와 함께 반복 행 편집에 쓴다 */
158
+ | {
159
+ action: 'arrayAppend';
160
+ key: string;
161
+ value?: SduiPropValue;
162
+ } | {
163
+ action: 'arrayRemove';
164
+ key: string;
165
+ index: number | SduiIndexRef;
166
+ } | {
167
+ action: 'arrayMove';
168
+ key: string;
169
+ from: number | SduiIndexRef;
170
+ to: number | SduiIndexRef;
171
+ } | {
172
+ action: 'arrayUpdate';
173
+ key: string;
174
+ index: number | SduiIndexRef;
175
+ field: string;
176
+ value?: SduiPropValue;
177
+ fromEvent?: boolean;
178
+ }
179
+ /** 앱 코드에 위임하는 확장 지점 — SduiRenderer의 onCustomAction으로 전달된다. */
180
+ | {
181
+ action: 'custom';
182
+ name: string;
183
+ payload?: SduiPropValue;
184
+ };
185
+ interface SduiPage {
186
+ version: 1;
187
+ /** 문서 <title> 및 페이지 헤더에 쓰인다. */
188
+ title?: string;
189
+ subtitle?: string;
190
+ /** 실시간 채널 구독 — 어댑터의 subscribe로 위임(이벤트 수신 시 조용한 재조회) */
191
+ subscribe?: string[];
192
+ /** URL 쿼리파라미터와 동기화할 상태 키 — 딥링크/새로고침 보존용 */
193
+ urlState?: string[];
194
+ body: SduiChild[];
195
+ }
196
+
197
+ export type { SduiAction as S, SduiPropValue as a, SduiRule as b, SduiPage as c, SduiCalcRef as d, SduiChild as e, SduiIndexRef as f, SduiItemRef as g, SduiNode as h, SduiScalar as i, SduiStateRef as j };
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@wipco/sdui",
3
+ "version": "0.1.0",
4
+ "description": "WIPCO 서버 주도 UI(SDUI) 렌더러 — JSON 페이지 스펙을 React 컴포넌트로 렌더링",
5
+ "keywords": [
6
+ "sdui",
7
+ "server-driven-ui",
8
+ "react",
9
+ "renderer"
10
+ ],
11
+ "author": "wipco-dev",
12
+ "license": "UNLICENSED",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/wipco-dev/wipco-sdui.git",
16
+ "directory": "packages/sdui"
17
+ },
18
+ "homepage": "https://github.com/wipco-dev/wipco-sdui#readme",
19
+ "bugs": {
20
+ "url": "https://github.com/wipco-dev/wipco-sdui/issues"
21
+ },
22
+ "type": "module",
23
+ "main": "./dist/index.js",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ },
31
+ "./rich": {
32
+ "types": "./dist/rich.d.ts",
33
+ "default": "./dist/rich.js"
34
+ },
35
+ "./meta": {
36
+ "types": "./dist/meta.d.ts",
37
+ "default": "./dist/meta.js"
38
+ },
39
+ "./styles.css": "./dist/styles.css",
40
+ "./package.json": "./package.json"
41
+ },
42
+ "sideEffects": [
43
+ "**/*.css"
44
+ ],
45
+ "files": [
46
+ "dist"
47
+ ],
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "registry": "https://registry.npmjs.org/"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup && cp src/styles.css dist/styles.css",
54
+ "dev": "tsup --watch",
55
+ "typecheck": "tsc --noEmit",
56
+ "check:meta": "node scripts/check-meta.mjs",
57
+ "prepack": "pnpm run build"
58
+ },
59
+ "peerDependencies": {
60
+ "ol": ">=9",
61
+ "react": ">=18",
62
+ "react-dom": ">=18",
63
+ "recharts": ">=3"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "ol": {
67
+ "optional": true
68
+ },
69
+ "recharts": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "devDependencies": {
74
+ "@types/react": "^19.0.0",
75
+ "@types/react-dom": "^19.0.0",
76
+ "tsup": "^8.5.0",
77
+ "typescript": "^5.7.0"
78
+ }
79
+ }