@alloy-framework/core 0.4.0 β†’ 0.14.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.
@@ -1,265 +0,0 @@
1
- import native from '@alloy-framework/rust';
2
- import { AlloyModel } from './AlloyModel.js';
3
- import { validateForSave, validateForUpdate, applyMongoDefaults } from './validationHelper.js';
4
- import { AlloyError } from '../util/AlloyError.js';
5
-
6
- /**
7
- * πŸƒ Alloy MongoDB μ „μš© λͺ¨λΈ
8
- * MongoDB의 find, insertOne, updateOne, aggregate λ“± 직관적 APIλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
9
- * λ‚΄λΆ€μ μœΌλ‘œ Rust의 mongoCommand (run_command)λ₯Ό λž˜ν•‘ν•©λ‹ˆλ‹€.
10
- *
11
- * @example
12
- * class ActivityLogs extends AlloyMongoModel {
13
- * constructor() { super('activity_logs'); }
14
- * }
15
- *
16
- * const logs = await new ActivityLogs().find({ domain: 'AUTH' }, { sort: { _id: -1 }, limit: 50 });
17
- */
18
- export class AlloyMongoModel extends AlloyModel {
19
- /**
20
- * @param {string} collection - MongoDB μ»¬λ ‰μ…˜ 이름
21
- * @param {string} [store='mongo_main'] - alloy.conf에 μ •μ˜λœ MongoDB μŠ€ν† μ–΄ 이름
22
- */
23
- constructor(collection, store = 'mongo_main') {
24
- super(collection, store); // AlloyModel μƒμ„±μž: table, store, _native μ΄ˆκΈ°ν™”
25
- /** @type {string} μ»¬λ ‰μ…˜ 이름 (MongoDB μ „μš© 별칭) */
26
- this.collection = collection;
27
- }
28
-
29
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
30
- // πŸ“– 쑰회 (Read)
31
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
32
-
33
- /**
34
- * λ¬Έμ„œ λ°°μ—΄ 쑰회
35
- * @param {object} [filter={}] - 검색 쑰건
36
- * @param {object} [options={}] - 쑰회 μ˜΅μ…˜ (sort, limit, skip, projection)
37
- * @returns {Promise<object[]>} λ¬Έμ„œ λ°°μ—΄
38
- */
39
- async find(filter = {}, options = {}) {
40
- const cmd = { find: this.collection, filter };
41
- if (options.sort) cmd.sort = options.sort;
42
- if (options.limit != null) cmd.limit = options.limit;
43
- if (options.skip != null) cmd.skip = options.skip;
44
- if (options.projection) cmd.projection = options.projection;
45
-
46
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
47
- return result?.cursor?.firstBatch || [];
48
- }
49
-
50
- /**
51
- * 단일 λ¬Έμ„œ 쑰회
52
- * @param {object} [filter={}] - 검색 쑰건
53
- * @param {object} [options={}] - 쑰회 μ˜΅μ…˜
54
- * @returns {Promise<object|null>} λ¬Έμ„œ λ˜λŠ” null
55
- */
56
- async findOne(filter = {}, options = {}) {
57
- const docs = await this.find(filter, { ...options, limit: 1 });
58
- return docs[0] || null;
59
- }
60
-
61
- /**
62
- * λ¬Έμ„œ 수 쑰회
63
- * - filter μΈμžκ°€ 있으면 mongoCommand 직접 μ‹€ν–‰
64
- * - filter 없이 체이닝(.where().count()) μ‹œ λΆ€λͺ¨ AlloyModel.count() μœ„μž„
65
- * @param {object} [filter] - 검색 쑰건 (μ—†μœΌλ©΄ 체이닝 쑰건 μ‚¬μš©)
66
- * @returns {Promise<number>}
67
- */
68
- async count(filter) {
69
- // λͺ…μ‹œμ  filterκ°€ 있으면 mongoCommand 직접 μ‹€ν–‰
70
- if (filter && Object.keys(filter).length > 0) {
71
- const result = await this._native.mongoCommand(
72
- JSON.stringify({ count: this.collection, query: filter }),
73
- );
74
- return result?.n || 0;
75
- }
76
- // 체이닝 where 쑰건 μ‚¬μš© β†’ λΆ€λͺ¨ AlloyModel.count() (aggregate $sum:1)
77
- return super.count();
78
- }
79
-
80
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
81
- // ✏️ μ‚½μž… (Create)
82
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
83
-
84
- /**
85
- * 단일 λ¬Έμ„œ μ‚½μž…
86
- * @param {object} doc - μ‚½μž…ν•  λ¬Έμ„œ
87
- * @returns {Promise<object>} { ok, n }
88
- */
89
- async insertOne(doc) {
90
- const schema = this.constructor.schema;
91
- doc = await validateForSave(schema, doc);
92
- applyMongoDefaults(schema, doc);
93
-
94
- const cmd = { insert: this.collection, documents: [doc], ordered: true };
95
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
96
- this._assertOk(result, 'insertOne');
97
- return { ok: true, n: result?.n || 1 };
98
- }
99
-
100
- /**
101
- * 닀쀑 λ¬Έμ„œ μ‚½μž…
102
- * @param {object[]} docs - μ‚½μž…ν•  λ¬Έμ„œ λ°°μ—΄
103
- * @param {object} [options={}] - μ˜΅μ…˜ { ordered: true }
104
- * @returns {Promise<object>} { ok, n }
105
- */
106
- async insertMany(docs, options = {}) {
107
- const schema = this.constructor.schema;
108
-
109
- // 각 λ¬Έμ„œμ— GooseValidator 검증 + κΈ°λ³Έκ°’ μ£Όμž…
110
- for (const doc of docs) {
111
- await validateForSave(schema, doc);
112
- applyMongoDefaults(schema, doc);
113
- }
114
-
115
- const cmd = {
116
- insert: this.collection,
117
- documents: docs,
118
- ordered: options.ordered !== false,
119
- };
120
-
121
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
122
- this._assertOk(result, 'insertMany');
123
- return { ok: true, n: result?.n || docs.length };
124
- }
125
-
126
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
127
- // πŸ”„ μˆ˜μ • (Update)
128
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
129
-
130
- /**
131
- * 단일 λ¬Έμ„œ μˆ˜μ •
132
- * @param {object} filter - 검색 쑰건
133
- * @param {object} update - μˆ˜μ • λ‚΄μš© (예: { $set: { name: 'new' } })
134
- * @param {object} [options={}] - μ˜΅μ…˜ { upsert, arrayFilters, hint, collation }
135
- * @returns {Promise<object>} { ok, nModified, upserted }
136
- */
137
- async updateOne(filter, update, options = {}) {
138
- // $set 데이터에 λŒ€ν•΄ μŠ€ν‚€λ§ˆ 검증 적용
139
- if (update?.$set) {
140
- const schema = this.constructor.schema;
141
- update.$set = await validateForUpdate(schema, update.$set);
142
- }
143
-
144
- const spec = { q: filter, u: update, multi: false };
145
- if (options.upsert) spec.upsert = true;
146
- if (options.arrayFilters) spec.arrayFilters = options.arrayFilters;
147
- if (options.hint) spec.hint = options.hint;
148
- if (options.collation) spec.collation = options.collation;
149
-
150
- const cmd = { update: this.collection, updates: [spec] };
151
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
152
- this._assertOk(result, 'updateOne');
153
- return { ok: true, nModified: result?.nModified || 0, upserted: result?.upserted || null };
154
- }
155
-
156
- /**
157
- * 닀쀑 λ¬Έμ„œ μˆ˜μ •
158
- * @param {object} filter - 검색 쑰건
159
- * @param {object} update - μˆ˜μ • λ‚΄μš©
160
- * @param {object} [options={}] - μ˜΅μ…˜
161
- * @returns {Promise<object>} { ok, nModified }
162
- */
163
- async updateMany(filter, update, options = {}) {
164
- // $set 데이터에 λŒ€ν•΄ μŠ€ν‚€λ§ˆ 검증 적용
165
- if (update?.$set) {
166
- const schema = this.constructor.schema;
167
- update.$set = await validateForUpdate(schema, update.$set);
168
- }
169
-
170
- const spec = { q: filter, u: update, multi: true };
171
- if (options.upsert) spec.upsert = true;
172
- if (options.arrayFilters) spec.arrayFilters = options.arrayFilters;
173
-
174
- const cmd = { update: this.collection, updates: [spec] };
175
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
176
- this._assertOk(result, 'updateMany');
177
- return { ok: true, nModified: result?.nModified || 0 };
178
- }
179
-
180
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
181
- // πŸ—‘οΈ μ‚­μ œ (Delete)
182
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
183
-
184
- /**
185
- * 단일 λ¬Έμ„œ μ‚­μ œ
186
- * @param {object} filter - 검색 쑰건
187
- * @returns {Promise<object>} { ok, n }
188
- */
189
- async deleteOne(filter) {
190
- const cmd = { delete: this.collection, deletes: [{ q: filter, limit: 1 }] };
191
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
192
- this._assertOk(result, 'deleteOne');
193
- return { ok: true, n: result?.n || 0 };
194
- }
195
-
196
- /**
197
- * 닀쀑 λ¬Έμ„œ μ‚­μ œ
198
- * @param {object} filter - 검색 쑰건
199
- * @returns {Promise<object>} { ok, n }
200
- */
201
- async deleteMany(filter) {
202
- const cmd = { delete: this.collection, deletes: [{ q: filter, limit: 0 }] };
203
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
204
- this._assertOk(result, 'deleteMany');
205
- return { ok: true, n: result?.n || 0 };
206
- }
207
-
208
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
209
- // πŸ“Š 집계 (Aggregate)
210
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
211
-
212
- /**
213
- * MongoDB Aggregation Pipeline μ‹€ν–‰
214
- * @param {object[]} pipeline - νŒŒμ΄ν”„λΌμΈ μŠ€ν…Œμ΄μ§€ λ°°μ—΄
215
- * @param {object} [options={}] - μ˜΅μ…˜ { collation, allowDiskUse }
216
- * @returns {Promise<object[]>} κ²°κ³Ό λ°°μ—΄
217
- */
218
- async aggregate(pipeline, options = {}) {
219
- const cmd = { aggregate: this.collection, pipeline, cursor: {} };
220
- if (options.collation) cmd.collation = options.collation;
221
- if (options.allowDiskUse) cmd.allowDiskUse = true;
222
-
223
- const result = await this._native.mongoCommand(JSON.stringify(cmd));
224
- return result?.cursor?.firstBatch || [];
225
- }
226
-
227
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
228
- // πŸ”— 체이닝 ν˜Έν™˜ API (AlloyModel 상속)
229
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
230
- // select, where, orWhere, whereIn, whereNull, limit, offset, orderBy,
231
- // get, first, count, update, delete 등은 AlloyModelμ—μ„œ 상속
232
-
233
- /**
234
- * 체이닝 방식 μ‚½μž…
235
- * @param {object} data - μ‚½μž…ν•  데이터
236
- * @returns {Promise<object>}
237
- */
238
- async save(data) {
239
- const schema = this.constructor.schema;
240
- data = await validateForSave(schema, data);
241
- applyMongoDefaults(schema, data);
242
-
243
- return await this._native.save(JSON.stringify(data));
244
- }
245
-
246
- /**
247
- * Raw MongoDB μ»€λ§¨λ“œ μ‹€ν–‰
248
- * @param {object} cmdObj - MongoDB μ»€λ§¨λ“œ 객체
249
- * @returns {Promise<object>}
250
- */
251
- async command(cmdObj) {
252
- return await this._native.mongoCommand(JSON.stringify(cmdObj));
253
- }
254
-
255
- /**
256
- * 응닡 검증 (ok !== 1이면 μ—λŸ¬)
257
- * @private
258
- */
259
- _assertOk(result, operation) {
260
- const ok = result?.ok;
261
- if (ok !== 1 && ok !== 1.0) {
262
- throw new AlloyError('MONGO_OPERATION_FAILED', `Mongo ${operation} Failed: ${result?.errmsg || 'Unknown error'}`, 500);
263
- }
264
- }
265
- }
@@ -1,158 +0,0 @@
1
- import native from '@alloy-framework/rust';
2
-
3
- /**
4
- * πŸ”΄ Alloy Redis λͺ¨λΈ
5
- * Redis의 자주 μ‚¬μš©ν•˜λŠ” λͺ…령을 직관적 API둜 μ œκ³΅ν•©λ‹ˆλ‹€.
6
- * Rust N-API의 AlloyRedisλ₯Ό λž˜ν•‘ν•©λ‹ˆλ‹€.
7
- *
8
- * @example
9
- * const redis = new AlloyRedisModel('cache');
10
- * await redis.set('key', 'value', 300);
11
- * const val = await redis.get('key');
12
- */
13
- export class AlloyRedisModel {
14
- /**
15
- * @param {string} [store='cache'] - alloy.conf에 μ •μ˜λœ Redis μŠ€ν† μ–΄ 이름
16
- */
17
- constructor(store = 'cache') {
18
- /** @type {string} μŠ€ν† μ–΄ 이름 */
19
- this.store = store;
20
- /** @type {import('@alloy/rust').AlloyRedis} λ„€μ΄ν‹°λΈŒ Redis μΈμŠ€ν„΄μŠ€ */
21
- this._native = new native.AlloyRedis(store);
22
- }
23
-
24
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
25
- // πŸ“ String λͺ…λ Ή
26
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
27
-
28
- /**
29
- * ν‚€-κ°’ μ €μž₯ (TTL μ˜΅μ…˜)
30
- * @param {string} key
31
- * @param {string} value
32
- * @param {number} [ttlSeconds] - TTL (초, λ―Έμ§€μ • μ‹œ 영ꡬ)
33
- * @returns {Promise<void>}
34
- */
35
- async set(key, value, ttlSeconds) {
36
- return await this._native.set(key, String(value), ttlSeconds || 0);
37
- }
38
-
39
- /**
40
- * ν‚€ 쑰회
41
- * @param {string} key
42
- * @returns {Promise<string|null>}
43
- */
44
- async get(key) {
45
- return await this._native.get(key);
46
- }
47
-
48
- /**
49
- * ν‚€ μ‚­μ œ
50
- * @param {string} key
51
- * @returns {Promise<void>}
52
- */
53
- async del(key) {
54
- return await this._native.del(key);
55
- }
56
-
57
- /**
58
- * ν‚€ 쑴재 μ—¬λΆ€
59
- * @param {string} key
60
- * @returns {Promise<boolean>}
61
- */
62
- async exists(key) {
63
- return await this._native.exists(key);
64
- }
65
-
66
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
67
- // πŸ“‹ Hash λͺ…λ Ή
68
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
69
-
70
- /**
71
- * Hash ν•„λ“œ μ„€μ •
72
- * @param {string} key - Hash ν‚€
73
- * @param {string} field - ν•„λ“œλͺ…
74
- * @param {string} value - κ°’
75
- * @returns {Promise<void>}
76
- */
77
- async hset(key, field, value) {
78
- return await this._native.hset(key, field, String(value));
79
- }
80
-
81
- /**
82
- * Hash ν•„λ“œ 쑰회
83
- * @param {string} key
84
- * @param {string} field
85
- * @returns {Promise<string|null>}
86
- */
87
- async hget(key, field) {
88
- return await this._native.hget(key, field);
89
- }
90
-
91
- /**
92
- * Hash ν•„λ“œ μ‚­μ œ
93
- * @param {string} key
94
- * @param {string} field
95
- * @returns {Promise<void>}
96
- */
97
- async hdel(key, field) {
98
- return await this._native.hdel(key, field);
99
- }
100
-
101
- /**
102
- * Hash 전체 쑰회
103
- * @param {string} key
104
- * @returns {Promise<object>} ν‚€-κ°’ 쌍
105
- */
106
- async hgetall(key) {
107
- return await this._native.hgetall(key);
108
- }
109
-
110
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
111
- // πŸ“ List λͺ…λ Ή
112
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
113
-
114
- /**
115
- * List μ™Όμͺ½ ν‘Έμ‹œ
116
- * @param {string} key
117
- * @param {string} value
118
- * @returns {Promise<void>}
119
- */
120
- async lpush(key, value) {
121
- return await this._native.lpush(key, String(value));
122
- }
123
-
124
- /**
125
- * List 였λ₯Έμͺ½ ν‘Έμ‹œ
126
- * @param {string} key
127
- * @param {string} value
128
- * @returns {Promise<void>}
129
- */
130
- async rpush(key, value) {
131
- return await this._native.rpush(key, String(value));
132
- }
133
-
134
- /**
135
- * List λ²”μœ„ 쑰회
136
- * @param {string} key
137
- * @param {number} [start=0]
138
- * @param {number} [stop=-1]
139
- * @returns {Promise<string[]>}
140
- */
141
- async lrange(key, start = 0, stop = -1) {
142
- return await this._native.lrange(key, start, stop);
143
- }
144
-
145
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
146
- // πŸ”§ μœ ν‹Έλ¦¬ν‹°
147
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
148
-
149
- /**
150
- * Raw Redis λͺ…λ Ή μ‹€ν–‰
151
- * @param {string} cmd - λͺ…λ Ήμ–΄ (예: 'TTL')
152
- * @param {string[]} args - 인자 λ°°μ—΄
153
- * @returns {Promise<any>}
154
- */
155
- async command(cmd, ...args) {
156
- return await this._native.rawCommand(cmd, args);
157
- }
158
- }
@@ -1,168 +0,0 @@
1
- import { AlloyError } from '../util/AlloyError.js';
2
-
3
- /**
4
- * 🦒 AlloyGoose μŠ€ν‚€λ§ˆ μžλ™ 검증기
5
- *
6
- * AlloyGoose μŠ€ν‚€λ§ˆμ˜ `fields` μ •μ˜λ₯Ό 기반으둜 데이터λ₯Ό μžλ™ κ²€μ¦ν•©λ‹ˆλ‹€.
7
- * Joi 없이 기본적인 νƒ€μž…/ν•„μˆ˜κ°’/길이/μ—΄κ±°ν˜• 검증을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.
8
- *
9
- * @example
10
- * GooseValidator.validateInsert(schema.fields, { username: 'admin', age: 25 });
11
- * GooseValidator.validateUpdate(schema.fields, { age: 30 });
12
- */
13
- export class GooseValidator {
14
- /** 숫자 νƒ€μž… λͺ©λ‘ */
15
- static #NUMBER_TYPES = new Set([
16
- 'int', 'bigint', 'mediumint', 'smallint', 'tinyint',
17
- 'float', 'double', 'decimal', 'number',
18
- ]);
19
-
20
- /** λ¬Έμžμ—΄ νƒ€μž… λͺ©λ‘ */
21
- static #STRING_TYPES = new Set([
22
- 'string', 'text', 'mediumtext', 'longtext', 'enum',
23
- ]);
24
-
25
- /**
26
- * INSERT 용 검증 β€” required ν•„λ“œ 체크 포함
27
- * @param {object} fields - AlloyGoose fields μ •μ˜
28
- * @param {object} data - μ‚½μž…ν•  데이터
29
- * @throws {Error} 검증 μ‹€νŒ¨ μ‹œ (statusCode: 400)
30
- */
31
- static validateInsert(fields, data) {
32
- const errors = [];
33
-
34
- for (const [name, def] of Object.entries(fields)) {
35
- // auto_increment PKλŠ” 검증 μŠ€ν‚΅
36
- if (def.primary && def.auto_increment) continue;
37
- // MongoDB _idλŠ” μ‹œμŠ€ν…œ 생성
38
- if (name === '_id') continue;
39
-
40
- const value = data[name];
41
-
42
- // ── required 체크 (INSERT μ „μš©) ──
43
- const isRequired = def.required === true || def.nullable === false;
44
- if (isRequired && (value === undefined || value === null)) {
45
- // defaultκ°€ 있으면 μžλ™ μ£Όμž…λ˜λ―€λ‘œ 패슀
46
- if (def.default !== undefined) continue;
47
- errors.push(`'${name}' ν•„λ“œλŠ” ν•„μˆ˜μž…λ‹ˆλ‹€`);
48
- continue;
49
- }
50
-
51
- // 값이 μ—†μœΌλ©΄ 이후 νƒ€μž… 체크 λΆˆν•„μš”
52
- if (value === undefined || value === null) continue;
53
-
54
- // ── νƒ€μž… 체크 ──
55
- this.#checkType(name, def, value, errors);
56
-
57
- // ── 길이 체크 ──
58
- this.#checkLength(name, def, value, errors);
59
-
60
- // ── enum 체크 ──
61
- this.#checkEnum(name, def, value, errors);
62
-
63
- // ── ν•„λ“œλ³„ Joi μΆ”κ°€ 검증 ──
64
- this.#checkValidator(name, def, value, errors);
65
- }
66
-
67
- if (errors.length > 0) {
68
- throw new AlloyError('VALIDATION_ERROR', `Validation Error: ${errors.join(', ')}`, 400);
69
- }
70
- }
71
-
72
- /**
73
- * UPDATE 용 검증 β€” required λ¬΄μ‹œ, 제곡된 ν•„λ“œλ§Œ 검증
74
- * @param {object} fields - AlloyGoose fields μ •μ˜
75
- * @param {object} data - μˆ˜μ •ν•  데이터
76
- * @throws {Error} 검증 μ‹€νŒ¨ μ‹œ (statusCode: 400)
77
- */
78
- static validateUpdate(fields, data) {
79
- const errors = [];
80
-
81
- for (const [name, value] of Object.entries(data)) {
82
- const def = fields[name];
83
- // μŠ€ν‚€λ§ˆμ— μ—†λŠ” ν•„λ“œλŠ” κ±΄λ„ˆλœ€ (MongoDB μœ μ—°μ„±)
84
- if (!def) continue;
85
- // null ν—ˆμš© 체크
86
- if (value === null) {
87
- if (def.nullable === false || def.required === true) {
88
- errors.push(`'${name}' ν•„λ“œλŠ” NULL일 수 μ—†μŠ΅λ‹ˆλ‹€`);
89
- }
90
- continue;
91
- }
92
-
93
- this.#checkType(name, def, value, errors);
94
- this.#checkLength(name, def, value, errors);
95
- this.#checkEnum(name, def, value, errors);
96
- this.#checkValidator(name, def, value, errors);
97
- }
98
-
99
- if (errors.length > 0) {
100
- throw new AlloyError('VALIDATION_ERROR', `Validation Error: ${errors.join(', ')}`, 400);
101
- }
102
- }
103
-
104
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
105
- // πŸ”’ Private 검증 헬퍼
106
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
107
-
108
- static #checkType(name, def, value, errors) {
109
- const ft = def.type;
110
- if (!ft) return;
111
-
112
- if (this.#NUMBER_TYPES.has(ft)) {
113
- if (typeof value !== 'number' || Number.isNaN(value)) {
114
- errors.push(`'${name}' ν•„λ“œλŠ” μˆ«μžμ—¬μ•Ό ν•©λ‹ˆλ‹€ (type: ${ft})`);
115
- }
116
- } else if (this.#STRING_TYPES.has(ft)) {
117
- if (typeof value !== 'string') {
118
- errors.push(`'${name}' ν•„λ“œλŠ” λ¬Έμžμ—΄μ΄μ–΄μ•Ό ν•©λ‹ˆλ‹€ (type: ${ft})`);
119
- }
120
- } else if (ft === 'boolean') {
121
- if (typeof value !== 'boolean') {
122
- errors.push(`'${name}' ν•„λ“œλŠ” boolean이어야 ν•©λ‹ˆλ‹€`);
123
- }
124
- } else if (ft === 'json' || ft === 'object') {
125
- if (typeof value !== 'object') {
126
- errors.push(`'${name}' ν•„λ“œλŠ” 객체여야 ν•©λ‹ˆλ‹€ (type: ${ft})`);
127
- }
128
- } else if (ft === 'array') {
129
- if (!Array.isArray(value)) {
130
- errors.push(`'${name}' ν•„λ“œλŠ” 배열이어야 ν•©λ‹ˆλ‹€`);
131
- }
132
- } else if (ft === 'datetime' || ft === 'timestamp' || ft === 'date') {
133
- // λ¬Έμžμ—΄ λ˜λŠ” Date 객체 ν—ˆμš©
134
- if (typeof value !== 'string' && !(value instanceof Date)) {
135
- errors.push(`'${name}' ν•„λ“œλŠ” λ‚ μ§œ λ¬Έμžμ—΄ λ˜λŠ” Date 객체여야 ν•©λ‹ˆλ‹€`);
136
- }
137
- }
138
- }
139
-
140
- /** 길이 체크 μ œμ™Έ νƒ€μž… (fractional seconds precision) */
141
- static #SKIP_LENGTH_TYPES = new Set(['datetime', 'timestamp', 'date']);
142
-
143
- static #checkLength(name, def, value, errors) {
144
- if (this.#SKIP_LENGTH_TYPES.has(def.type)) return;
145
- if (def.length && typeof value === 'string' && value.length > def.length) {
146
- errors.push(`'${name}' ν•„λ“œλŠ” ${def.length}자 μ΄ν•˜μ—¬μ•Ό ν•©λ‹ˆλ‹€ (ν˜„μž¬: ${value.length}자)`);
147
- }
148
- }
149
-
150
- static #checkEnum(name, def, value, errors) {
151
- const enumValues = def.enum || def.enum_values;
152
- if (enumValues && Array.isArray(enumValues)) {
153
- if (!enumValues.includes(value)) {
154
- errors.push(`'${name}' ν•„λ“œλŠ” [${enumValues.join(', ')}] 쀑 ν•˜λ‚˜μ—¬μ•Ό ν•©λ‹ˆλ‹€`);
155
- }
156
- }
157
- }
158
-
159
- static #checkValidator(name, def, value, errors) {
160
- if (def.validator && typeof def.validator.validate === 'function') {
161
- const { error } = def.validator.validate(value);
162
- if (error) {
163
- const details = error.details?.map(d => d.message).join(', ') || error.message;
164
- errors.push(`'${name}': ${details}`);
165
- }
166
- }
167
- }
168
- }
package/src/data/index.js DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * πŸ“Š 데이터 계측 톡합 export
3
- */
4
- export { AlloyModel, AlloyMariaModel } from './AlloyModel.js';
5
- export { AlloyMongoModel } from './AlloyMongoModel.js';
6
- export { AlloyRedisModel } from './AlloyRedisModel.js';
@@ -1,79 +0,0 @@
1
- /**
2
- * πŸ”§ 검증/κΈ°λ³Έκ°’ 곡톡 헬퍼
3
- * AlloyModel, AlloyMongoModelμ—μ„œ μ€‘λ³΅λ˜λ˜ 검증 + κΈ°λ³Έκ°’ λ‘œμ§μ„ ν†΅ν•©ν•©λ‹ˆλ‹€.
4
- */
5
-
6
- /**
7
- * INSERT용 데이터 검증 (AlloyGoose + legacy Joi μžλ™ λΆ„κΈ°)
8
- * @param {object|null} schema - λͺ¨λΈμ˜ static schema
9
- * @param {object} data - μ‚½μž…ν•  데이터
10
- * @returns {object} 검증/μ •μ œλœ 데이터
11
- */
12
- export async function validateForSave(schema, data) {
13
- if (!schema) return data;
14
-
15
- // 1) AlloyGoose μŠ€ν‚€λ§ˆ (fields 기반)
16
- if (schema.fields && typeof schema.fields === 'object') {
17
- const { GooseValidator } = await import('./GooseValidator.js');
18
- GooseValidator.validateInsert(schema.fields, data);
19
- return data;
20
- }
21
-
22
- // 2) legacy Joi μŠ€ν‚€λ§ˆ (validate λ©”μ„œλ“œ)
23
- if (typeof schema.validate === 'function') {
24
- const { Validator } = await import('../http/Validator.js');
25
- return Validator.validate(schema, data);
26
- }
27
-
28
- return data;
29
- }
30
-
31
- /**
32
- * UPDATE용 데이터 검증 (AlloyGoose + legacy Joi μžλ™ λΆ„κΈ°)
33
- * @param {object|null} schema - λͺ¨λΈμ˜ static schema
34
- * @param {object} data - μˆ˜μ •ν•  데이터
35
- * @returns {object} 검증/μ •μ œλœ 데이터
36
- */
37
- export async function validateForUpdate(schema, data) {
38
- if (!schema) return data;
39
-
40
- // 1) AlloyGoose μŠ€ν‚€λ§ˆ (fields 기반 β€” 제곡된 ν•„λ“œλ§Œ 검증)
41
- if (schema.fields && typeof schema.fields === 'object') {
42
- const { GooseValidator } = await import('./GooseValidator.js');
43
- GooseValidator.validateUpdate(schema.fields, data);
44
- return data;
45
- }
46
-
47
- // 2) legacy Joi μŠ€ν‚€λ§ˆ (partial 검증)
48
- if (typeof schema.validate === 'function') {
49
- const { Validator } = await import('../http/Validator.js');
50
- return Validator.validate(schema, data, true);
51
- }
52
-
53
- return data;
54
- }
55
-
56
- /**
57
- * MongoDB κΈ°λ³Έκ°’ μžλ™ μ£Όμž… (DDL DEFAULT μ—†μœΌλ―€λ‘œ μ•± λ ˆλ²¨μ—μ„œ 처리)
58
- * @param {object|null} schema - λͺ¨λΈμ˜ static schema
59
- * @param {object} data - μ‚½μž…ν•  데이터 (in-place μˆ˜μ •)
60
- */
61
- export function applyMongoDefaults(schema, data) {
62
- if (!schema?.fields || typeof schema.fields !== 'object') return;
63
-
64
- for (const [fieldName, fieldDef] of Object.entries(schema.fields)) {
65
- if (fieldName === '_id') continue;
66
- if (data[fieldName] !== undefined) continue;
67
-
68
- if (fieldDef.default !== undefined) {
69
- if (fieldDef.default === 'CURRENT_TIMESTAMP' || fieldDef.default === 'NOW()') {
70
- data[fieldName] = new Date().toISOString();
71
- } else {
72
- data[fieldName] = fieldDef.default;
73
- }
74
- } else {
75
- // default λ―Έμ§€μ • ν•„λ“œλŠ” null둜 μ£Όμž… (MongoDB λ¬Έμ„œμ— ν•„λ“œ 쑴재 보μž₯)
76
- data[fieldName] = null;
77
- }
78
- }
79
- }