@fuzionx/core 0.1.38 → 0.1.41

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/lib/context.js CHANGED
@@ -1,225 +1,229 @@
1
- /**
2
- * Context — Fusion raw req 객체를 Express 스타일 req/res로 변환.
3
- *
4
- * 최적화:
5
- * - 프로토타입 기반 + 재사용 가능한 응답 객체
6
- * - Object.defineProperty 제거 → 함수 기반 lazy JSON
7
- * - 요청당 클로저 최소화
8
- * - _toFusionResponse()에서 할당 0
9
- */
10
-
11
- // ── req.session 프로토타입 ──
12
- const SessionProto = {
13
- get(key) {
14
- if (key) return this._data[key] || null;
15
- return { ...this._data };
16
- },
17
- set(key, value) {
18
- this._data[key] = String(value);
19
- if (this._id) {
20
- this._bridge.sessionSet(this._id, { ...this._data });
21
- }
22
- },
23
- destroy() {
24
- if (this._id) {
25
- this._bridge.sessionDestroy(this._id);
26
- this._data = {};
27
- }
28
- },
29
- renew() {
30
- if (this._id) {
31
- return this._bridge.sessionRenew(this._id);
32
- }
33
- return null;
34
- },
35
- };
36
-
37
- // ── req 프로토타입 ──
38
- const ReqProto = {
39
- /** lazy JSON 파싱 — body가 이미 object면 그대로 반환 */
40
- get json() {
41
- if (this._json === undefined) {
42
- if (typeof this.body === 'object' && this.body !== null) {
43
- this._json = this.body;
44
- } else {
45
- try {
46
- this._json = this.body ? JSON.parse(this.body) : null;
47
- } catch (_) {
48
- this._json = null;
49
- }
50
- }
51
- }
52
- return this._json;
53
- },
54
- /** i18n 번역 */
55
- t(key, defaultValue) {
56
- const app = this._app;
57
- if (!app.i18n) return key;
58
- const locale = this.headers?.['accept-language']?.split(',')[0]?.split('-')[0] || 'en';
59
- const result = app.i18n.translate(locale, key);
60
- if (result == null && defaultValue != null) {
61
- app.i18n.updateMissing(key, defaultValue);
62
- return defaultValue;
63
- }
64
- return result || defaultValue || key;
65
- },
66
- };
67
-
68
- // ── 사전 할당된 session 인스턴스 (재사용) ──
69
- const _sharedSession = Object.create(SessionProto);
70
- _sharedSession._id = null;
71
- _sharedSession._data = {};
72
- _sharedSession._bridge = null;
73
-
74
- // ── 사전 할당된 req/res 인스턴스 (단일 스레드 재사용) ──
75
- const _sharedReq = Object.create(ReqProto);
76
- _sharedReq.method = '';
77
- _sharedReq.url = '';
78
- _sharedReq.path = '';
79
- _sharedReq.query = null;
80
- _sharedReq.params = null;
81
- _sharedReq.headers = null;
82
- _sharedReq.ip = '';
83
- _sharedReq.body = '';
84
- _sharedReq.handlerId = 0;
85
- _sharedReq.requestId = 0;
86
- _sharedReq.sessionId = null;
87
- _sharedReq.files = null;
88
- _sharedReq._app = null;
89
- _sharedReq._json = undefined;
90
- _sharedReq.session = _sharedSession;
91
-
92
- /**
93
- * Request 래퍼 생성 — 프로토타입 기반 + 재사용
94
- * @param {object} rawReq - Fusion 콜백의 raw 요청 객체
95
- * @param {object} app - FuzionXApp 인스턴스
96
- * @param {boolean} [reusable=true] - true면 공유 인스턴스 재사용 (sync용), false면 새로 생성 (async용)
97
- */
98
- export function createReq(rawReq, app, reusable = true) {
99
- let req;
100
- if (reusable) {
101
- // sync 핸들러: 공유 인스턴스 재사용 (할당 0)
102
- req = _sharedReq;
103
- } else {
104
- // async 핸들러: 새 인스턴스 (응답 전까지 유지 필요)
105
- req = Object.create(ReqProto);
106
- }
107
-
108
- req.method = rawReq.method;
109
- req.url = rawReq.url;
110
- req.path = rawReq.path;
111
- req.query = rawReq.query || null;
112
- req.params = rawReq.params || null;
113
- req.headers = rawReq.headers || null;
114
- req.ip = rawReq.remoteIp || '';
115
- req.body = rawReq.body || '';
116
- req.handlerId = rawReq.handlerId;
117
- req.requestId = rawReq.requestId;
118
- req.sessionId = rawReq.sessionId || null;
119
- req._app = app;
120
- req._json = undefined;
121
- req.files = rawReq.files || null;
122
- req.formFields = rawReq.formFields || null;
123
-
124
- // session 업데이트 (재사용)
125
- if (reusable) {
126
- _sharedSession._id = rawReq.sessionId || null;
127
- _sharedSession._data = rawReq.session || {};
128
- _sharedSession._bridge = app._bridge;
129
- } else {
130
- const session = Object.create(SessionProto);
131
- session._id = rawReq.sessionId || null;
132
- session._data = rawReq.session || {};
133
- session._bridge = app._bridge;
134
- req.session = session;
135
- }
136
-
137
- return req;
138
- }
139
-
140
- // ── Response 프로토타입 ──
141
- const ResProto = {
142
- status(code) {
143
- this._statusCode = code;
144
- return this;
145
- },
146
- json(data) {
147
- this._body = JSON.stringify(data);
148
- this._headers['Content-Type'] = 'application/json';
149
- this._sent = true;
150
- return this;
151
- },
152
- send(text) {
153
- this._body = String(text);
154
- this._sent = true;
155
- return this;
156
- },
157
- html(content) {
158
- this._body = String(content);
159
- this._headers['Content-Type'] = 'text/html; charset=utf-8';
160
- this._sent = true;
161
- return this;
162
- },
163
- redirect(url, code = 302) {
164
- this._statusCode = code;
165
- this._headers['Location'] = url;
166
- this._body = '';
167
- this._sent = true;
168
- return this;
169
- },
170
- end() {
171
- this._body = '';
172
- this._sent = true;
173
- return this;
174
- },
175
- header(key, value) {
176
- this._headers[key] = value;
177
- return this;
178
- },
179
- /** Fusion 응답 포맷으로 변환 — 공유 응답 객체 재사용 */
180
- _toFusionResponse() {
181
- _sharedResponse.status = this._statusCode;
182
- _sharedResponse.body = this._body;
183
- _sharedResponse.headers = this._headers;
184
- return _sharedResponse;
185
- },
186
- };
187
-
188
- // ── 사전 할당된 응답 객체 (재사용) ──
189
- const _sharedResponse = { status: 200, body: '', headers: null };
190
- const _sharedHeaders = {};
191
-
192
- // ── 사전 할당된 res 인스턴스 ──
193
- const _sharedRes = Object.create(ResProto);
194
- _sharedRes._statusCode = 200;
195
- _sharedRes._body = '';
196
- _sharedRes._headers = _sharedHeaders;
197
- _sharedRes._sent = false;
198
-
199
- /**
200
- * Response 래퍼 생성 — 프로토타입 기반 + 재사용
201
- * @param {boolean} [reusable=true] - true면 공유 인스턴스 재사용, false면 새로 생성
202
- */
203
- export function createRes(reusable = true) {
204
- if (reusable) {
205
- // sync: 공유 인스턴스 리셋 재사용 (할당 0)
206
- _sharedRes._statusCode = 200;
207
- _sharedRes._body = '';
208
- // headers 객체 재사용 — 이전 키 제거
209
- const keys = Object.keys(_sharedHeaders);
210
- for (let i = 0; i < keys.length; i++) {
211
- delete _sharedHeaders[keys[i]];
212
- }
213
- _sharedRes._headers = _sharedHeaders;
214
- _sharedRes._sent = false;
215
- return _sharedRes;
216
- }
217
-
218
- // async: 새 인스턴스
219
- const res = Object.create(ResProto);
220
- res._statusCode = 200;
221
- res._body = '';
222
- res._headers = {};
223
- res._sent = false;
224
- return res;
225
- }
1
+ /**
2
+ * Context — Fusion raw req 객체를 Express 스타일 req/res로 변환.
3
+ *
4
+ * 최적화:
5
+ * - 프로토타입 기반 + 재사용 가능한 응답 객체
6
+ * - Object.defineProperty 제거 → 함수 기반 lazy JSON
7
+ * - 요청당 클로저 최소화
8
+ * - _toFusionResponse()에서 할당 0
9
+ */
10
+
11
+ // ── req.session 프로토타입 ──
12
+ const SessionProto = {
13
+ get(key) {
14
+ if (key) return this._data[key] || null;
15
+ return { ...this._data };
16
+ },
17
+ set(key, value) {
18
+ this._data[key] = String(value);
19
+ if (this._id) {
20
+ this._bridge.sessionSet(this._id, { ...this._data });
21
+ }
22
+ },
23
+ destroy() {
24
+ if (this._id) {
25
+ this._bridge.sessionDestroy(this._id);
26
+ this._data = {};
27
+ }
28
+ },
29
+ renew() {
30
+ if (this._id) {
31
+ return this._bridge.sessionRenew(this._id);
32
+ }
33
+ return null;
34
+ },
35
+ };
36
+
37
+ // ── req 프로토타입 ──
38
+ const ReqProto = {
39
+ /** lazy JSON 파싱 — body가 이미 object면 그대로 반환 */
40
+ get json() {
41
+ if (this._json === undefined) {
42
+ if (typeof this.body === 'object' && this.body !== null) {
43
+ this._json = this.body;
44
+ } else {
45
+ try {
46
+ this._json = this.body ? JSON.parse(this.body) : null;
47
+ } catch (_) {
48
+ this._json = null;
49
+ }
50
+ }
51
+ }
52
+ return this._json;
53
+ },
54
+ /** i18n 번역 */
55
+ t(key, defaultValue) {
56
+ const app = this._app;
57
+ if (!app.i18n) return key;
58
+ const locale = this.headers?.['accept-language']?.split(',')[0]?.split('-')[0] || 'en';
59
+ const result = app.i18n.translate(locale, key);
60
+ if (result == null && defaultValue != null) {
61
+ app.i18n.updateMissing(key, defaultValue);
62
+ return defaultValue;
63
+ }
64
+ return result || defaultValue || key;
65
+ },
66
+ };
67
+
68
+ // ── 사전 할당된 session 인스턴스 (재사용) ──
69
+ const _sharedSession = Object.create(SessionProto);
70
+ _sharedSession._id = null;
71
+ _sharedSession._data = {};
72
+ _sharedSession._bridge = null;
73
+
74
+ // ── 사전 할당된 req/res 인스턴스 (단일 스레드 재사용) ──
75
+ const _sharedReq = Object.create(ReqProto);
76
+ _sharedReq.method = '';
77
+ _sharedReq.url = '';
78
+ _sharedReq.path = '';
79
+ _sharedReq.query = null;
80
+ _sharedReq.params = null;
81
+ _sharedReq.headers = null;
82
+ _sharedReq.ip = '';
83
+ _sharedReq.body = '';
84
+ _sharedReq.handlerId = 0;
85
+ _sharedReq.requestId = 0;
86
+ _sharedReq.sessionId = null;
87
+ _sharedReq.files = null;
88
+ _sharedReq._app = null;
89
+ _sharedReq._json = undefined;
90
+ _sharedReq.session = _sharedSession;
91
+
92
+ /**
93
+ * Request 래퍼 생성 — 프로토타입 기반 + 재사용
94
+ * @param {object} rawReq - Fusion 콜백의 raw 요청 객체
95
+ * @param {object} app - FuzionXApp 인스턴스
96
+ * @param {boolean} [reusable=true] - true면 공유 인스턴스 재사용 (sync용), false면 새로 생성 (async용)
97
+ */
98
+ export function createReq(rawReq, app, reusable = true) {
99
+ let req;
100
+ if (reusable) {
101
+ // sync 핸들러: 공유 인스턴스 재사용 (할당 0)
102
+ req = _sharedReq;
103
+ } else {
104
+ // async 핸들러: 새 인스턴스 (응답 전까지 유지 필요)
105
+ req = Object.create(ReqProto);
106
+ }
107
+
108
+ req.method = rawReq.method;
109
+ req.url = rawReq.url;
110
+ req.path = rawReq.path;
111
+ req.query = rawReq.query || null;
112
+ req.params = rawReq.params || null;
113
+ req.headers = rawReq.headers || null;
114
+ req.ip = rawReq.remoteIp || '';
115
+ req.body = rawReq.body || '';
116
+ req.handlerId = rawReq.handlerId;
117
+ req.requestId = rawReq.requestId;
118
+ req.sessionId = rawReq.sessionId || null;
119
+ req._app = app;
120
+ req._json = undefined;
121
+ req.files = rawReq.files || null;
122
+ req.formFields = rawReq.formFields || null;
123
+
124
+ // session 업데이트 (재사용)
125
+ if (reusable) {
126
+ _sharedSession._id = rawReq.sessionId || null;
127
+ _sharedSession._data = rawReq.session || {};
128
+ _sharedSession._bridge = app._bridge;
129
+ } else {
130
+ const session = Object.create(SessionProto);
131
+ session._id = rawReq.sessionId || null;
132
+ session._data = rawReq.session || {};
133
+ session._bridge = app._bridge;
134
+ req.session = session;
135
+ }
136
+
137
+ return req;
138
+ }
139
+
140
+ // ── Response 프로토타입 ──
141
+ const ResProto = {
142
+ status(code) {
143
+ this._statusCode = code;
144
+ return this;
145
+ },
146
+ json(data) {
147
+ this._body = JSON.stringify(data);
148
+ this._headers['Content-Type'] = 'application/json';
149
+ this._sent = true;
150
+ return this;
151
+ },
152
+ send(text) {
153
+ this._body = String(text);
154
+ this._sent = true;
155
+ return this;
156
+ },
157
+ html(content) {
158
+ this._body = String(content);
159
+ this._headers['Content-Type'] = 'text/html; charset=utf-8';
160
+ this._sent = true;
161
+ return this;
162
+ },
163
+ redirect(url, code = 302) {
164
+ this._statusCode = code;
165
+ this._headers['Location'] = url;
166
+ this._body = '';
167
+ this._sent = true;
168
+ return this;
169
+ },
170
+ end() {
171
+ this._body = '';
172
+ this._sent = true;
173
+ return this;
174
+ },
175
+ header(key, value) {
176
+ this._headers[key] = value;
177
+ return this;
178
+ },
179
+ /** Fusion 응답 포맷으로 변환 — sync: 공유 객체, async: 새 객체 */
180
+ _toFusionResponse() {
181
+ // async (비공유) 인스턴스면 새 객체 반환 → race condition 방지
182
+ if (this !== _sharedRes) {
183
+ return { status: this._statusCode, body: this._body, headers: this._headers };
184
+ }
185
+ _sharedResponse.status = this._statusCode;
186
+ _sharedResponse.body = this._body;
187
+ _sharedResponse.headers = this._headers;
188
+ return _sharedResponse;
189
+ },
190
+ };
191
+
192
+ // ── 사전 할당된 응답 객체 (재사용) ──
193
+ const _sharedResponse = { status: 200, body: '', headers: null };
194
+ const _sharedHeaders = {};
195
+
196
+ // ── 사전 할당된 res 인스턴스 ──
197
+ const _sharedRes = Object.create(ResProto);
198
+ _sharedRes._statusCode = 200;
199
+ _sharedRes._body = '';
200
+ _sharedRes._headers = _sharedHeaders;
201
+ _sharedRes._sent = false;
202
+
203
+ /**
204
+ * Response 래퍼 생성 — 프로토타입 기반 + 재사용
205
+ * @param {boolean} [reusable=true] - true면 공유 인스턴스 재사용, false면 새로 생성
206
+ */
207
+ export function createRes(reusable = true) {
208
+ if (reusable) {
209
+ // sync: 공유 인스턴스 리셋 후 재사용 (할당 0)
210
+ _sharedRes._statusCode = 200;
211
+ _sharedRes._body = '';
212
+ // headers 객체 재사용 — 이전 키 제거
213
+ const keys = Object.keys(_sharedHeaders);
214
+ for (let i = 0; i < keys.length; i++) {
215
+ delete _sharedHeaders[keys[i]];
216
+ }
217
+ _sharedRes._headers = _sharedHeaders;
218
+ _sharedRes._sent = false;
219
+ return _sharedRes;
220
+ }
221
+
222
+ // async: 새 인스턴스
223
+ const res = Object.create(ResProto);
224
+ res._statusCode = 200;
225
+ res._body = '';
226
+ res._headers = {};
227
+ res._sent = false;
228
+ return res;
229
+ }
package/lib/crypto.js CHANGED
@@ -1,23 +1,23 @@
1
- /**
2
- * Crypto — fuzionx-bridge ASP crypto N-API를 래핑하는 헬퍼.
3
- * app.crypto로 접근.
4
- */
5
-
6
- export function createCrypto(bridge) {
7
- return {
8
- /** UUID v4 생성 */
9
- uuid: () => bridge.cryptoUuid(),
10
-
11
- /** MD5 해시 */
12
- md5: (input) => bridge.cryptoMd5(input),
13
-
14
- /** SHA-256 해시 */
15
- sha256: (input) => bridge.cryptoSha256(input),
16
-
17
- /** AES-256-GCM 암호화 */
18
- encrypt: (key, plaintext) => bridge.cryptoEncryptAes(key, plaintext),
19
-
20
- /** AES-256-GCM 복호화 */
21
- decrypt: (key, ciphertext) => bridge.cryptoDecryptAes(key, ciphertext),
22
- };
23
- }
1
+ /**
2
+ * Crypto — fuzionx-bridge ASP crypto N-API를 래핑하는 헬퍼.
3
+ * app.crypto로 접근.
4
+ */
5
+
6
+ export function createCrypto(bridge) {
7
+ return {
8
+ /** UUID v4 생성 */
9
+ uuid: () => bridge.cryptoUuid(),
10
+
11
+ /** MD5 해시 */
12
+ md5: (input) => bridge.cryptoMd5(input),
13
+
14
+ /** SHA-256 해시 */
15
+ sha256: (input) => bridge.cryptoSha256(input),
16
+
17
+ /** AES-256-GCM 암호화 */
18
+ encrypt: (key, plaintext) => bridge.cryptoEncryptAes(key, plaintext),
19
+
20
+ /** AES-256-GCM 복호화 */
21
+ decrypt: (key, ciphertext) => bridge.cryptoDecryptAes(key, ciphertext),
22
+ };
23
+ }
package/lib/file.js CHANGED
@@ -1,34 +1,34 @@
1
- /**
2
- * FileHelper — fuzionx-bridge 파일 유틸리티 N-API 래핑.
3
- * app.file로 접근.
4
- *
5
- * 최적화: 모든 호출은 Rust N-API 직통, JS 오버헤드 없음.
6
- */
7
-
8
- export function createFile(bridge) {
9
- return {
10
- /** 파일 이동 (cross-device 자동 대응) */
11
- move: (src, dst) => bridge.fileMoveFile(src, dst),
12
-
13
- /** 파일 복사 — 복사된 바이트 수 반환 */
14
- copy: (src, dst) => bridge.fileCopyFile(src, dst),
15
-
16
- /** 디렉토리 재귀 생성 */
17
- ensureDir: (dirPath) => bridge.fileEnsureDir(dirPath),
18
-
19
- /** 파일 크기 (bytes) */
20
- size: (filePath) => bridge.fileSize(filePath),
21
-
22
- /** 파일 존재 여부 */
23
- exists: (filePath) => bridge.fileExists(filePath),
24
-
25
- /** 파일 삭제 */
26
- remove: (filePath) => bridge.fileRemove(filePath),
27
-
28
- /** 임시 파일 경로 생성 (UUID 기반) */
29
- tempPath: (prefix) => bridge.fileTempPath(prefix || 'fuzionx'),
30
-
31
- /** 확장자 추출 (소문자, dot 제외) — null 가능 */
32
- extension: (filePath) => bridge.fileExtension(filePath),
33
- };
34
- }
1
+ /**
2
+ * FileHelper — fuzionx-bridge 파일 유틸리티 N-API 래핑.
3
+ * app.file로 접근.
4
+ *
5
+ * 최적화: 모든 호출은 Rust N-API 직통, JS 오버헤드 없음.
6
+ */
7
+
8
+ export function createFile(bridge) {
9
+ return {
10
+ /** 파일 이동 (cross-device 자동 대응) */
11
+ move: (src, dst) => bridge.fileMoveFile(src, dst),
12
+
13
+ /** 파일 복사 — 복사된 바이트 수 반환 */
14
+ copy: (src, dst) => bridge.fileCopyFile(src, dst),
15
+
16
+ /** 디렉토리 재귀 생성 */
17
+ ensureDir: (dirPath) => bridge.fileEnsureDir(dirPath),
18
+
19
+ /** 파일 크기 (bytes) */
20
+ size: (filePath) => bridge.fileSize(filePath),
21
+
22
+ /** 파일 존재 여부 */
23
+ exists: (filePath) => bridge.fileExists(filePath),
24
+
25
+ /** 파일 삭제 */
26
+ remove: (filePath) => bridge.fileRemove(filePath),
27
+
28
+ /** 임시 파일 경로 생성 (UUID 기반) */
29
+ tempPath: (prefix) => bridge.fileTempPath(prefix || 'fuzionx'),
30
+
31
+ /** 확장자 추출 (소문자, dot 제외) — null 가능 */
32
+ extension: (filePath) => bridge.fileExtension(filePath),
33
+ };
34
+ }
package/lib/hash.js CHANGED
@@ -1,22 +1,22 @@
1
- /**
2
- * HashHelper — fuzionx-bridge 해싱 유틸리티 N-API 래핑.
3
- * app.hash로 접근.
4
- *
5
- * 최적화: Rust bcrypt/argon2 직통 호출, JS crypto 미사용.
6
- */
7
-
8
- export function createHash(bridge) {
9
- return {
10
- /** bcrypt 해시 생성 (cost 기본 12) */
11
- bcrypt: (password, cost) => bridge.hashBcrypt(password, cost),
12
-
13
- /** bcrypt 해시 검증 */
14
- bcryptVerify: (password, hash) => bridge.hashBcryptVerify(password, hash),
15
-
16
- /** argon2id 해시 생성 (랜덤 salt) */
17
- argon2: (password) => bridge.hashArgon2(password),
18
-
19
- /** argon2 해시 검증 */
20
- argon2Verify: (password, hash) => bridge.hashArgon2Verify(password, hash),
21
- };
22
- }
1
+ /**
2
+ * HashHelper — fuzionx-bridge 해싱 유틸리티 N-API 래핑.
3
+ * app.hash로 접근.
4
+ *
5
+ * 최적화: Rust bcrypt/argon2 직통 호출, JS crypto 미사용.
6
+ */
7
+
8
+ export function createHash(bridge) {
9
+ return {
10
+ /** bcrypt 해시 생성 (cost 기본 12) */
11
+ bcrypt: (password, cost) => bridge.hashBcrypt(password, cost),
12
+
13
+ /** bcrypt 해시 검증 */
14
+ bcryptVerify: (password, hash) => bridge.hashBcryptVerify(password, hash),
15
+
16
+ /** argon2id 해시 생성 (랜덤 salt) */
17
+ argon2: (password) => bridge.hashArgon2(password),
18
+
19
+ /** argon2 해시 검증 */
20
+ argon2Verify: (password, hash) => bridge.hashArgon2Verify(password, hash),
21
+ };
22
+ }