@rscc/common-core 0.4.0 → 0.6.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.
- package/README.md +205 -14
- package/dist/index.cjs +617 -36
- package/dist/index.d.cts +425 -19
- package/dist/index.d.ts +425 -19
- package/dist/index.js +602 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,6 +20,21 @@ var ResultCode = {
|
|
|
20
20
|
INTERNAL_SERVER_ERROR: "500"
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
// src/errorCodes.ts
|
|
24
|
+
var COMMON_CODES = new Set(Object.values(ResultCode));
|
|
25
|
+
function isCommonResultCode(code) {
|
|
26
|
+
return COMMON_CODES.has(code);
|
|
27
|
+
}
|
|
28
|
+
function resultCodeForStatus(status) {
|
|
29
|
+
const exact = String(status);
|
|
30
|
+
if (exact !== ResultCode.SUCCESS && COMMON_CODES.has(exact)) return exact;
|
|
31
|
+
if (status >= 400 && status < 500) return ResultCode.BAD_REQUEST;
|
|
32
|
+
return ResultCode.INTERNAL_SERVER_ERROR;
|
|
33
|
+
}
|
|
34
|
+
function toCommonResultCode(code, status) {
|
|
35
|
+
return isCommonResultCode(code) ? code : resultCodeForStatus(status);
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
// src/csrf.ts
|
|
24
39
|
var DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
|
|
25
40
|
var DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
|
|
@@ -119,6 +134,141 @@ function generateIdempotencyKey() {
|
|
|
119
134
|
}).join("-");
|
|
120
135
|
}
|
|
121
136
|
|
|
137
|
+
// src/messages.ts
|
|
138
|
+
var KO = Object.freeze({
|
|
139
|
+
"rscc.result.SUCCESS": "\uC694\uCCAD\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uCC98\uB9AC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
140
|
+
"rscc.result.BAD_REQUEST": "\uC798\uBABB\uB41C \uC694\uCCAD\uC785\uB2C8\uB2E4.",
|
|
141
|
+
"rscc.result.UNAUTHORIZED": "\uC778\uC99D\uB418\uC9C0 \uC54A\uC740 \uC0AC\uC6A9\uC790\uC785\uB2C8\uB2E4.",
|
|
142
|
+
"rscc.result.FORBIDDEN": "\uC811\uADFC \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
143
|
+
"rscc.result.NOT_FOUND": "\uB9AC\uC18C\uC2A4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
144
|
+
"rscc.result.METHOD_NOT_ALLOWED": "\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC740 \uC694\uCCAD \uBC29\uC2DD\uC785\uB2C8\uB2E4.",
|
|
145
|
+
"rscc.result.CONFLICT": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uB9AC\uC18C\uC2A4\uC785\uB2C8\uB2E4.",
|
|
146
|
+
"rscc.result.UNSUPPORTED_MEDIA_TYPE": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC694\uCCAD \uD615\uC2DD\uC785\uB2C8\uB2E4.",
|
|
147
|
+
"rscc.result.TOO_MANY_REQUESTS": "\uC694\uCCAD \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
148
|
+
"rscc.result.INTERNAL_SERVER_ERROR": "\uC11C\uBC84 \uB0B4\uBD80 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.",
|
|
149
|
+
"rscc.web.missingParameter": "\uD544\uC218 \uC694\uCCAD \uD30C\uB77C\uBBF8\uD130\uAC00 \uB204\uB77D\uB418\uC5C8\uC2B5\uB2C8\uB2E4: {0}",
|
|
150
|
+
"rscc.web.typeMismatch": "\uC694\uCCAD \uD30C\uB77C\uBBF8\uD130 \uD0C0\uC785\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {0}",
|
|
151
|
+
"rscc.web.unreadableBody": "\uC694\uCCAD \uBCF8\uBB38\uC744 \uD574\uC11D\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. JSON \uD615\uC2DD\uACFC \uAC01 \uD544\uB4DC\uC758 \uD0C0\uC785\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
152
|
+
"rscc.idempotency.conflict": "\uC774\uBBF8 \uCC98\uB9AC\uB418\uC5C8\uAC70\uB098 \uCC98\uB9AC \uC911\uC778 \uC694\uCCAD\uC785\uB2C8\uB2E4.",
|
|
153
|
+
"rscc.idempotency.invalidFormat": "Idempotency-Key \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
154
|
+
"rscc.idempotency.required": "Idempotency-Key \uD5E4\uB354\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
155
|
+
"rscc.upload.size": "\uC5C5\uB85C\uB4DC \uAC00\uB2A5\uD55C \uCD5C\uB300 \uD06C\uAE30\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
156
|
+
"rscc.upload.extension": "\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4: {0}",
|
|
157
|
+
"rscc.upload.contentMismatch": "\uD30C\uC77C \uB0B4\uC6A9\uC774 \uD655\uC7A5\uC790\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
158
|
+
"rscc.query.sortField": "\uC815\uB82C\uD560 \uC218 \uC5C6\uB294 \uD544\uB4DC\uC785\uB2C8\uB2E4: {0}",
|
|
159
|
+
"rscc.query.sortDirection": "\uC815\uB82C \uBC29\uD5A5\uC740 asc|desc \uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4: {0}",
|
|
160
|
+
"rscc.security.csrf": "CSRF \uD1A0\uD070\uC774 \uC5C6\uAC70\uB098 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
161
|
+
"rscc.client.unreadableBody": "\uC751\uB2F5 \uBCF8\uBB38\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
162
|
+
"rscc.client.invalidJson": "\uC751\uB2F5 \uBCF8\uBB38\uC774 \uC720\uD6A8\uD55C JSON \uC774 \uC544\uB2D9\uB2C8\uB2E4.",
|
|
163
|
+
"rscc.client.httpError": "API \uC624\uB958: {0} {1}"
|
|
164
|
+
});
|
|
165
|
+
var EN = Object.freeze({
|
|
166
|
+
"rscc.result.SUCCESS": "The request was processed successfully.",
|
|
167
|
+
"rscc.result.BAD_REQUEST": "Bad request.",
|
|
168
|
+
"rscc.result.UNAUTHORIZED": "Authentication is required.",
|
|
169
|
+
"rscc.result.FORBIDDEN": "Access is denied.",
|
|
170
|
+
"rscc.result.NOT_FOUND": "Resource not found.",
|
|
171
|
+
"rscc.result.METHOD_NOT_ALLOWED": "Request method is not allowed.",
|
|
172
|
+
"rscc.result.CONFLICT": "The resource already exists.",
|
|
173
|
+
"rscc.result.UNSUPPORTED_MEDIA_TYPE": "Unsupported request content type.",
|
|
174
|
+
"rscc.result.TOO_MANY_REQUESTS": "Too many requests.",
|
|
175
|
+
"rscc.result.INTERNAL_SERVER_ERROR": "An internal server error occurred.",
|
|
176
|
+
"rscc.web.missingParameter": "Required request parameter is missing: {0}",
|
|
177
|
+
"rscc.web.typeMismatch": "Request parameter has an invalid type: {0}",
|
|
178
|
+
"rscc.web.unreadableBody": "The request body could not be read. Check the JSON syntax and field types.",
|
|
179
|
+
"rscc.idempotency.conflict": "The request has already been processed or is in progress.",
|
|
180
|
+
"rscc.idempotency.invalidFormat": "Invalid Idempotency-Key format.",
|
|
181
|
+
"rscc.idempotency.required": "The Idempotency-Key header is required.",
|
|
182
|
+
"rscc.upload.size": "The upload exceeds the maximum allowed size.",
|
|
183
|
+
"rscc.upload.extension": "File type is not allowed: {0}",
|
|
184
|
+
"rscc.upload.contentMismatch": "File content does not match its extension.",
|
|
185
|
+
"rscc.query.sortField": "Field cannot be sorted: {0}",
|
|
186
|
+
"rscc.query.sortDirection": "Sort direction must be asc or desc: {0}",
|
|
187
|
+
"rscc.security.csrf": "The CSRF token is missing or invalid.",
|
|
188
|
+
"rscc.client.unreadableBody": "Failed to read the response body.",
|
|
189
|
+
"rscc.client.invalidJson": "The response body is not valid JSON.",
|
|
190
|
+
"rscc.client.httpError": "API error: {0} {1}"
|
|
191
|
+
});
|
|
192
|
+
var MESSAGES = Object.freeze({ ko: KO, en: EN });
|
|
193
|
+
var FIXED_KEY_LIST = [
|
|
194
|
+
"rscc.idempotency.conflict",
|
|
195
|
+
"rscc.idempotency.invalidFormat",
|
|
196
|
+
"rscc.idempotency.required",
|
|
197
|
+
"rscc.query.sortField",
|
|
198
|
+
"rscc.query.sortDirection",
|
|
199
|
+
"rscc.security.csrf"
|
|
200
|
+
];
|
|
201
|
+
var FIXED_KEYS = new Set(FIXED_KEY_LIST);
|
|
202
|
+
var FIXED_MESSAGE_KEYS = new Set(FIXED_KEY_LIST);
|
|
203
|
+
var PLACEHOLDER = /\{(\d+)\}/g;
|
|
204
|
+
function formatMessage(template, ...args) {
|
|
205
|
+
if (args.length === 0) return template;
|
|
206
|
+
return template.replace(PLACEHOLDER, (match, digits) => {
|
|
207
|
+
const index = Number(digits);
|
|
208
|
+
return index < args.length ? String(args[index]) : match;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function primarySubtag(tag) {
|
|
212
|
+
if (tag === void 0) return "";
|
|
213
|
+
const trimmed = tag.trim();
|
|
214
|
+
const dash = trimmed.indexOf("-");
|
|
215
|
+
return (dash < 0 ? trimmed : trimmed.slice(0, dash)).toLowerCase();
|
|
216
|
+
}
|
|
217
|
+
function builtinCatalog(language) {
|
|
218
|
+
return language === "ko" || language === "en" ? MESSAGES[language] : void 0;
|
|
219
|
+
}
|
|
220
|
+
function lookupBuiltin(language, key) {
|
|
221
|
+
const catalog = builtinCatalog(language);
|
|
222
|
+
return catalog !== void 0 && Object.prototype.hasOwnProperty.call(catalog, key) ? catalog[key] : void 0;
|
|
223
|
+
}
|
|
224
|
+
function lookupOverride(overrides, key, language) {
|
|
225
|
+
if (overrides === void 0) return void 0;
|
|
226
|
+
const value = typeof overrides === "function" ? overrides(key, language) : Object.prototype.hasOwnProperty.call(overrides, key) ? overrides[key] : void 0;
|
|
227
|
+
return typeof value === "string" && value !== "" && value !== key ? value : void 0;
|
|
228
|
+
}
|
|
229
|
+
function getMessage(key, options = {}) {
|
|
230
|
+
const defaultLanguage = primarySubtag(options.defaultLocale) || "ko";
|
|
231
|
+
const language = primarySubtag(options.locale) || defaultLanguage;
|
|
232
|
+
const args = options.args ?? [];
|
|
233
|
+
const template = (FIXED_KEYS.has(key) && (language === "ko" || language === "en") ? lookupBuiltin(language, key) : void 0) ?? lookupOverride(options.overrides, key, language) ?? lookupBuiltin(language, key) ?? lookupBuiltin(defaultLanguage, key) ?? lookupBuiltin("ko", key) ?? key;
|
|
234
|
+
return formatMessage(template, ...args);
|
|
235
|
+
}
|
|
236
|
+
var LANGUAGE_RANGE = /^(?:\*|[A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*)$/;
|
|
237
|
+
var Q_VALUE = /^(?:\d+(?:\.\d*)?|\.\d+)$/;
|
|
238
|
+
function negotiateLanguage(acceptLanguage, supported = ["ko", "en"], fallback = "ko") {
|
|
239
|
+
if (acceptLanguage == null || acceptLanguage.trim() === "") return fallback;
|
|
240
|
+
const supportedSet = new Set(supported.map((s) => primarySubtag(s)).filter((s) => s !== ""));
|
|
241
|
+
const ranges = [];
|
|
242
|
+
for (const item of acceptLanguage.split(",")) {
|
|
243
|
+
const [rawTag = "", ...params] = item.split(";");
|
|
244
|
+
const tag = rawTag.trim();
|
|
245
|
+
if (!LANGUAGE_RANGE.test(tag)) continue;
|
|
246
|
+
let q = 1;
|
|
247
|
+
let valid = true;
|
|
248
|
+
for (const param of params) {
|
|
249
|
+
const eq = param.indexOf("=");
|
|
250
|
+
const name = (eq < 0 ? param : param.slice(0, eq)).trim().toLowerCase();
|
|
251
|
+
if (name !== "q") continue;
|
|
252
|
+
const value = eq < 0 ? "" : param.slice(eq + 1).trim();
|
|
253
|
+
const parsed = Number(value);
|
|
254
|
+
if (!Q_VALUE.test(value) || parsed < 0 || parsed > 1) {
|
|
255
|
+
valid = false;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
q = parsed;
|
|
259
|
+
}
|
|
260
|
+
if (!valid || q === 0) continue;
|
|
261
|
+
ranges.push({ tag, q });
|
|
262
|
+
}
|
|
263
|
+
ranges.sort((a, b) => b.q - a.q);
|
|
264
|
+
for (const { tag } of ranges) {
|
|
265
|
+
if (tag === "*") return fallback;
|
|
266
|
+
const language = primarySubtag(tag);
|
|
267
|
+
if (supportedSet.has(language)) return language;
|
|
268
|
+
}
|
|
269
|
+
return fallback;
|
|
270
|
+
}
|
|
271
|
+
|
|
122
272
|
// src/retry.ts
|
|
123
273
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
124
274
|
function isRetryableStatus(status) {
|
|
@@ -204,6 +354,7 @@ var ApiError = class extends Error {
|
|
|
204
354
|
super(args.message);
|
|
205
355
|
this.name = "ApiError";
|
|
206
356
|
this.code = args.code;
|
|
357
|
+
this.commonCode = toCommonResultCode(args.code, args.status);
|
|
207
358
|
this.status = args.status;
|
|
208
359
|
this.traceId = args.traceId;
|
|
209
360
|
this.data = args.data;
|
|
@@ -235,6 +386,8 @@ function isCommonResponse(value) {
|
|
|
235
386
|
}
|
|
236
387
|
function createApiClient(config) {
|
|
237
388
|
const traceIdHeader = config.traceIdHeader ?? "X-Trace-Id";
|
|
389
|
+
const clientMessage = (key, ...args) => getMessage(key, { locale: config.locale, args });
|
|
390
|
+
const acceptLanguage = config.sendAcceptLanguage && config.locale && config.locale.trim() !== "" ? config.locale.trim() : null;
|
|
238
391
|
let hookWarned = false;
|
|
239
392
|
const safeHook = (fn) => {
|
|
240
393
|
try {
|
|
@@ -263,6 +416,9 @@ function createApiClient(config) {
|
|
|
263
416
|
if (!headers.has("Content-Type") && typeof init.body === "string") {
|
|
264
417
|
headers.set("Content-Type", "application/json");
|
|
265
418
|
}
|
|
419
|
+
if (acceptLanguage !== null && !headers.has("Accept-Language")) {
|
|
420
|
+
headers.set("Accept-Language", acceptLanguage);
|
|
421
|
+
}
|
|
266
422
|
if (config.getToken && !headers.has("Authorization")) {
|
|
267
423
|
const token = config.getToken();
|
|
268
424
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
@@ -286,7 +442,7 @@ function createApiClient(config) {
|
|
|
286
442
|
if (config.strictJson && response.ok) {
|
|
287
443
|
throw new ApiError({
|
|
288
444
|
code: "INVALID_JSON",
|
|
289
|
-
message: "
|
|
445
|
+
message: clientMessage("rscc.client.unreadableBody"),
|
|
290
446
|
status: response.status,
|
|
291
447
|
traceId,
|
|
292
448
|
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After"))
|
|
@@ -305,7 +461,7 @@ function createApiClient(config) {
|
|
|
305
461
|
if (config.strictJson && response.ok) {
|
|
306
462
|
throw new ApiError({
|
|
307
463
|
code: "INVALID_JSON",
|
|
308
|
-
message: "
|
|
464
|
+
message: clientMessage("rscc.client.invalidJson"),
|
|
309
465
|
status: response.status,
|
|
310
466
|
traceId,
|
|
311
467
|
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After")),
|
|
@@ -334,7 +490,7 @@ function createApiClient(config) {
|
|
|
334
490
|
}
|
|
335
491
|
if (!response.ok) {
|
|
336
492
|
const j2 = json;
|
|
337
|
-
const message = typeof j2?.message === "string" && j2.message || typeof j2?.error === "string" && j2.error ||
|
|
493
|
+
const message = typeof j2?.message === "string" && j2.message || typeof j2?.error === "string" && j2.error || clientMessage("rscc.client.httpError", response.status, response.statusText);
|
|
338
494
|
fail(String(response.status), message);
|
|
339
495
|
}
|
|
340
496
|
return { data: json, traceId, status: response.status, response };
|
|
@@ -392,13 +548,232 @@ function createApiClient(config) {
|
|
|
392
548
|
return { request, requestWithMeta };
|
|
393
549
|
}
|
|
394
550
|
|
|
551
|
+
// src/sseEvents.ts
|
|
552
|
+
var RETRY_PATTERN = /^[0-9]+$/;
|
|
553
|
+
function createParser(onEvent, onRetry, haltOnTrue, initialLastEventId) {
|
|
554
|
+
let pending = "";
|
|
555
|
+
let bomChecked = false;
|
|
556
|
+
let skipLeadingLf = false;
|
|
557
|
+
let data = "";
|
|
558
|
+
let eventType = "";
|
|
559
|
+
let idBuffer = initialLastEventId;
|
|
560
|
+
let lastEventId = initialLastEventId;
|
|
561
|
+
let retryMs = null;
|
|
562
|
+
let ended = false;
|
|
563
|
+
let halted = false;
|
|
564
|
+
const lineBreak = /[\r\n]/g;
|
|
565
|
+
const dispatch = () => {
|
|
566
|
+
lastEventId = idBuffer;
|
|
567
|
+
if (data === "") {
|
|
568
|
+
eventType = "";
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const event = {
|
|
572
|
+
event: eventType === "" ? "message" : eventType,
|
|
573
|
+
data: data.endsWith("\n") ? data.slice(0, -1) : data,
|
|
574
|
+
id: lastEventId
|
|
575
|
+
};
|
|
576
|
+
data = "";
|
|
577
|
+
eventType = "";
|
|
578
|
+
if (onEvent(event) === true && haltOnTrue) halted = true;
|
|
579
|
+
};
|
|
580
|
+
const processLine = (line) => {
|
|
581
|
+
if (line === "") {
|
|
582
|
+
dispatch();
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (line.charCodeAt(0) === 58) return;
|
|
586
|
+
const colon = line.indexOf(":");
|
|
587
|
+
let field;
|
|
588
|
+
let value;
|
|
589
|
+
if (colon === -1) {
|
|
590
|
+
field = line;
|
|
591
|
+
value = "";
|
|
592
|
+
} else {
|
|
593
|
+
field = line.slice(0, colon);
|
|
594
|
+
value = line.slice(colon + 1);
|
|
595
|
+
if (value.charCodeAt(0) === 32) value = value.slice(1);
|
|
596
|
+
}
|
|
597
|
+
switch (field) {
|
|
598
|
+
case "event":
|
|
599
|
+
eventType = value;
|
|
600
|
+
break;
|
|
601
|
+
case "data":
|
|
602
|
+
data += value + "\n";
|
|
603
|
+
break;
|
|
604
|
+
case "id":
|
|
605
|
+
if (!value.includes("\0")) idBuffer = value;
|
|
606
|
+
break;
|
|
607
|
+
case "retry":
|
|
608
|
+
if (RETRY_PATTERN.test(value)) {
|
|
609
|
+
retryMs = Number(value);
|
|
610
|
+
onRetry?.(retryMs);
|
|
611
|
+
}
|
|
612
|
+
break;
|
|
613
|
+
default:
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
const feed = (chunk) => {
|
|
618
|
+
if (ended) throw new Error("SSE \uD30C\uC11C\uAC00 \uC774\uBBF8 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4(end \uC774\uD6C4 feed).");
|
|
619
|
+
if (halted || chunk === "") return;
|
|
620
|
+
let text = chunk;
|
|
621
|
+
if (!bomChecked) {
|
|
622
|
+
bomChecked = true;
|
|
623
|
+
if (text.charCodeAt(0) === 65279) text = text.slice(1);
|
|
624
|
+
}
|
|
625
|
+
if (skipLeadingLf) {
|
|
626
|
+
skipLeadingLf = false;
|
|
627
|
+
if (text.charCodeAt(0) === 10) text = text.slice(1);
|
|
628
|
+
}
|
|
629
|
+
let start = 0;
|
|
630
|
+
for (; ; ) {
|
|
631
|
+
lineBreak.lastIndex = start;
|
|
632
|
+
const match = lineBreak.exec(text);
|
|
633
|
+
if (match === null) break;
|
|
634
|
+
const at = match.index;
|
|
635
|
+
const line = pending + text.slice(start, at);
|
|
636
|
+
pending = "";
|
|
637
|
+
let next = at + 1;
|
|
638
|
+
if (text.charCodeAt(at) === 13) {
|
|
639
|
+
if (next < text.length) {
|
|
640
|
+
if (text.charCodeAt(next) === 10) next += 1;
|
|
641
|
+
} else {
|
|
642
|
+
skipLeadingLf = true;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
start = next;
|
|
646
|
+
processLine(line);
|
|
647
|
+
if (halted) return;
|
|
648
|
+
}
|
|
649
|
+
pending += text.slice(start);
|
|
650
|
+
};
|
|
651
|
+
const end = (options) => {
|
|
652
|
+
if (ended) return;
|
|
653
|
+
ended = true;
|
|
654
|
+
if (halted) return;
|
|
655
|
+
if (options?.dispatchIncomplete) {
|
|
656
|
+
if (pending !== "") processLine(pending);
|
|
657
|
+
if (!halted) dispatch();
|
|
658
|
+
}
|
|
659
|
+
pending = "";
|
|
660
|
+
data = "";
|
|
661
|
+
eventType = "";
|
|
662
|
+
};
|
|
663
|
+
return {
|
|
664
|
+
feed,
|
|
665
|
+
end,
|
|
666
|
+
get lastEventId() {
|
|
667
|
+
return lastEventId;
|
|
668
|
+
},
|
|
669
|
+
get retryMs() {
|
|
670
|
+
return retryMs;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function createSseEventParser(onEvent, onRetry, options) {
|
|
675
|
+
return createParser(onEvent, onRetry, false, options?.lastEventId ?? "");
|
|
676
|
+
}
|
|
677
|
+
function parseSseEvents(text) {
|
|
678
|
+
const events = [];
|
|
679
|
+
const parser = createSseEventParser((e) => {
|
|
680
|
+
events.push(e);
|
|
681
|
+
});
|
|
682
|
+
parser.feed(text);
|
|
683
|
+
parser.end();
|
|
684
|
+
return events;
|
|
685
|
+
}
|
|
686
|
+
async function readSseEvents(response, options) {
|
|
687
|
+
if (!response.body) {
|
|
688
|
+
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
689
|
+
}
|
|
690
|
+
const reader = response.body.getReader();
|
|
691
|
+
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
692
|
+
let stopped = false;
|
|
693
|
+
const parser = createParser(
|
|
694
|
+
(event) => {
|
|
695
|
+
if (options.onEvent(event) === true) {
|
|
696
|
+
stopped = true;
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
return void 0;
|
|
700
|
+
},
|
|
701
|
+
options.onRetry,
|
|
702
|
+
true,
|
|
703
|
+
options.lastEventId ?? ""
|
|
704
|
+
);
|
|
705
|
+
const result = () => ({
|
|
706
|
+
lastEventId: parser.lastEventId,
|
|
707
|
+
retryMs: parser.retryMs,
|
|
708
|
+
stopped
|
|
709
|
+
});
|
|
710
|
+
try {
|
|
711
|
+
while (true) {
|
|
712
|
+
const { done, value } = await reader.read();
|
|
713
|
+
if (done) break;
|
|
714
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
715
|
+
if (stopped) {
|
|
716
|
+
await reader.cancel().catch(() => void 0);
|
|
717
|
+
return result();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
parser.feed(decoder.decode());
|
|
721
|
+
parser.end({ dispatchIncomplete: options.dispatchIncompleteAtEof === true });
|
|
722
|
+
return result();
|
|
723
|
+
} catch (error) {
|
|
724
|
+
await reader.cancel(error).catch(() => void 0);
|
|
725
|
+
throw error;
|
|
726
|
+
} finally {
|
|
727
|
+
reader.releaseLock();
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
var CR_OR_LF = /[\r\n]/;
|
|
731
|
+
function formatSseEvent(data, options = {}) {
|
|
732
|
+
const { event, id, retry: retry2 } = options;
|
|
733
|
+
let out = "";
|
|
734
|
+
if (id !== void 0) {
|
|
735
|
+
if (CR_OR_LF.test(id) || id.includes("\0")) {
|
|
736
|
+
throw new Error("SSE id \uC5D0 CR\xB7LF\xB7NUL \uC744 \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
737
|
+
}
|
|
738
|
+
out += `id: ${id}
|
|
739
|
+
`;
|
|
740
|
+
}
|
|
741
|
+
if (event !== void 0 && event !== "") {
|
|
742
|
+
if (CR_OR_LF.test(event)) throw new Error("SSE event \uC5D0 CR\xB7LF \uB97C \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
743
|
+
out += `event: ${event}
|
|
744
|
+
`;
|
|
745
|
+
}
|
|
746
|
+
if (retry2 !== void 0) {
|
|
747
|
+
if (!Number.isSafeInteger(retry2) || retry2 < 0) {
|
|
748
|
+
throw new Error(`SSE retry \uB294 0 \uC774\uC0C1\uC758 \uC815\uC218(ms)\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${retry2}`);
|
|
749
|
+
}
|
|
750
|
+
out += `retry: ${retry2}
|
|
751
|
+
`;
|
|
752
|
+
}
|
|
753
|
+
for (const line of data.split(/\r\n|\r|\n/)) {
|
|
754
|
+
out += `data: ${line}
|
|
755
|
+
`;
|
|
756
|
+
}
|
|
757
|
+
return out + "\n";
|
|
758
|
+
}
|
|
759
|
+
function formatSseComment(text = "") {
|
|
760
|
+
if (CR_OR_LF.test(text)) throw new Error("SSE \uC8FC\uC11D\uC5D0 CR\xB7LF \uB97C \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
761
|
+
return text === "" ? ":\n\n" : `: ${text}
|
|
762
|
+
|
|
763
|
+
`;
|
|
764
|
+
}
|
|
765
|
+
|
|
395
766
|
// src/sse.ts
|
|
396
767
|
function parseSseFrame(frame) {
|
|
397
768
|
const dataLine = frame.split("\n").find((l) => l.startsWith("data:"))?.replace(/^data:\s*/, "");
|
|
398
|
-
if (dataLine === void 0
|
|
399
|
-
|
|
769
|
+
if (dataLine === void 0) return { type: "skip" };
|
|
770
|
+
return parseChatPayload(dataLine);
|
|
771
|
+
}
|
|
772
|
+
function parseChatPayload(data) {
|
|
773
|
+
if (data === "") return { type: "skip" };
|
|
774
|
+
if (data === "[DONE]") return { type: "done" };
|
|
400
775
|
try {
|
|
401
|
-
const obj = JSON.parse(
|
|
776
|
+
const obj = JSON.parse(data);
|
|
402
777
|
if (typeof obj.conversationId === "number") {
|
|
403
778
|
return { type: "conversationId", id: obj.conversationId };
|
|
404
779
|
}
|
|
@@ -411,16 +786,9 @@ function parseSseFrame(frame) {
|
|
|
411
786
|
}
|
|
412
787
|
return { type: "skip" };
|
|
413
788
|
}
|
|
414
|
-
|
|
415
|
-
if (!response.body) {
|
|
416
|
-
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
417
|
-
}
|
|
418
|
-
const reader = response.body.getReader();
|
|
419
|
-
const decoder = new TextDecoder();
|
|
420
|
-
let buffer = "";
|
|
789
|
+
function createChatDispatcher(callbacks) {
|
|
421
790
|
let errored = false;
|
|
422
|
-
|
|
423
|
-
const ev = parseSseFrame(frame);
|
|
791
|
+
return (ev) => {
|
|
424
792
|
switch (ev.type) {
|
|
425
793
|
case "conversationId":
|
|
426
794
|
callbacks.onConversationId?.(ev.id);
|
|
@@ -443,6 +811,16 @@ async function readSseStream(response, callbacks) {
|
|
|
443
811
|
}
|
|
444
812
|
return false;
|
|
445
813
|
};
|
|
814
|
+
}
|
|
815
|
+
async function readSseStream(response, callbacks) {
|
|
816
|
+
if (!response.body) {
|
|
817
|
+
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
818
|
+
}
|
|
819
|
+
const reader = response.body.getReader();
|
|
820
|
+
const decoder = new TextDecoder();
|
|
821
|
+
let buffer = "";
|
|
822
|
+
const deliver = createChatDispatcher(callbacks);
|
|
823
|
+
const dispatch = (frame) => deliver(parseSseFrame(frame));
|
|
446
824
|
try {
|
|
447
825
|
while (true) {
|
|
448
826
|
const { done, value } = await reader.read();
|
|
@@ -464,6 +842,14 @@ async function readSseStream(response, callbacks) {
|
|
|
464
842
|
reader.releaseLock();
|
|
465
843
|
}
|
|
466
844
|
}
|
|
845
|
+
async function readSseChatEvents(response, callbacks) {
|
|
846
|
+
const deliver = createChatDispatcher(callbacks);
|
|
847
|
+
await readSseEvents(response, {
|
|
848
|
+
onEvent: (event) => deliver(parseChatPayload(event.data.trim())),
|
|
849
|
+
// 레거시 readSseStream 의 EOF 잔여 버퍼 처리와 같게 — 미완성 이벤트도 디스패치
|
|
850
|
+
dispatchIncompleteAtEof: true
|
|
851
|
+
});
|
|
852
|
+
}
|
|
467
853
|
|
|
468
854
|
// src/jwt.ts
|
|
469
855
|
function decodeJwtPayload(token) {
|
|
@@ -523,22 +909,190 @@ function maskCardNumber(cardNumber) {
|
|
|
523
909
|
|
|
524
910
|
// src/datetime.ts
|
|
525
911
|
var pad = (n) => String(n).padStart(2, "0");
|
|
526
|
-
|
|
527
|
-
|
|
912
|
+
var zoneFormatters = /* @__PURE__ */ new Map();
|
|
913
|
+
function zoneFormatter(timeZone) {
|
|
914
|
+
let formatter = zoneFormatters.get(timeZone);
|
|
915
|
+
if (formatter === void 0) {
|
|
916
|
+
formatter = new Intl.DateTimeFormat("en-US-u-ca-gregory-nu-latn", {
|
|
917
|
+
timeZone,
|
|
918
|
+
hourCycle: "h23",
|
|
919
|
+
year: "numeric",
|
|
920
|
+
month: "2-digit",
|
|
921
|
+
day: "2-digit",
|
|
922
|
+
hour: "2-digit",
|
|
923
|
+
minute: "2-digit",
|
|
924
|
+
second: "2-digit"
|
|
925
|
+
});
|
|
926
|
+
zoneFormatters.set(timeZone, formatter);
|
|
927
|
+
}
|
|
928
|
+
return formatter;
|
|
929
|
+
}
|
|
930
|
+
function zonedFields(ms, timeZone) {
|
|
931
|
+
const fields = {
|
|
932
|
+
year: 0,
|
|
933
|
+
month: 0,
|
|
934
|
+
day: 0,
|
|
935
|
+
hour: 0,
|
|
936
|
+
minute: 0,
|
|
937
|
+
second: 0,
|
|
938
|
+
millisecond: (ms % 1e3 + 1e3) % 1e3
|
|
939
|
+
};
|
|
940
|
+
for (const part of zoneFormatter(timeZone).formatToParts(ms)) {
|
|
941
|
+
switch (part.type) {
|
|
942
|
+
case "year":
|
|
943
|
+
fields.year = Number(part.value);
|
|
944
|
+
break;
|
|
945
|
+
case "month":
|
|
946
|
+
fields.month = Number(part.value);
|
|
947
|
+
break;
|
|
948
|
+
case "day":
|
|
949
|
+
fields.day = Number(part.value);
|
|
950
|
+
break;
|
|
951
|
+
case "hour":
|
|
952
|
+
fields.hour = Number(part.value) % 24;
|
|
953
|
+
break;
|
|
954
|
+
case "minute":
|
|
955
|
+
fields.minute = Number(part.value);
|
|
956
|
+
break;
|
|
957
|
+
case "second":
|
|
958
|
+
fields.second = Number(part.value);
|
|
959
|
+
break;
|
|
960
|
+
default:
|
|
961
|
+
break;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return fields;
|
|
965
|
+
}
|
|
966
|
+
function wallAsUtcMs(f) {
|
|
967
|
+
const d = /* @__PURE__ */ new Date(0);
|
|
968
|
+
d.setUTCFullYear(f.year, f.month - 1, f.day);
|
|
969
|
+
d.setUTCHours(f.hour, f.minute, f.second, f.millisecond);
|
|
970
|
+
return d.getTime();
|
|
971
|
+
}
|
|
972
|
+
function offsetMinutes(ms, timeZone) {
|
|
973
|
+
const wall = zonedFields(ms, timeZone);
|
|
974
|
+
wall.millisecond = 0;
|
|
975
|
+
return (wallAsUtcMs(wall) - Math.floor(ms / 1e3) * 1e3) / 6e4;
|
|
976
|
+
}
|
|
977
|
+
var DAY_MS = 864e5;
|
|
978
|
+
function wallToEpoch(fields, timeZone) {
|
|
979
|
+
const wall = wallAsUtcMs(fields);
|
|
980
|
+
const toMs = (minutes) => Math.round(minutes * 6e4);
|
|
981
|
+
const before = offsetMinutes(wall - DAY_MS, timeZone);
|
|
982
|
+
const after = offsetMinutes(wall + DAY_MS, timeZone);
|
|
983
|
+
const atBefore = wall - toMs(before);
|
|
984
|
+
if (offsetMinutes(atBefore, timeZone) === before) return atBefore;
|
|
985
|
+
const atAfter = wall - toMs(after);
|
|
986
|
+
if (offsetMinutes(atAfter, timeZone) === after) return atAfter;
|
|
987
|
+
return atBefore;
|
|
988
|
+
}
|
|
989
|
+
function formatOffset(minutes) {
|
|
990
|
+
const totalSeconds = Math.round(minutes * 60);
|
|
991
|
+
if (totalSeconds === 0) return "Z";
|
|
992
|
+
const sign = totalSeconds < 0 ? "-" : "+";
|
|
993
|
+
const abs = Math.abs(totalSeconds);
|
|
994
|
+
const hh = pad(Math.floor(abs / 3600));
|
|
995
|
+
const mm = pad(Math.floor(abs % 3600 / 60));
|
|
996
|
+
const ss = abs % 60;
|
|
997
|
+
return `${sign}${hh}:${mm}${ss === 0 ? "" : `:${pad(ss)}`}`;
|
|
998
|
+
}
|
|
999
|
+
var formatWallDate = (f) => `${f.year}-${pad(f.month)}-${pad(f.day)}`;
|
|
1000
|
+
var formatWallDateTime = (f) => `${formatWallDate(f)}T${pad(f.hour)}:${pad(f.minute)}:${pad(f.second)}`;
|
|
1001
|
+
function assertValidDate(date) {
|
|
1002
|
+
const ms = date.getTime();
|
|
1003
|
+
if (Number.isNaN(ms)) throw new RangeError("Invalid time value");
|
|
1004
|
+
return ms;
|
|
1005
|
+
}
|
|
1006
|
+
function toWireDateTime(date, options) {
|
|
1007
|
+
const timeZone = options?.timeZone;
|
|
1008
|
+
const withOffset = options?.offset === true;
|
|
1009
|
+
if (timeZone === void 0) {
|
|
1010
|
+
const wall2 = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
1011
|
+
if (!withOffset) return wall2;
|
|
1012
|
+
assertValidDate(date);
|
|
1013
|
+
return wall2 + formatOffset(-date.getTimezoneOffset());
|
|
1014
|
+
}
|
|
1015
|
+
const ms = assertValidDate(date);
|
|
1016
|
+
const wall = formatWallDateTime(zonedFields(ms, timeZone));
|
|
1017
|
+
return withOffset ? wall + formatOffset(offsetMinutes(ms, timeZone)) : wall;
|
|
1018
|
+
}
|
|
1019
|
+
function toWireDate(date, options) {
|
|
1020
|
+
const timeZone = options?.timeZone;
|
|
1021
|
+
if (timeZone === void 0) {
|
|
1022
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
1023
|
+
}
|
|
1024
|
+
return formatWallDate(zonedFields(assertValidDate(date), timeZone));
|
|
1025
|
+
}
|
|
1026
|
+
var WALL_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?$/;
|
|
1027
|
+
function parseWallText(text, original) {
|
|
1028
|
+
const m = text.match(WALL_PATTERN);
|
|
1029
|
+
if (!m) throw new Error(`\uC62C\uBC14\uB978 datetime \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${original}`);
|
|
1030
|
+
const [, y, mo, day, h, mi, s = "0", frac = "0"] = m;
|
|
1031
|
+
return {
|
|
1032
|
+
year: Number(y),
|
|
1033
|
+
month: Number(mo),
|
|
1034
|
+
day: Number(day),
|
|
1035
|
+
hour: Number(h),
|
|
1036
|
+
minute: Number(mi),
|
|
1037
|
+
second: Number(s),
|
|
1038
|
+
millisecond: Number((frac + "000").slice(0, 3))
|
|
1039
|
+
// JS Date 는 ms 정밀도까지만
|
|
1040
|
+
};
|
|
528
1041
|
}
|
|
529
|
-
function
|
|
530
|
-
|
|
1042
|
+
function assertValidWall(f, original) {
|
|
1043
|
+
const d = new Date(wallAsUtcMs(f));
|
|
1044
|
+
if (d.getUTCFullYear() !== f.year || d.getUTCMonth() !== f.month - 1 || d.getUTCDate() !== f.day || d.getUTCHours() !== f.hour || d.getUTCMinutes() !== f.minute || d.getUTCSeconds() !== f.second) {
|
|
1045
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 datetime \uAC12\uC785\uB2C8\uB2E4: ${original}`);
|
|
1046
|
+
}
|
|
531
1047
|
}
|
|
532
|
-
function
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
const
|
|
539
|
-
|
|
540
|
-
const
|
|
541
|
-
|
|
1048
|
+
function matchRfc3339Offset(value) {
|
|
1049
|
+
const tIndex = value.indexOf("T");
|
|
1050
|
+
if (tIndex === -1) return null;
|
|
1051
|
+
const m = value.slice(tIndex).match(/(?:[Zz]|([+-])(\d{2}):(\d{2}))$/);
|
|
1052
|
+
if (!m || m.index === void 0) return null;
|
|
1053
|
+
const [token, sign, hh, mm] = m;
|
|
1054
|
+
const index = value.length - token.length;
|
|
1055
|
+
if (sign === void 0) return { index, minutes: 0 };
|
|
1056
|
+
const hours = Number(hh);
|
|
1057
|
+
const minutes = Number(mm);
|
|
1058
|
+
if (hours > 23 || minutes > 59) {
|
|
1059
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC624\uD504\uC14B\uC785\uB2C8\uB2E4: ${value}`);
|
|
1060
|
+
}
|
|
1061
|
+
return { index, minutes: (sign === "-" ? -1 : 1) * (hours * 60 + minutes) };
|
|
1062
|
+
}
|
|
1063
|
+
function parseWireDateTime(value, options) {
|
|
1064
|
+
const timeZone = options?.timeZone;
|
|
1065
|
+
const policy = options?.inboundOffset ?? "drop";
|
|
1066
|
+
if (policy !== "drop" && policy !== "convert" && policy !== "reject") {
|
|
1067
|
+
throw new RangeError(`\uC54C \uC218 \uC5C6\uB294 inboundOffset \uC815\uCC45\uC785\uB2C8\uB2E4: ${String(policy)}`);
|
|
1068
|
+
}
|
|
1069
|
+
if (timeZone !== void 0) zoneFormatter(timeZone);
|
|
1070
|
+
const trimmed = value.trim();
|
|
1071
|
+
let wallText = trimmed;
|
|
1072
|
+
let offset;
|
|
1073
|
+
if (policy === "drop") {
|
|
1074
|
+
wallText = stripZone(trimmed);
|
|
1075
|
+
} else {
|
|
1076
|
+
const found = matchRfc3339Offset(trimmed);
|
|
1077
|
+
if (found) {
|
|
1078
|
+
if (policy === "reject") {
|
|
1079
|
+
throw new RangeError(`\uC624\uD504\uC14B\uC774 \uBD99\uC740 datetime \uC740 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(inboundOffset: "reject"): ${value}`);
|
|
1080
|
+
}
|
|
1081
|
+
offset = found.minutes;
|
|
1082
|
+
wallText = trimmed.slice(0, found.index);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const f = parseWallText(wallText, value);
|
|
1086
|
+
if (offset !== void 0) {
|
|
1087
|
+
assertValidWall(f, value);
|
|
1088
|
+
return new Date(wallAsUtcMs(f) - offset * 6e4);
|
|
1089
|
+
}
|
|
1090
|
+
if (timeZone !== void 0) {
|
|
1091
|
+
assertValidWall(f, value);
|
|
1092
|
+
return new Date(wallToEpoch(f, timeZone));
|
|
1093
|
+
}
|
|
1094
|
+
const date = new Date(f.year, f.month - 1, f.day, f.hour, f.minute, f.second, f.millisecond);
|
|
1095
|
+
if (date.getFullYear() !== f.year || date.getMonth() !== f.month - 1 || date.getDate() !== f.day || date.getHours() !== f.hour || date.getMinutes() !== f.minute || date.getSeconds() !== f.second) {
|
|
542
1096
|
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 datetime \uAC12\uC785\uB2C8\uB2E4: ${value}`);
|
|
543
1097
|
}
|
|
544
1098
|
return date;
|
|
@@ -1095,25 +1649,22 @@ function extensionOf(fileName) {
|
|
|
1095
1649
|
return fileName.slice(dot + 1).toLowerCase();
|
|
1096
1650
|
}
|
|
1097
1651
|
function validateUpload(input) {
|
|
1652
|
+
const message = (key, ...args) => getMessage(key, { locale: input.locale, args, overrides: input.messages });
|
|
1098
1653
|
if (input.size > input.maxSizeBytes) {
|
|
1099
|
-
return { ok: false, reason: "size", message: "
|
|
1654
|
+
return { ok: false, reason: "size", message: message("rscc.upload.size") };
|
|
1100
1655
|
}
|
|
1101
1656
|
const ext = extensionOf(input.fileName);
|
|
1102
1657
|
const allowed = input.allowedExtensions.map(
|
|
1103
1658
|
(e) => (e.startsWith(".") ? e.slice(1) : e).toLowerCase()
|
|
1104
1659
|
);
|
|
1105
1660
|
if (ext === "" || !allowed.includes(ext)) {
|
|
1106
|
-
return { ok: false, reason: "extension", message:
|
|
1661
|
+
return { ok: false, reason: "extension", message: message("rscc.upload.extension", ext) };
|
|
1107
1662
|
}
|
|
1108
1663
|
const extra = input.extraMappings?.[ext];
|
|
1109
1664
|
const kinds = extra !== void 0 ? new Set(extra) : kindsForExtension(ext);
|
|
1110
1665
|
const kind = sniffFile(input.head);
|
|
1111
1666
|
if (kind === null || !kinds.has(kind)) {
|
|
1112
|
-
return {
|
|
1113
|
-
ok: false,
|
|
1114
|
-
reason: "content-mismatch",
|
|
1115
|
-
message: "\uD30C\uC77C \uB0B4\uC6A9\uC774 \uD655\uC7A5\uC790\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."
|
|
1116
|
-
};
|
|
1667
|
+
return { ok: false, reason: "content-mismatch", message: message("rscc.upload.contentMismatch") };
|
|
1117
1668
|
}
|
|
1118
1669
|
return { ok: true };
|
|
1119
1670
|
}
|
|
@@ -1785,6 +2336,8 @@ export {
|
|
|
1785
2336
|
CircuitOpenError,
|
|
1786
2337
|
DEFAULT_CSRF_COOKIE_NAME,
|
|
1787
2338
|
DEFAULT_CSRF_HEADER_NAME,
|
|
2339
|
+
FIXED_MESSAGE_KEYS,
|
|
2340
|
+
MESSAGES,
|
|
1788
2341
|
ResultCode,
|
|
1789
2342
|
WEBHOOK_SIGNATURE_HEADER,
|
|
1790
2343
|
abbreviateAmount,
|
|
@@ -1802,16 +2355,22 @@ export {
|
|
|
1802
2355
|
createBusinessDays,
|
|
1803
2356
|
createCircuitBreaker,
|
|
1804
2357
|
createFeatureFlags,
|
|
2358
|
+
createSseEventParser,
|
|
1805
2359
|
createTokenBucket,
|
|
1806
2360
|
createTtlCache,
|
|
1807
2361
|
csrfHeaderFor,
|
|
1808
2362
|
decodeJwtPayload,
|
|
1809
2363
|
decomposeHangul,
|
|
2364
|
+
formatMessage,
|
|
1810
2365
|
formatPhoneNumber,
|
|
2366
|
+
formatSseComment,
|
|
2367
|
+
formatSseEvent,
|
|
1811
2368
|
generateIdempotencyKey,
|
|
2369
|
+
getMessage,
|
|
1812
2370
|
getTokenExpiry,
|
|
1813
2371
|
isBulkResult,
|
|
1814
2372
|
isChosungQuery,
|
|
2373
|
+
isCommonResultCode,
|
|
1815
2374
|
isForeignerRrn,
|
|
1816
2375
|
isRetryableStatus,
|
|
1817
2376
|
isTokenExpired,
|
|
@@ -1827,16 +2386,22 @@ export {
|
|
|
1827
2386
|
maskPhone,
|
|
1828
2387
|
maskSecret,
|
|
1829
2388
|
matchesHangul,
|
|
2389
|
+
negotiateLanguage,
|
|
1830
2390
|
normalizeBusinessNumber,
|
|
1831
2391
|
normalizePhoneNumber,
|
|
1832
2392
|
normalizeRrn,
|
|
2393
|
+
parseChatPayload,
|
|
1833
2394
|
parseFlag,
|
|
1834
2395
|
parseRetryAfterMs,
|
|
2396
|
+
parseSseEvents,
|
|
1835
2397
|
parseSseFrame,
|
|
1836
2398
|
parseWireDateTime,
|
|
1837
2399
|
pickJosa,
|
|
1838
2400
|
readCookie,
|
|
2401
|
+
readSseChatEvents,
|
|
2402
|
+
readSseEvents,
|
|
1839
2403
|
readSseStream,
|
|
2404
|
+
resultCodeForStatus,
|
|
1840
2405
|
retry,
|
|
1841
2406
|
rrnBirthDate,
|
|
1842
2407
|
rrnChecksumOkLegacy,
|
|
@@ -1845,6 +2410,7 @@ export {
|
|
|
1845
2410
|
sniffFile,
|
|
1846
2411
|
stripZone,
|
|
1847
2412
|
toChosung,
|
|
2413
|
+
toCommonResultCode,
|
|
1848
2414
|
toE164,
|
|
1849
2415
|
toFormalNotation,
|
|
1850
2416
|
toKoreanWords,
|