@rscc/common-core 0.3.0 → 0.5.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 +219 -14
- package/dist/index.cjs +543 -23
- package/dist/index.d.cts +454 -9
- package/dist/index.d.ts +454 -9
- package/dist/index.js +523 -23
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,10 @@ __export(index_exports, {
|
|
|
23
23
|
ApiError: () => ApiError,
|
|
24
24
|
BulkheadFullError: () => BulkheadFullError,
|
|
25
25
|
CircuitOpenError: () => CircuitOpenError,
|
|
26
|
+
DEFAULT_CSRF_COOKIE_NAME: () => DEFAULT_CSRF_COOKIE_NAME,
|
|
27
|
+
DEFAULT_CSRF_HEADER_NAME: () => DEFAULT_CSRF_HEADER_NAME,
|
|
28
|
+
FIXED_MESSAGE_KEYS: () => FIXED_MESSAGE_KEYS,
|
|
29
|
+
MESSAGES: () => MESSAGES,
|
|
26
30
|
ResultCode: () => ResultCode,
|
|
27
31
|
WEBHOOK_SIGNATURE_HEADER: () => WEBHOOK_SIGNATURE_HEADER,
|
|
28
32
|
abbreviateAmount: () => abbreviateAmount,
|
|
@@ -40,18 +44,26 @@ __export(index_exports, {
|
|
|
40
44
|
createBusinessDays: () => createBusinessDays,
|
|
41
45
|
createCircuitBreaker: () => createCircuitBreaker,
|
|
42
46
|
createFeatureFlags: () => createFeatureFlags,
|
|
47
|
+
createSseEventParser: () => createSseEventParser,
|
|
43
48
|
createTokenBucket: () => createTokenBucket,
|
|
44
49
|
createTtlCache: () => createTtlCache,
|
|
50
|
+
csrfHeaderFor: () => csrfHeaderFor,
|
|
45
51
|
decodeJwtPayload: () => decodeJwtPayload,
|
|
46
52
|
decomposeHangul: () => decomposeHangul,
|
|
53
|
+
formatMessage: () => formatMessage,
|
|
47
54
|
formatPhoneNumber: () => formatPhoneNumber,
|
|
55
|
+
formatSseComment: () => formatSseComment,
|
|
56
|
+
formatSseEvent: () => formatSseEvent,
|
|
48
57
|
generateIdempotencyKey: () => generateIdempotencyKey,
|
|
58
|
+
getMessage: () => getMessage,
|
|
49
59
|
getTokenExpiry: () => getTokenExpiry,
|
|
50
60
|
isBulkResult: () => isBulkResult,
|
|
51
61
|
isChosungQuery: () => isChosungQuery,
|
|
62
|
+
isCommonResultCode: () => isCommonResultCode,
|
|
52
63
|
isForeignerRrn: () => isForeignerRrn,
|
|
53
64
|
isRetryableStatus: () => isRetryableStatus,
|
|
54
65
|
isTokenExpired: () => isTokenExpired,
|
|
66
|
+
isUnsafeMethod: () => isUnsafeMethod,
|
|
55
67
|
isValidBusinessNumber: () => isValidBusinessNumber,
|
|
56
68
|
isValidCorporateNumber: () => isValidCorporateNumber,
|
|
57
69
|
isValidRrn: () => isValidRrn,
|
|
@@ -63,15 +75,22 @@ __export(index_exports, {
|
|
|
63
75
|
maskPhone: () => maskPhone,
|
|
64
76
|
maskSecret: () => maskSecret,
|
|
65
77
|
matchesHangul: () => matchesHangul,
|
|
78
|
+
negotiateLanguage: () => negotiateLanguage,
|
|
66
79
|
normalizeBusinessNumber: () => normalizeBusinessNumber,
|
|
67
80
|
normalizePhoneNumber: () => normalizePhoneNumber,
|
|
68
81
|
normalizeRrn: () => normalizeRrn,
|
|
82
|
+
parseChatPayload: () => parseChatPayload,
|
|
69
83
|
parseFlag: () => parseFlag,
|
|
70
84
|
parseRetryAfterMs: () => parseRetryAfterMs,
|
|
85
|
+
parseSseEvents: () => parseSseEvents,
|
|
71
86
|
parseSseFrame: () => parseSseFrame,
|
|
72
87
|
parseWireDateTime: () => parseWireDateTime,
|
|
73
88
|
pickJosa: () => pickJosa,
|
|
89
|
+
readCookie: () => readCookie,
|
|
90
|
+
readSseChatEvents: () => readSseChatEvents,
|
|
91
|
+
readSseEvents: () => readSseEvents,
|
|
74
92
|
readSseStream: () => readSseStream,
|
|
93
|
+
resultCodeForStatus: () => resultCodeForStatus,
|
|
75
94
|
retry: () => retry,
|
|
76
95
|
rrnBirthDate: () => rrnBirthDate,
|
|
77
96
|
rrnChecksumOkLegacy: () => rrnChecksumOkLegacy,
|
|
@@ -80,6 +99,7 @@ __export(index_exports, {
|
|
|
80
99
|
sniffFile: () => sniffFile,
|
|
81
100
|
stripZone: () => stripZone,
|
|
82
101
|
toChosung: () => toChosung,
|
|
102
|
+
toCommonResultCode: () => toCommonResultCode,
|
|
83
103
|
toE164: () => toE164,
|
|
84
104
|
toFormalNotation: () => toFormalNotation,
|
|
85
105
|
toKoreanWords: () => toKoreanWords,
|
|
@@ -112,6 +132,108 @@ var ResultCode = {
|
|
|
112
132
|
INTERNAL_SERVER_ERROR: "500"
|
|
113
133
|
};
|
|
114
134
|
|
|
135
|
+
// src/errorCodes.ts
|
|
136
|
+
var COMMON_CODES = new Set(Object.values(ResultCode));
|
|
137
|
+
function isCommonResultCode(code) {
|
|
138
|
+
return COMMON_CODES.has(code);
|
|
139
|
+
}
|
|
140
|
+
function resultCodeForStatus(status) {
|
|
141
|
+
const exact = String(status);
|
|
142
|
+
if (exact !== ResultCode.SUCCESS && COMMON_CODES.has(exact)) return exact;
|
|
143
|
+
if (status >= 400 && status < 500) return ResultCode.BAD_REQUEST;
|
|
144
|
+
return ResultCode.INTERNAL_SERVER_ERROR;
|
|
145
|
+
}
|
|
146
|
+
function toCommonResultCode(code, status) {
|
|
147
|
+
return isCommonResultCode(code) ? code : resultCodeForStatus(status);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/csrf.ts
|
|
151
|
+
var DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
|
|
152
|
+
var DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
|
|
153
|
+
var SAFE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS", "TRACE"]);
|
|
154
|
+
var SENTINEL_ORIGIN = "http://rscc-csrf.invalid";
|
|
155
|
+
var ABSOLUTE_URL = /^([a-zA-Z][a-zA-Z\d+.-]*):\/\/([^/?#\\]*)/;
|
|
156
|
+
var HAS_SCHEME = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
|
|
157
|
+
var PROTOCOL_RELATIVE = /^[\\/]{2}/;
|
|
158
|
+
var DEFAULT_PORTS = { http: "80", https: "443", ws: "80", wss: "443" };
|
|
159
|
+
function readCookie(name, cookieString) {
|
|
160
|
+
const source = cookieString ?? documentCookie();
|
|
161
|
+
if (!source) return null;
|
|
162
|
+
for (const part of source.split(";")) {
|
|
163
|
+
const pair = part.trim();
|
|
164
|
+
const eq = pair.indexOf("=");
|
|
165
|
+
if (eq < 0) continue;
|
|
166
|
+
if (pair.slice(0, eq) === name) return pair.slice(eq + 1);
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function isUnsafeMethod(method) {
|
|
171
|
+
return !SAFE_METHODS.has((method || "GET").toUpperCase());
|
|
172
|
+
}
|
|
173
|
+
function csrfHeaderFor(url, method, options) {
|
|
174
|
+
if (!isUnsafeMethod(method)) return null;
|
|
175
|
+
const opts = options === true || options === void 0 ? {} : options;
|
|
176
|
+
const cookieString = opts.readCookie ? opts.readCookie() : documentCookie();
|
|
177
|
+
if (!cookieString) return null;
|
|
178
|
+
const token = readCookie(opts.cookieName ?? DEFAULT_CSRF_COOKIE_NAME, cookieString);
|
|
179
|
+
if (!token) return null;
|
|
180
|
+
if (!isAllowedTarget(url, opts.allowedOrigins)) return null;
|
|
181
|
+
return [opts.headerName ?? DEFAULT_CSRF_HEADER_NAME, token];
|
|
182
|
+
}
|
|
183
|
+
function documentCookie() {
|
|
184
|
+
if (typeof document === "undefined") return null;
|
|
185
|
+
try {
|
|
186
|
+
return typeof document.cookie === "string" ? document.cookie : null;
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function currentLocation() {
|
|
192
|
+
if (typeof location === "undefined") return null;
|
|
193
|
+
try {
|
|
194
|
+
const { href, origin } = location;
|
|
195
|
+
return typeof href === "string" && typeof origin === "string" ? { href, origin } : null;
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function isAllowedTarget(url, allowedOrigins) {
|
|
201
|
+
const loc = currentLocation();
|
|
202
|
+
const base = loc ? loc.href : SENTINEL_ORIGIN;
|
|
203
|
+
const selfOrigin = loc ? loc.origin : SENTINEL_ORIGIN;
|
|
204
|
+
const target = originOf(url, base, selfOrigin);
|
|
205
|
+
if (target === null || target === "null") return false;
|
|
206
|
+
if (selfOrigin !== "null" && target === selfOrigin) return true;
|
|
207
|
+
if (!allowedOrigins || allowedOrigins.length === 0) return false;
|
|
208
|
+
return allowedOrigins.some((entry) => originOf(entry, base, selfOrigin) === target);
|
|
209
|
+
}
|
|
210
|
+
function originOf(url, base, baseOrigin) {
|
|
211
|
+
try {
|
|
212
|
+
if (typeof URL === "function") {
|
|
213
|
+
const origin = new URL(url, base).origin;
|
|
214
|
+
if (typeof origin === "string") return origin;
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
return looseOriginOf(url, baseOrigin);
|
|
219
|
+
}
|
|
220
|
+
function looseOriginOf(url, baseOrigin) {
|
|
221
|
+
const trimmed = url.trim();
|
|
222
|
+
const m = ABSOLUTE_URL.exec(trimmed);
|
|
223
|
+
if (m) {
|
|
224
|
+
const scheme = m[1].toLowerCase();
|
|
225
|
+
const authority = m[2];
|
|
226
|
+
const hostPort = authority.slice(authority.lastIndexOf("@") + 1).toLowerCase();
|
|
227
|
+
const hp = /^(.*?)(?::(\d*))?$/.exec(hostPort);
|
|
228
|
+
const host = hp?.[1] ?? "";
|
|
229
|
+
const port = hp?.[2] ?? "";
|
|
230
|
+
if (!host) return null;
|
|
231
|
+
return port === "" || port === DEFAULT_PORTS[scheme] ? `${scheme}://${host}` : `${scheme}://${host}:${port}`;
|
|
232
|
+
}
|
|
233
|
+
if (PROTOCOL_RELATIVE.test(trimmed) || HAS_SCHEME.test(trimmed)) return null;
|
|
234
|
+
return baseOrigin;
|
|
235
|
+
}
|
|
236
|
+
|
|
115
237
|
// src/idempotency.ts
|
|
116
238
|
var FALLBACK_GROUPS = [8, 4, 4, 4, 12];
|
|
117
239
|
function generateIdempotencyKey() {
|
|
@@ -124,6 +246,141 @@ function generateIdempotencyKey() {
|
|
|
124
246
|
}).join("-");
|
|
125
247
|
}
|
|
126
248
|
|
|
249
|
+
// src/messages.ts
|
|
250
|
+
var KO = Object.freeze({
|
|
251
|
+
"rscc.result.SUCCESS": "\uC694\uCCAD\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uCC98\uB9AC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
252
|
+
"rscc.result.BAD_REQUEST": "\uC798\uBABB\uB41C \uC694\uCCAD\uC785\uB2C8\uB2E4.",
|
|
253
|
+
"rscc.result.UNAUTHORIZED": "\uC778\uC99D\uB418\uC9C0 \uC54A\uC740 \uC0AC\uC6A9\uC790\uC785\uB2C8\uB2E4.",
|
|
254
|
+
"rscc.result.FORBIDDEN": "\uC811\uADFC \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
255
|
+
"rscc.result.NOT_FOUND": "\uB9AC\uC18C\uC2A4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
256
|
+
"rscc.result.METHOD_NOT_ALLOWED": "\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC740 \uC694\uCCAD \uBC29\uC2DD\uC785\uB2C8\uB2E4.",
|
|
257
|
+
"rscc.result.CONFLICT": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uB9AC\uC18C\uC2A4\uC785\uB2C8\uB2E4.",
|
|
258
|
+
"rscc.result.UNSUPPORTED_MEDIA_TYPE": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC694\uCCAD \uD615\uC2DD\uC785\uB2C8\uB2E4.",
|
|
259
|
+
"rscc.result.TOO_MANY_REQUESTS": "\uC694\uCCAD \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
260
|
+
"rscc.result.INTERNAL_SERVER_ERROR": "\uC11C\uBC84 \uB0B4\uBD80 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.",
|
|
261
|
+
"rscc.web.missingParameter": "\uD544\uC218 \uC694\uCCAD \uD30C\uB77C\uBBF8\uD130\uAC00 \uB204\uB77D\uB418\uC5C8\uC2B5\uB2C8\uB2E4: {0}",
|
|
262
|
+
"rscc.web.typeMismatch": "\uC694\uCCAD \uD30C\uB77C\uBBF8\uD130 \uD0C0\uC785\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {0}",
|
|
263
|
+
"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.",
|
|
264
|
+
"rscc.idempotency.conflict": "\uC774\uBBF8 \uCC98\uB9AC\uB418\uC5C8\uAC70\uB098 \uCC98\uB9AC \uC911\uC778 \uC694\uCCAD\uC785\uB2C8\uB2E4.",
|
|
265
|
+
"rscc.idempotency.invalidFormat": "Idempotency-Key \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
266
|
+
"rscc.idempotency.required": "Idempotency-Key \uD5E4\uB354\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
267
|
+
"rscc.upload.size": "\uC5C5\uB85C\uB4DC \uAC00\uB2A5\uD55C \uCD5C\uB300 \uD06C\uAE30\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
268
|
+
"rscc.upload.extension": "\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4: {0}",
|
|
269
|
+
"rscc.upload.contentMismatch": "\uD30C\uC77C \uB0B4\uC6A9\uC774 \uD655\uC7A5\uC790\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
270
|
+
"rscc.query.sortField": "\uC815\uB82C\uD560 \uC218 \uC5C6\uB294 \uD544\uB4DC\uC785\uB2C8\uB2E4: {0}",
|
|
271
|
+
"rscc.query.sortDirection": "\uC815\uB82C \uBC29\uD5A5\uC740 asc|desc \uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4: {0}",
|
|
272
|
+
"rscc.security.csrf": "CSRF \uD1A0\uD070\uC774 \uC5C6\uAC70\uB098 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
273
|
+
"rscc.client.unreadableBody": "\uC751\uB2F5 \uBCF8\uBB38\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
274
|
+
"rscc.client.invalidJson": "\uC751\uB2F5 \uBCF8\uBB38\uC774 \uC720\uD6A8\uD55C JSON \uC774 \uC544\uB2D9\uB2C8\uB2E4.",
|
|
275
|
+
"rscc.client.httpError": "API \uC624\uB958: {0} {1}"
|
|
276
|
+
});
|
|
277
|
+
var EN = Object.freeze({
|
|
278
|
+
"rscc.result.SUCCESS": "The request was processed successfully.",
|
|
279
|
+
"rscc.result.BAD_REQUEST": "Bad request.",
|
|
280
|
+
"rscc.result.UNAUTHORIZED": "Authentication is required.",
|
|
281
|
+
"rscc.result.FORBIDDEN": "Access is denied.",
|
|
282
|
+
"rscc.result.NOT_FOUND": "Resource not found.",
|
|
283
|
+
"rscc.result.METHOD_NOT_ALLOWED": "Request method is not allowed.",
|
|
284
|
+
"rscc.result.CONFLICT": "The resource already exists.",
|
|
285
|
+
"rscc.result.UNSUPPORTED_MEDIA_TYPE": "Unsupported request content type.",
|
|
286
|
+
"rscc.result.TOO_MANY_REQUESTS": "Too many requests.",
|
|
287
|
+
"rscc.result.INTERNAL_SERVER_ERROR": "An internal server error occurred.",
|
|
288
|
+
"rscc.web.missingParameter": "Required request parameter is missing: {0}",
|
|
289
|
+
"rscc.web.typeMismatch": "Request parameter has an invalid type: {0}",
|
|
290
|
+
"rscc.web.unreadableBody": "The request body could not be read. Check the JSON syntax and field types.",
|
|
291
|
+
"rscc.idempotency.conflict": "The request has already been processed or is in progress.",
|
|
292
|
+
"rscc.idempotency.invalidFormat": "Invalid Idempotency-Key format.",
|
|
293
|
+
"rscc.idempotency.required": "The Idempotency-Key header is required.",
|
|
294
|
+
"rscc.upload.size": "The upload exceeds the maximum allowed size.",
|
|
295
|
+
"rscc.upload.extension": "File type is not allowed: {0}",
|
|
296
|
+
"rscc.upload.contentMismatch": "File content does not match its extension.",
|
|
297
|
+
"rscc.query.sortField": "Field cannot be sorted: {0}",
|
|
298
|
+
"rscc.query.sortDirection": "Sort direction must be asc or desc: {0}",
|
|
299
|
+
"rscc.security.csrf": "The CSRF token is missing or invalid.",
|
|
300
|
+
"rscc.client.unreadableBody": "Failed to read the response body.",
|
|
301
|
+
"rscc.client.invalidJson": "The response body is not valid JSON.",
|
|
302
|
+
"rscc.client.httpError": "API error: {0} {1}"
|
|
303
|
+
});
|
|
304
|
+
var MESSAGES = Object.freeze({ ko: KO, en: EN });
|
|
305
|
+
var FIXED_KEY_LIST = [
|
|
306
|
+
"rscc.idempotency.conflict",
|
|
307
|
+
"rscc.idempotency.invalidFormat",
|
|
308
|
+
"rscc.idempotency.required",
|
|
309
|
+
"rscc.query.sortField",
|
|
310
|
+
"rscc.query.sortDirection",
|
|
311
|
+
"rscc.security.csrf"
|
|
312
|
+
];
|
|
313
|
+
var FIXED_KEYS = new Set(FIXED_KEY_LIST);
|
|
314
|
+
var FIXED_MESSAGE_KEYS = new Set(FIXED_KEY_LIST);
|
|
315
|
+
var PLACEHOLDER = /\{(\d+)\}/g;
|
|
316
|
+
function formatMessage(template, ...args) {
|
|
317
|
+
if (args.length === 0) return template;
|
|
318
|
+
return template.replace(PLACEHOLDER, (match, digits) => {
|
|
319
|
+
const index = Number(digits);
|
|
320
|
+
return index < args.length ? String(args[index]) : match;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function primarySubtag(tag) {
|
|
324
|
+
if (tag === void 0) return "";
|
|
325
|
+
const trimmed = tag.trim();
|
|
326
|
+
const dash = trimmed.indexOf("-");
|
|
327
|
+
return (dash < 0 ? trimmed : trimmed.slice(0, dash)).toLowerCase();
|
|
328
|
+
}
|
|
329
|
+
function builtinCatalog(language) {
|
|
330
|
+
return language === "ko" || language === "en" ? MESSAGES[language] : void 0;
|
|
331
|
+
}
|
|
332
|
+
function lookupBuiltin(language, key) {
|
|
333
|
+
const catalog = builtinCatalog(language);
|
|
334
|
+
return catalog !== void 0 && Object.prototype.hasOwnProperty.call(catalog, key) ? catalog[key] : void 0;
|
|
335
|
+
}
|
|
336
|
+
function lookupOverride(overrides, key, language) {
|
|
337
|
+
if (overrides === void 0) return void 0;
|
|
338
|
+
const value = typeof overrides === "function" ? overrides(key, language) : Object.prototype.hasOwnProperty.call(overrides, key) ? overrides[key] : void 0;
|
|
339
|
+
return typeof value === "string" && value !== "" && value !== key ? value : void 0;
|
|
340
|
+
}
|
|
341
|
+
function getMessage(key, options = {}) {
|
|
342
|
+
const defaultLanguage = primarySubtag(options.defaultLocale) || "ko";
|
|
343
|
+
const language = primarySubtag(options.locale) || defaultLanguage;
|
|
344
|
+
const args = options.args ?? [];
|
|
345
|
+
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;
|
|
346
|
+
return formatMessage(template, ...args);
|
|
347
|
+
}
|
|
348
|
+
var LANGUAGE_RANGE = /^(?:\*|[A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*)$/;
|
|
349
|
+
var Q_VALUE = /^(?:\d+(?:\.\d*)?|\.\d+)$/;
|
|
350
|
+
function negotiateLanguage(acceptLanguage, supported = ["ko", "en"], fallback = "ko") {
|
|
351
|
+
if (acceptLanguage == null || acceptLanguage.trim() === "") return fallback;
|
|
352
|
+
const supportedSet = new Set(supported.map((s) => primarySubtag(s)).filter((s) => s !== ""));
|
|
353
|
+
const ranges = [];
|
|
354
|
+
for (const item of acceptLanguage.split(",")) {
|
|
355
|
+
const [rawTag = "", ...params] = item.split(";");
|
|
356
|
+
const tag = rawTag.trim();
|
|
357
|
+
if (!LANGUAGE_RANGE.test(tag)) continue;
|
|
358
|
+
let q = 1;
|
|
359
|
+
let valid = true;
|
|
360
|
+
for (const param of params) {
|
|
361
|
+
const eq = param.indexOf("=");
|
|
362
|
+
const name = (eq < 0 ? param : param.slice(0, eq)).trim().toLowerCase();
|
|
363
|
+
if (name !== "q") continue;
|
|
364
|
+
const value = eq < 0 ? "" : param.slice(eq + 1).trim();
|
|
365
|
+
const parsed = Number(value);
|
|
366
|
+
if (!Q_VALUE.test(value) || parsed < 0 || parsed > 1) {
|
|
367
|
+
valid = false;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
q = parsed;
|
|
371
|
+
}
|
|
372
|
+
if (!valid || q === 0) continue;
|
|
373
|
+
ranges.push({ tag, q });
|
|
374
|
+
}
|
|
375
|
+
ranges.sort((a, b) => b.q - a.q);
|
|
376
|
+
for (const { tag } of ranges) {
|
|
377
|
+
if (tag === "*") return fallback;
|
|
378
|
+
const language = primarySubtag(tag);
|
|
379
|
+
if (supportedSet.has(language)) return language;
|
|
380
|
+
}
|
|
381
|
+
return fallback;
|
|
382
|
+
}
|
|
383
|
+
|
|
127
384
|
// src/retry.ts
|
|
128
385
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
129
386
|
function isRetryableStatus(status) {
|
|
@@ -209,6 +466,7 @@ var ApiError = class extends Error {
|
|
|
209
466
|
super(args.message);
|
|
210
467
|
this.name = "ApiError";
|
|
211
468
|
this.code = args.code;
|
|
469
|
+
this.commonCode = toCommonResultCode(args.code, args.status);
|
|
212
470
|
this.status = args.status;
|
|
213
471
|
this.traceId = args.traceId;
|
|
214
472
|
this.data = args.data;
|
|
@@ -240,6 +498,8 @@ function isCommonResponse(value) {
|
|
|
240
498
|
}
|
|
241
499
|
function createApiClient(config) {
|
|
242
500
|
const traceIdHeader = config.traceIdHeader ?? "X-Trace-Id";
|
|
501
|
+
const clientMessage = (key, ...args) => getMessage(key, { locale: config.locale, args });
|
|
502
|
+
const acceptLanguage = config.sendAcceptLanguage && config.locale && config.locale.trim() !== "" ? config.locale.trim() : null;
|
|
243
503
|
let hookWarned = false;
|
|
244
504
|
const safeHook = (fn) => {
|
|
245
505
|
try {
|
|
@@ -255,6 +515,8 @@ function createApiClient(config) {
|
|
|
255
515
|
async function requestWithMeta(path, init = {}) {
|
|
256
516
|
const sentTraceId = generateTraceId();
|
|
257
517
|
const method = (init.method ?? "GET").toUpperCase();
|
|
518
|
+
const url = joinUrl(config.baseUrl, path);
|
|
519
|
+
const credentials = init.credentials ?? config.credentials;
|
|
258
520
|
const idempotencyHeader = config.idempotency?.header ?? "Idempotency-Key";
|
|
259
521
|
const idempotencyKey = config.idempotency && (config.idempotency.methods ?? DEFAULT_IDEMPOTENCY_METHODS).includes(method) ? generateIdempotencyKey() : null;
|
|
260
522
|
const attemptOnce = async () => {
|
|
@@ -266,12 +528,23 @@ function createApiClient(config) {
|
|
|
266
528
|
if (!headers.has("Content-Type") && typeof init.body === "string") {
|
|
267
529
|
headers.set("Content-Type", "application/json");
|
|
268
530
|
}
|
|
531
|
+
if (acceptLanguage !== null && !headers.has("Accept-Language")) {
|
|
532
|
+
headers.set("Accept-Language", acceptLanguage);
|
|
533
|
+
}
|
|
269
534
|
if (config.getToken && !headers.has("Authorization")) {
|
|
270
535
|
const token = config.getToken();
|
|
271
536
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
272
537
|
}
|
|
538
|
+
if (config.csrf) {
|
|
539
|
+
const csrfHeader = csrfHeaderFor(url, method, config.csrf);
|
|
540
|
+
if (csrfHeader && !headers.has(csrfHeader[0])) headers.set(csrfHeader[0], csrfHeader[1]);
|
|
541
|
+
}
|
|
273
542
|
const fetchFn = config.fetchImpl ?? globalThis.fetch;
|
|
274
|
-
const response = await fetchFn(
|
|
543
|
+
const response = await fetchFn(url, {
|
|
544
|
+
...init,
|
|
545
|
+
...credentials !== void 0 ? { credentials } : {},
|
|
546
|
+
headers
|
|
547
|
+
});
|
|
275
548
|
const traceId = response.headers.get(traceIdHeader) ?? sentTraceId;
|
|
276
549
|
let raw = null;
|
|
277
550
|
try {
|
|
@@ -281,7 +554,7 @@ function createApiClient(config) {
|
|
|
281
554
|
if (config.strictJson && response.ok) {
|
|
282
555
|
throw new ApiError({
|
|
283
556
|
code: "INVALID_JSON",
|
|
284
|
-
message: "
|
|
557
|
+
message: clientMessage("rscc.client.unreadableBody"),
|
|
285
558
|
status: response.status,
|
|
286
559
|
traceId,
|
|
287
560
|
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After"))
|
|
@@ -300,7 +573,7 @@ function createApiClient(config) {
|
|
|
300
573
|
if (config.strictJson && response.ok) {
|
|
301
574
|
throw new ApiError({
|
|
302
575
|
code: "INVALID_JSON",
|
|
303
|
-
message: "
|
|
576
|
+
message: clientMessage("rscc.client.invalidJson"),
|
|
304
577
|
status: response.status,
|
|
305
578
|
traceId,
|
|
306
579
|
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After")),
|
|
@@ -329,7 +602,7 @@ function createApiClient(config) {
|
|
|
329
602
|
}
|
|
330
603
|
if (!response.ok) {
|
|
331
604
|
const j2 = json;
|
|
332
|
-
const message = typeof j2?.message === "string" && j2.message || typeof j2?.error === "string" && j2.error ||
|
|
605
|
+
const message = typeof j2?.message === "string" && j2.message || typeof j2?.error === "string" && j2.error || clientMessage("rscc.client.httpError", response.status, response.statusText);
|
|
333
606
|
fail(String(response.status), message);
|
|
334
607
|
}
|
|
335
608
|
return { data: json, traceId, status: response.status, response };
|
|
@@ -387,13 +660,232 @@ function createApiClient(config) {
|
|
|
387
660
|
return { request, requestWithMeta };
|
|
388
661
|
}
|
|
389
662
|
|
|
663
|
+
// src/sseEvents.ts
|
|
664
|
+
var RETRY_PATTERN = /^[0-9]+$/;
|
|
665
|
+
function createParser(onEvent, onRetry, haltOnTrue, initialLastEventId) {
|
|
666
|
+
let pending = "";
|
|
667
|
+
let bomChecked = false;
|
|
668
|
+
let skipLeadingLf = false;
|
|
669
|
+
let data = "";
|
|
670
|
+
let eventType = "";
|
|
671
|
+
let idBuffer = initialLastEventId;
|
|
672
|
+
let lastEventId = initialLastEventId;
|
|
673
|
+
let retryMs = null;
|
|
674
|
+
let ended = false;
|
|
675
|
+
let halted = false;
|
|
676
|
+
const lineBreak = /[\r\n]/g;
|
|
677
|
+
const dispatch = () => {
|
|
678
|
+
lastEventId = idBuffer;
|
|
679
|
+
if (data === "") {
|
|
680
|
+
eventType = "";
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const event = {
|
|
684
|
+
event: eventType === "" ? "message" : eventType,
|
|
685
|
+
data: data.endsWith("\n") ? data.slice(0, -1) : data,
|
|
686
|
+
id: lastEventId
|
|
687
|
+
};
|
|
688
|
+
data = "";
|
|
689
|
+
eventType = "";
|
|
690
|
+
if (onEvent(event) === true && haltOnTrue) halted = true;
|
|
691
|
+
};
|
|
692
|
+
const processLine = (line) => {
|
|
693
|
+
if (line === "") {
|
|
694
|
+
dispatch();
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
if (line.charCodeAt(0) === 58) return;
|
|
698
|
+
const colon = line.indexOf(":");
|
|
699
|
+
let field;
|
|
700
|
+
let value;
|
|
701
|
+
if (colon === -1) {
|
|
702
|
+
field = line;
|
|
703
|
+
value = "";
|
|
704
|
+
} else {
|
|
705
|
+
field = line.slice(0, colon);
|
|
706
|
+
value = line.slice(colon + 1);
|
|
707
|
+
if (value.charCodeAt(0) === 32) value = value.slice(1);
|
|
708
|
+
}
|
|
709
|
+
switch (field) {
|
|
710
|
+
case "event":
|
|
711
|
+
eventType = value;
|
|
712
|
+
break;
|
|
713
|
+
case "data":
|
|
714
|
+
data += value + "\n";
|
|
715
|
+
break;
|
|
716
|
+
case "id":
|
|
717
|
+
if (!value.includes("\0")) idBuffer = value;
|
|
718
|
+
break;
|
|
719
|
+
case "retry":
|
|
720
|
+
if (RETRY_PATTERN.test(value)) {
|
|
721
|
+
retryMs = Number(value);
|
|
722
|
+
onRetry?.(retryMs);
|
|
723
|
+
}
|
|
724
|
+
break;
|
|
725
|
+
default:
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
const feed = (chunk) => {
|
|
730
|
+
if (ended) throw new Error("SSE \uD30C\uC11C\uAC00 \uC774\uBBF8 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4(end \uC774\uD6C4 feed).");
|
|
731
|
+
if (halted || chunk === "") return;
|
|
732
|
+
let text = chunk;
|
|
733
|
+
if (!bomChecked) {
|
|
734
|
+
bomChecked = true;
|
|
735
|
+
if (text.charCodeAt(0) === 65279) text = text.slice(1);
|
|
736
|
+
}
|
|
737
|
+
if (skipLeadingLf) {
|
|
738
|
+
skipLeadingLf = false;
|
|
739
|
+
if (text.charCodeAt(0) === 10) text = text.slice(1);
|
|
740
|
+
}
|
|
741
|
+
let start = 0;
|
|
742
|
+
for (; ; ) {
|
|
743
|
+
lineBreak.lastIndex = start;
|
|
744
|
+
const match = lineBreak.exec(text);
|
|
745
|
+
if (match === null) break;
|
|
746
|
+
const at = match.index;
|
|
747
|
+
const line = pending + text.slice(start, at);
|
|
748
|
+
pending = "";
|
|
749
|
+
let next = at + 1;
|
|
750
|
+
if (text.charCodeAt(at) === 13) {
|
|
751
|
+
if (next < text.length) {
|
|
752
|
+
if (text.charCodeAt(next) === 10) next += 1;
|
|
753
|
+
} else {
|
|
754
|
+
skipLeadingLf = true;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
start = next;
|
|
758
|
+
processLine(line);
|
|
759
|
+
if (halted) return;
|
|
760
|
+
}
|
|
761
|
+
pending += text.slice(start);
|
|
762
|
+
};
|
|
763
|
+
const end = (options) => {
|
|
764
|
+
if (ended) return;
|
|
765
|
+
ended = true;
|
|
766
|
+
if (halted) return;
|
|
767
|
+
if (options?.dispatchIncomplete) {
|
|
768
|
+
if (pending !== "") processLine(pending);
|
|
769
|
+
if (!halted) dispatch();
|
|
770
|
+
}
|
|
771
|
+
pending = "";
|
|
772
|
+
data = "";
|
|
773
|
+
eventType = "";
|
|
774
|
+
};
|
|
775
|
+
return {
|
|
776
|
+
feed,
|
|
777
|
+
end,
|
|
778
|
+
get lastEventId() {
|
|
779
|
+
return lastEventId;
|
|
780
|
+
},
|
|
781
|
+
get retryMs() {
|
|
782
|
+
return retryMs;
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
function createSseEventParser(onEvent, onRetry, options) {
|
|
787
|
+
return createParser(onEvent, onRetry, false, options?.lastEventId ?? "");
|
|
788
|
+
}
|
|
789
|
+
function parseSseEvents(text) {
|
|
790
|
+
const events = [];
|
|
791
|
+
const parser = createSseEventParser((e) => {
|
|
792
|
+
events.push(e);
|
|
793
|
+
});
|
|
794
|
+
parser.feed(text);
|
|
795
|
+
parser.end();
|
|
796
|
+
return events;
|
|
797
|
+
}
|
|
798
|
+
async function readSseEvents(response, options) {
|
|
799
|
+
if (!response.body) {
|
|
800
|
+
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
801
|
+
}
|
|
802
|
+
const reader = response.body.getReader();
|
|
803
|
+
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
804
|
+
let stopped = false;
|
|
805
|
+
const parser = createParser(
|
|
806
|
+
(event) => {
|
|
807
|
+
if (options.onEvent(event) === true) {
|
|
808
|
+
stopped = true;
|
|
809
|
+
return true;
|
|
810
|
+
}
|
|
811
|
+
return void 0;
|
|
812
|
+
},
|
|
813
|
+
options.onRetry,
|
|
814
|
+
true,
|
|
815
|
+
options.lastEventId ?? ""
|
|
816
|
+
);
|
|
817
|
+
const result = () => ({
|
|
818
|
+
lastEventId: parser.lastEventId,
|
|
819
|
+
retryMs: parser.retryMs,
|
|
820
|
+
stopped
|
|
821
|
+
});
|
|
822
|
+
try {
|
|
823
|
+
while (true) {
|
|
824
|
+
const { done, value } = await reader.read();
|
|
825
|
+
if (done) break;
|
|
826
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
827
|
+
if (stopped) {
|
|
828
|
+
await reader.cancel().catch(() => void 0);
|
|
829
|
+
return result();
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
parser.feed(decoder.decode());
|
|
833
|
+
parser.end({ dispatchIncomplete: options.dispatchIncompleteAtEof === true });
|
|
834
|
+
return result();
|
|
835
|
+
} catch (error) {
|
|
836
|
+
await reader.cancel(error).catch(() => void 0);
|
|
837
|
+
throw error;
|
|
838
|
+
} finally {
|
|
839
|
+
reader.releaseLock();
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
var CR_OR_LF = /[\r\n]/;
|
|
843
|
+
function formatSseEvent(data, options = {}) {
|
|
844
|
+
const { event, id, retry: retry2 } = options;
|
|
845
|
+
let out = "";
|
|
846
|
+
if (id !== void 0) {
|
|
847
|
+
if (CR_OR_LF.test(id) || id.includes("\0")) {
|
|
848
|
+
throw new Error("SSE id \uC5D0 CR\xB7LF\xB7NUL \uC744 \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
849
|
+
}
|
|
850
|
+
out += `id: ${id}
|
|
851
|
+
`;
|
|
852
|
+
}
|
|
853
|
+
if (event !== void 0 && event !== "") {
|
|
854
|
+
if (CR_OR_LF.test(event)) throw new Error("SSE event \uC5D0 CR\xB7LF \uB97C \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
855
|
+
out += `event: ${event}
|
|
856
|
+
`;
|
|
857
|
+
}
|
|
858
|
+
if (retry2 !== void 0) {
|
|
859
|
+
if (!Number.isSafeInteger(retry2) || retry2 < 0) {
|
|
860
|
+
throw new Error(`SSE retry \uB294 0 \uC774\uC0C1\uC758 \uC815\uC218(ms)\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${retry2}`);
|
|
861
|
+
}
|
|
862
|
+
out += `retry: ${retry2}
|
|
863
|
+
`;
|
|
864
|
+
}
|
|
865
|
+
for (const line of data.split(/\r\n|\r|\n/)) {
|
|
866
|
+
out += `data: ${line}
|
|
867
|
+
`;
|
|
868
|
+
}
|
|
869
|
+
return out + "\n";
|
|
870
|
+
}
|
|
871
|
+
function formatSseComment(text = "") {
|
|
872
|
+
if (CR_OR_LF.test(text)) throw new Error("SSE \uC8FC\uC11D\uC5D0 CR\xB7LF \uB97C \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
873
|
+
return text === "" ? ":\n\n" : `: ${text}
|
|
874
|
+
|
|
875
|
+
`;
|
|
876
|
+
}
|
|
877
|
+
|
|
390
878
|
// src/sse.ts
|
|
391
879
|
function parseSseFrame(frame) {
|
|
392
880
|
const dataLine = frame.split("\n").find((l) => l.startsWith("data:"))?.replace(/^data:\s*/, "");
|
|
393
|
-
if (dataLine === void 0
|
|
394
|
-
|
|
881
|
+
if (dataLine === void 0) return { type: "skip" };
|
|
882
|
+
return parseChatPayload(dataLine);
|
|
883
|
+
}
|
|
884
|
+
function parseChatPayload(data) {
|
|
885
|
+
if (data === "") return { type: "skip" };
|
|
886
|
+
if (data === "[DONE]") return { type: "done" };
|
|
395
887
|
try {
|
|
396
|
-
const obj = JSON.parse(
|
|
888
|
+
const obj = JSON.parse(data);
|
|
397
889
|
if (typeof obj.conversationId === "number") {
|
|
398
890
|
return { type: "conversationId", id: obj.conversationId };
|
|
399
891
|
}
|
|
@@ -406,16 +898,9 @@ function parseSseFrame(frame) {
|
|
|
406
898
|
}
|
|
407
899
|
return { type: "skip" };
|
|
408
900
|
}
|
|
409
|
-
|
|
410
|
-
if (!response.body) {
|
|
411
|
-
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
412
|
-
}
|
|
413
|
-
const reader = response.body.getReader();
|
|
414
|
-
const decoder = new TextDecoder();
|
|
415
|
-
let buffer = "";
|
|
901
|
+
function createChatDispatcher(callbacks) {
|
|
416
902
|
let errored = false;
|
|
417
|
-
|
|
418
|
-
const ev = parseSseFrame(frame);
|
|
903
|
+
return (ev) => {
|
|
419
904
|
switch (ev.type) {
|
|
420
905
|
case "conversationId":
|
|
421
906
|
callbacks.onConversationId?.(ev.id);
|
|
@@ -438,6 +923,16 @@ async function readSseStream(response, callbacks) {
|
|
|
438
923
|
}
|
|
439
924
|
return false;
|
|
440
925
|
};
|
|
926
|
+
}
|
|
927
|
+
async function readSseStream(response, callbacks) {
|
|
928
|
+
if (!response.body) {
|
|
929
|
+
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
930
|
+
}
|
|
931
|
+
const reader = response.body.getReader();
|
|
932
|
+
const decoder = new TextDecoder();
|
|
933
|
+
let buffer = "";
|
|
934
|
+
const deliver = createChatDispatcher(callbacks);
|
|
935
|
+
const dispatch = (frame) => deliver(parseSseFrame(frame));
|
|
441
936
|
try {
|
|
442
937
|
while (true) {
|
|
443
938
|
const { done, value } = await reader.read();
|
|
@@ -459,6 +954,14 @@ async function readSseStream(response, callbacks) {
|
|
|
459
954
|
reader.releaseLock();
|
|
460
955
|
}
|
|
461
956
|
}
|
|
957
|
+
async function readSseChatEvents(response, callbacks) {
|
|
958
|
+
const deliver = createChatDispatcher(callbacks);
|
|
959
|
+
await readSseEvents(response, {
|
|
960
|
+
onEvent: (event) => deliver(parseChatPayload(event.data.trim())),
|
|
961
|
+
// 레거시 readSseStream 의 EOF 잔여 버퍼 처리와 같게 — 미완성 이벤트도 디스패치
|
|
962
|
+
dispatchIncompleteAtEof: true
|
|
963
|
+
});
|
|
964
|
+
}
|
|
462
965
|
|
|
463
966
|
// src/jwt.ts
|
|
464
967
|
function decodeJwtPayload(token) {
|
|
@@ -1090,25 +1593,22 @@ function extensionOf(fileName) {
|
|
|
1090
1593
|
return fileName.slice(dot + 1).toLowerCase();
|
|
1091
1594
|
}
|
|
1092
1595
|
function validateUpload(input) {
|
|
1596
|
+
const message = (key, ...args) => getMessage(key, { locale: input.locale, args, overrides: input.messages });
|
|
1093
1597
|
if (input.size > input.maxSizeBytes) {
|
|
1094
|
-
return { ok: false, reason: "size", message: "
|
|
1598
|
+
return { ok: false, reason: "size", message: message("rscc.upload.size") };
|
|
1095
1599
|
}
|
|
1096
1600
|
const ext = extensionOf(input.fileName);
|
|
1097
1601
|
const allowed = input.allowedExtensions.map(
|
|
1098
1602
|
(e) => (e.startsWith(".") ? e.slice(1) : e).toLowerCase()
|
|
1099
1603
|
);
|
|
1100
1604
|
if (ext === "" || !allowed.includes(ext)) {
|
|
1101
|
-
return { ok: false, reason: "extension", message:
|
|
1605
|
+
return { ok: false, reason: "extension", message: message("rscc.upload.extension", ext) };
|
|
1102
1606
|
}
|
|
1103
1607
|
const extra = input.extraMappings?.[ext];
|
|
1104
1608
|
const kinds = extra !== void 0 ? new Set(extra) : kindsForExtension(ext);
|
|
1105
1609
|
const kind = sniffFile(input.head);
|
|
1106
1610
|
if (kind === null || !kinds.has(kind)) {
|
|
1107
|
-
return {
|
|
1108
|
-
ok: false,
|
|
1109
|
-
reason: "content-mismatch",
|
|
1110
|
-
message: "\uD30C\uC77C \uB0B4\uC6A9\uC774 \uD655\uC7A5\uC790\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."
|
|
1111
|
-
};
|
|
1611
|
+
return { ok: false, reason: "content-mismatch", message: message("rscc.upload.contentMismatch") };
|
|
1112
1612
|
}
|
|
1113
1613
|
return { ok: true };
|
|
1114
1614
|
}
|
|
@@ -1779,6 +2279,10 @@ function matchesHangul(query, target) {
|
|
|
1779
2279
|
ApiError,
|
|
1780
2280
|
BulkheadFullError,
|
|
1781
2281
|
CircuitOpenError,
|
|
2282
|
+
DEFAULT_CSRF_COOKIE_NAME,
|
|
2283
|
+
DEFAULT_CSRF_HEADER_NAME,
|
|
2284
|
+
FIXED_MESSAGE_KEYS,
|
|
2285
|
+
MESSAGES,
|
|
1782
2286
|
ResultCode,
|
|
1783
2287
|
WEBHOOK_SIGNATURE_HEADER,
|
|
1784
2288
|
abbreviateAmount,
|
|
@@ -1796,18 +2300,26 @@ function matchesHangul(query, target) {
|
|
|
1796
2300
|
createBusinessDays,
|
|
1797
2301
|
createCircuitBreaker,
|
|
1798
2302
|
createFeatureFlags,
|
|
2303
|
+
createSseEventParser,
|
|
1799
2304
|
createTokenBucket,
|
|
1800
2305
|
createTtlCache,
|
|
2306
|
+
csrfHeaderFor,
|
|
1801
2307
|
decodeJwtPayload,
|
|
1802
2308
|
decomposeHangul,
|
|
2309
|
+
formatMessage,
|
|
1803
2310
|
formatPhoneNumber,
|
|
2311
|
+
formatSseComment,
|
|
2312
|
+
formatSseEvent,
|
|
1804
2313
|
generateIdempotencyKey,
|
|
2314
|
+
getMessage,
|
|
1805
2315
|
getTokenExpiry,
|
|
1806
2316
|
isBulkResult,
|
|
1807
2317
|
isChosungQuery,
|
|
2318
|
+
isCommonResultCode,
|
|
1808
2319
|
isForeignerRrn,
|
|
1809
2320
|
isRetryableStatus,
|
|
1810
2321
|
isTokenExpired,
|
|
2322
|
+
isUnsafeMethod,
|
|
1811
2323
|
isValidBusinessNumber,
|
|
1812
2324
|
isValidCorporateNumber,
|
|
1813
2325
|
isValidRrn,
|
|
@@ -1819,15 +2331,22 @@ function matchesHangul(query, target) {
|
|
|
1819
2331
|
maskPhone,
|
|
1820
2332
|
maskSecret,
|
|
1821
2333
|
matchesHangul,
|
|
2334
|
+
negotiateLanguage,
|
|
1822
2335
|
normalizeBusinessNumber,
|
|
1823
2336
|
normalizePhoneNumber,
|
|
1824
2337
|
normalizeRrn,
|
|
2338
|
+
parseChatPayload,
|
|
1825
2339
|
parseFlag,
|
|
1826
2340
|
parseRetryAfterMs,
|
|
2341
|
+
parseSseEvents,
|
|
1827
2342
|
parseSseFrame,
|
|
1828
2343
|
parseWireDateTime,
|
|
1829
2344
|
pickJosa,
|
|
2345
|
+
readCookie,
|
|
2346
|
+
readSseChatEvents,
|
|
2347
|
+
readSseEvents,
|
|
1830
2348
|
readSseStream,
|
|
2349
|
+
resultCodeForStatus,
|
|
1831
2350
|
retry,
|
|
1832
2351
|
rrnBirthDate,
|
|
1833
2352
|
rrnChecksumOkLegacy,
|
|
@@ -1836,6 +2355,7 @@ function matchesHangul(query, target) {
|
|
|
1836
2355
|
sniffFile,
|
|
1837
2356
|
stripZone,
|
|
1838
2357
|
toChosung,
|
|
2358
|
+
toCommonResultCode,
|
|
1839
2359
|
toE164,
|
|
1840
2360
|
toFormalNotation,
|
|
1841
2361
|
toKoreanWords,
|