@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.cjs
CHANGED
|
@@ -25,6 +25,8 @@ __export(index_exports, {
|
|
|
25
25
|
CircuitOpenError: () => CircuitOpenError,
|
|
26
26
|
DEFAULT_CSRF_COOKIE_NAME: () => DEFAULT_CSRF_COOKIE_NAME,
|
|
27
27
|
DEFAULT_CSRF_HEADER_NAME: () => DEFAULT_CSRF_HEADER_NAME,
|
|
28
|
+
FIXED_MESSAGE_KEYS: () => FIXED_MESSAGE_KEYS,
|
|
29
|
+
MESSAGES: () => MESSAGES,
|
|
28
30
|
ResultCode: () => ResultCode,
|
|
29
31
|
WEBHOOK_SIGNATURE_HEADER: () => WEBHOOK_SIGNATURE_HEADER,
|
|
30
32
|
abbreviateAmount: () => abbreviateAmount,
|
|
@@ -42,16 +44,22 @@ __export(index_exports, {
|
|
|
42
44
|
createBusinessDays: () => createBusinessDays,
|
|
43
45
|
createCircuitBreaker: () => createCircuitBreaker,
|
|
44
46
|
createFeatureFlags: () => createFeatureFlags,
|
|
47
|
+
createSseEventParser: () => createSseEventParser,
|
|
45
48
|
createTokenBucket: () => createTokenBucket,
|
|
46
49
|
createTtlCache: () => createTtlCache,
|
|
47
50
|
csrfHeaderFor: () => csrfHeaderFor,
|
|
48
51
|
decodeJwtPayload: () => decodeJwtPayload,
|
|
49
52
|
decomposeHangul: () => decomposeHangul,
|
|
53
|
+
formatMessage: () => formatMessage,
|
|
50
54
|
formatPhoneNumber: () => formatPhoneNumber,
|
|
55
|
+
formatSseComment: () => formatSseComment,
|
|
56
|
+
formatSseEvent: () => formatSseEvent,
|
|
51
57
|
generateIdempotencyKey: () => generateIdempotencyKey,
|
|
58
|
+
getMessage: () => getMessage,
|
|
52
59
|
getTokenExpiry: () => getTokenExpiry,
|
|
53
60
|
isBulkResult: () => isBulkResult,
|
|
54
61
|
isChosungQuery: () => isChosungQuery,
|
|
62
|
+
isCommonResultCode: () => isCommonResultCode,
|
|
55
63
|
isForeignerRrn: () => isForeignerRrn,
|
|
56
64
|
isRetryableStatus: () => isRetryableStatus,
|
|
57
65
|
isTokenExpired: () => isTokenExpired,
|
|
@@ -67,16 +75,22 @@ __export(index_exports, {
|
|
|
67
75
|
maskPhone: () => maskPhone,
|
|
68
76
|
maskSecret: () => maskSecret,
|
|
69
77
|
matchesHangul: () => matchesHangul,
|
|
78
|
+
negotiateLanguage: () => negotiateLanguage,
|
|
70
79
|
normalizeBusinessNumber: () => normalizeBusinessNumber,
|
|
71
80
|
normalizePhoneNumber: () => normalizePhoneNumber,
|
|
72
81
|
normalizeRrn: () => normalizeRrn,
|
|
82
|
+
parseChatPayload: () => parseChatPayload,
|
|
73
83
|
parseFlag: () => parseFlag,
|
|
74
84
|
parseRetryAfterMs: () => parseRetryAfterMs,
|
|
85
|
+
parseSseEvents: () => parseSseEvents,
|
|
75
86
|
parseSseFrame: () => parseSseFrame,
|
|
76
87
|
parseWireDateTime: () => parseWireDateTime,
|
|
77
88
|
pickJosa: () => pickJosa,
|
|
78
89
|
readCookie: () => readCookie,
|
|
90
|
+
readSseChatEvents: () => readSseChatEvents,
|
|
91
|
+
readSseEvents: () => readSseEvents,
|
|
79
92
|
readSseStream: () => readSseStream,
|
|
93
|
+
resultCodeForStatus: () => resultCodeForStatus,
|
|
80
94
|
retry: () => retry,
|
|
81
95
|
rrnBirthDate: () => rrnBirthDate,
|
|
82
96
|
rrnChecksumOkLegacy: () => rrnChecksumOkLegacy,
|
|
@@ -85,6 +99,7 @@ __export(index_exports, {
|
|
|
85
99
|
sniffFile: () => sniffFile,
|
|
86
100
|
stripZone: () => stripZone,
|
|
87
101
|
toChosung: () => toChosung,
|
|
102
|
+
toCommonResultCode: () => toCommonResultCode,
|
|
88
103
|
toE164: () => toE164,
|
|
89
104
|
toFormalNotation: () => toFormalNotation,
|
|
90
105
|
toKoreanWords: () => toKoreanWords,
|
|
@@ -117,6 +132,21 @@ var ResultCode = {
|
|
|
117
132
|
INTERNAL_SERVER_ERROR: "500"
|
|
118
133
|
};
|
|
119
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
|
+
|
|
120
150
|
// src/csrf.ts
|
|
121
151
|
var DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
|
|
122
152
|
var DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
|
|
@@ -216,6 +246,141 @@ function generateIdempotencyKey() {
|
|
|
216
246
|
}).join("-");
|
|
217
247
|
}
|
|
218
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
|
+
|
|
219
384
|
// src/retry.ts
|
|
220
385
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
221
386
|
function isRetryableStatus(status) {
|
|
@@ -301,6 +466,7 @@ var ApiError = class extends Error {
|
|
|
301
466
|
super(args.message);
|
|
302
467
|
this.name = "ApiError";
|
|
303
468
|
this.code = args.code;
|
|
469
|
+
this.commonCode = toCommonResultCode(args.code, args.status);
|
|
304
470
|
this.status = args.status;
|
|
305
471
|
this.traceId = args.traceId;
|
|
306
472
|
this.data = args.data;
|
|
@@ -332,6 +498,8 @@ function isCommonResponse(value) {
|
|
|
332
498
|
}
|
|
333
499
|
function createApiClient(config) {
|
|
334
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;
|
|
335
503
|
let hookWarned = false;
|
|
336
504
|
const safeHook = (fn) => {
|
|
337
505
|
try {
|
|
@@ -360,6 +528,9 @@ function createApiClient(config) {
|
|
|
360
528
|
if (!headers.has("Content-Type") && typeof init.body === "string") {
|
|
361
529
|
headers.set("Content-Type", "application/json");
|
|
362
530
|
}
|
|
531
|
+
if (acceptLanguage !== null && !headers.has("Accept-Language")) {
|
|
532
|
+
headers.set("Accept-Language", acceptLanguage);
|
|
533
|
+
}
|
|
363
534
|
if (config.getToken && !headers.has("Authorization")) {
|
|
364
535
|
const token = config.getToken();
|
|
365
536
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
@@ -383,7 +554,7 @@ function createApiClient(config) {
|
|
|
383
554
|
if (config.strictJson && response.ok) {
|
|
384
555
|
throw new ApiError({
|
|
385
556
|
code: "INVALID_JSON",
|
|
386
|
-
message: "
|
|
557
|
+
message: clientMessage("rscc.client.unreadableBody"),
|
|
387
558
|
status: response.status,
|
|
388
559
|
traceId,
|
|
389
560
|
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After"))
|
|
@@ -402,7 +573,7 @@ function createApiClient(config) {
|
|
|
402
573
|
if (config.strictJson && response.ok) {
|
|
403
574
|
throw new ApiError({
|
|
404
575
|
code: "INVALID_JSON",
|
|
405
|
-
message: "
|
|
576
|
+
message: clientMessage("rscc.client.invalidJson"),
|
|
406
577
|
status: response.status,
|
|
407
578
|
traceId,
|
|
408
579
|
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After")),
|
|
@@ -431,7 +602,7 @@ function createApiClient(config) {
|
|
|
431
602
|
}
|
|
432
603
|
if (!response.ok) {
|
|
433
604
|
const j2 = json;
|
|
434
|
-
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);
|
|
435
606
|
fail(String(response.status), message);
|
|
436
607
|
}
|
|
437
608
|
return { data: json, traceId, status: response.status, response };
|
|
@@ -489,13 +660,232 @@ function createApiClient(config) {
|
|
|
489
660
|
return { request, requestWithMeta };
|
|
490
661
|
}
|
|
491
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
|
+
|
|
492
878
|
// src/sse.ts
|
|
493
879
|
function parseSseFrame(frame) {
|
|
494
880
|
const dataLine = frame.split("\n").find((l) => l.startsWith("data:"))?.replace(/^data:\s*/, "");
|
|
495
|
-
if (dataLine === void 0
|
|
496
|
-
|
|
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" };
|
|
497
887
|
try {
|
|
498
|
-
const obj = JSON.parse(
|
|
888
|
+
const obj = JSON.parse(data);
|
|
499
889
|
if (typeof obj.conversationId === "number") {
|
|
500
890
|
return { type: "conversationId", id: obj.conversationId };
|
|
501
891
|
}
|
|
@@ -508,16 +898,9 @@ function parseSseFrame(frame) {
|
|
|
508
898
|
}
|
|
509
899
|
return { type: "skip" };
|
|
510
900
|
}
|
|
511
|
-
|
|
512
|
-
if (!response.body) {
|
|
513
|
-
throw new Error("SSE \uC751\uB2F5\uC5D0 body \uC2A4\uD2B8\uB9BC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
514
|
-
}
|
|
515
|
-
const reader = response.body.getReader();
|
|
516
|
-
const decoder = new TextDecoder();
|
|
517
|
-
let buffer = "";
|
|
901
|
+
function createChatDispatcher(callbacks) {
|
|
518
902
|
let errored = false;
|
|
519
|
-
|
|
520
|
-
const ev = parseSseFrame(frame);
|
|
903
|
+
return (ev) => {
|
|
521
904
|
switch (ev.type) {
|
|
522
905
|
case "conversationId":
|
|
523
906
|
callbacks.onConversationId?.(ev.id);
|
|
@@ -540,6 +923,16 @@ async function readSseStream(response, callbacks) {
|
|
|
540
923
|
}
|
|
541
924
|
return false;
|
|
542
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));
|
|
543
936
|
try {
|
|
544
937
|
while (true) {
|
|
545
938
|
const { done, value } = await reader.read();
|
|
@@ -561,6 +954,14 @@ async function readSseStream(response, callbacks) {
|
|
|
561
954
|
reader.releaseLock();
|
|
562
955
|
}
|
|
563
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
|
+
}
|
|
564
965
|
|
|
565
966
|
// src/jwt.ts
|
|
566
967
|
function decodeJwtPayload(token) {
|
|
@@ -620,22 +1021,190 @@ function maskCardNumber(cardNumber) {
|
|
|
620
1021
|
|
|
621
1022
|
// src/datetime.ts
|
|
622
1023
|
var pad = (n) => String(n).padStart(2, "0");
|
|
623
|
-
|
|
624
|
-
|
|
1024
|
+
var zoneFormatters = /* @__PURE__ */ new Map();
|
|
1025
|
+
function zoneFormatter(timeZone) {
|
|
1026
|
+
let formatter = zoneFormatters.get(timeZone);
|
|
1027
|
+
if (formatter === void 0) {
|
|
1028
|
+
formatter = new Intl.DateTimeFormat("en-US-u-ca-gregory-nu-latn", {
|
|
1029
|
+
timeZone,
|
|
1030
|
+
hourCycle: "h23",
|
|
1031
|
+
year: "numeric",
|
|
1032
|
+
month: "2-digit",
|
|
1033
|
+
day: "2-digit",
|
|
1034
|
+
hour: "2-digit",
|
|
1035
|
+
minute: "2-digit",
|
|
1036
|
+
second: "2-digit"
|
|
1037
|
+
});
|
|
1038
|
+
zoneFormatters.set(timeZone, formatter);
|
|
1039
|
+
}
|
|
1040
|
+
return formatter;
|
|
1041
|
+
}
|
|
1042
|
+
function zonedFields(ms, timeZone) {
|
|
1043
|
+
const fields = {
|
|
1044
|
+
year: 0,
|
|
1045
|
+
month: 0,
|
|
1046
|
+
day: 0,
|
|
1047
|
+
hour: 0,
|
|
1048
|
+
minute: 0,
|
|
1049
|
+
second: 0,
|
|
1050
|
+
millisecond: (ms % 1e3 + 1e3) % 1e3
|
|
1051
|
+
};
|
|
1052
|
+
for (const part of zoneFormatter(timeZone).formatToParts(ms)) {
|
|
1053
|
+
switch (part.type) {
|
|
1054
|
+
case "year":
|
|
1055
|
+
fields.year = Number(part.value);
|
|
1056
|
+
break;
|
|
1057
|
+
case "month":
|
|
1058
|
+
fields.month = Number(part.value);
|
|
1059
|
+
break;
|
|
1060
|
+
case "day":
|
|
1061
|
+
fields.day = Number(part.value);
|
|
1062
|
+
break;
|
|
1063
|
+
case "hour":
|
|
1064
|
+
fields.hour = Number(part.value) % 24;
|
|
1065
|
+
break;
|
|
1066
|
+
case "minute":
|
|
1067
|
+
fields.minute = Number(part.value);
|
|
1068
|
+
break;
|
|
1069
|
+
case "second":
|
|
1070
|
+
fields.second = Number(part.value);
|
|
1071
|
+
break;
|
|
1072
|
+
default:
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return fields;
|
|
1077
|
+
}
|
|
1078
|
+
function wallAsUtcMs(f) {
|
|
1079
|
+
const d = /* @__PURE__ */ new Date(0);
|
|
1080
|
+
d.setUTCFullYear(f.year, f.month - 1, f.day);
|
|
1081
|
+
d.setUTCHours(f.hour, f.minute, f.second, f.millisecond);
|
|
1082
|
+
return d.getTime();
|
|
1083
|
+
}
|
|
1084
|
+
function offsetMinutes(ms, timeZone) {
|
|
1085
|
+
const wall = zonedFields(ms, timeZone);
|
|
1086
|
+
wall.millisecond = 0;
|
|
1087
|
+
return (wallAsUtcMs(wall) - Math.floor(ms / 1e3) * 1e3) / 6e4;
|
|
1088
|
+
}
|
|
1089
|
+
var DAY_MS = 864e5;
|
|
1090
|
+
function wallToEpoch(fields, timeZone) {
|
|
1091
|
+
const wall = wallAsUtcMs(fields);
|
|
1092
|
+
const toMs = (minutes) => Math.round(minutes * 6e4);
|
|
1093
|
+
const before = offsetMinutes(wall - DAY_MS, timeZone);
|
|
1094
|
+
const after = offsetMinutes(wall + DAY_MS, timeZone);
|
|
1095
|
+
const atBefore = wall - toMs(before);
|
|
1096
|
+
if (offsetMinutes(atBefore, timeZone) === before) return atBefore;
|
|
1097
|
+
const atAfter = wall - toMs(after);
|
|
1098
|
+
if (offsetMinutes(atAfter, timeZone) === after) return atAfter;
|
|
1099
|
+
return atBefore;
|
|
1100
|
+
}
|
|
1101
|
+
function formatOffset(minutes) {
|
|
1102
|
+
const totalSeconds = Math.round(minutes * 60);
|
|
1103
|
+
if (totalSeconds === 0) return "Z";
|
|
1104
|
+
const sign = totalSeconds < 0 ? "-" : "+";
|
|
1105
|
+
const abs = Math.abs(totalSeconds);
|
|
1106
|
+
const hh = pad(Math.floor(abs / 3600));
|
|
1107
|
+
const mm = pad(Math.floor(abs % 3600 / 60));
|
|
1108
|
+
const ss = abs % 60;
|
|
1109
|
+
return `${sign}${hh}:${mm}${ss === 0 ? "" : `:${pad(ss)}`}`;
|
|
1110
|
+
}
|
|
1111
|
+
var formatWallDate = (f) => `${f.year}-${pad(f.month)}-${pad(f.day)}`;
|
|
1112
|
+
var formatWallDateTime = (f) => `${formatWallDate(f)}T${pad(f.hour)}:${pad(f.minute)}:${pad(f.second)}`;
|
|
1113
|
+
function assertValidDate(date) {
|
|
1114
|
+
const ms = date.getTime();
|
|
1115
|
+
if (Number.isNaN(ms)) throw new RangeError("Invalid time value");
|
|
1116
|
+
return ms;
|
|
1117
|
+
}
|
|
1118
|
+
function toWireDateTime(date, options) {
|
|
1119
|
+
const timeZone = options?.timeZone;
|
|
1120
|
+
const withOffset = options?.offset === true;
|
|
1121
|
+
if (timeZone === void 0) {
|
|
1122
|
+
const wall2 = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
1123
|
+
if (!withOffset) return wall2;
|
|
1124
|
+
assertValidDate(date);
|
|
1125
|
+
return wall2 + formatOffset(-date.getTimezoneOffset());
|
|
1126
|
+
}
|
|
1127
|
+
const ms = assertValidDate(date);
|
|
1128
|
+
const wall = formatWallDateTime(zonedFields(ms, timeZone));
|
|
1129
|
+
return withOffset ? wall + formatOffset(offsetMinutes(ms, timeZone)) : wall;
|
|
1130
|
+
}
|
|
1131
|
+
function toWireDate(date, options) {
|
|
1132
|
+
const timeZone = options?.timeZone;
|
|
1133
|
+
if (timeZone === void 0) {
|
|
1134
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
1135
|
+
}
|
|
1136
|
+
return formatWallDate(zonedFields(assertValidDate(date), timeZone));
|
|
1137
|
+
}
|
|
1138
|
+
var WALL_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?$/;
|
|
1139
|
+
function parseWallText(text, original) {
|
|
1140
|
+
const m = text.match(WALL_PATTERN);
|
|
1141
|
+
if (!m) throw new Error(`\uC62C\uBC14\uB978 datetime \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${original}`);
|
|
1142
|
+
const [, y, mo, day, h, mi, s = "0", frac = "0"] = m;
|
|
1143
|
+
return {
|
|
1144
|
+
year: Number(y),
|
|
1145
|
+
month: Number(mo),
|
|
1146
|
+
day: Number(day),
|
|
1147
|
+
hour: Number(h),
|
|
1148
|
+
minute: Number(mi),
|
|
1149
|
+
second: Number(s),
|
|
1150
|
+
millisecond: Number((frac + "000").slice(0, 3))
|
|
1151
|
+
// JS Date 는 ms 정밀도까지만
|
|
1152
|
+
};
|
|
625
1153
|
}
|
|
626
|
-
function
|
|
627
|
-
|
|
1154
|
+
function assertValidWall(f, original) {
|
|
1155
|
+
const d = new Date(wallAsUtcMs(f));
|
|
1156
|
+
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) {
|
|
1157
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 datetime \uAC12\uC785\uB2C8\uB2E4: ${original}`);
|
|
1158
|
+
}
|
|
628
1159
|
}
|
|
629
|
-
function
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
);
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
const
|
|
638
|
-
|
|
1160
|
+
function matchRfc3339Offset(value) {
|
|
1161
|
+
const tIndex = value.indexOf("T");
|
|
1162
|
+
if (tIndex === -1) return null;
|
|
1163
|
+
const m = value.slice(tIndex).match(/(?:[Zz]|([+-])(\d{2}):(\d{2}))$/);
|
|
1164
|
+
if (!m || m.index === void 0) return null;
|
|
1165
|
+
const [token, sign, hh, mm] = m;
|
|
1166
|
+
const index = value.length - token.length;
|
|
1167
|
+
if (sign === void 0) return { index, minutes: 0 };
|
|
1168
|
+
const hours = Number(hh);
|
|
1169
|
+
const minutes = Number(mm);
|
|
1170
|
+
if (hours > 23 || minutes > 59) {
|
|
1171
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC624\uD504\uC14B\uC785\uB2C8\uB2E4: ${value}`);
|
|
1172
|
+
}
|
|
1173
|
+
return { index, minutes: (sign === "-" ? -1 : 1) * (hours * 60 + minutes) };
|
|
1174
|
+
}
|
|
1175
|
+
function parseWireDateTime(value, options) {
|
|
1176
|
+
const timeZone = options?.timeZone;
|
|
1177
|
+
const policy = options?.inboundOffset ?? "drop";
|
|
1178
|
+
if (policy !== "drop" && policy !== "convert" && policy !== "reject") {
|
|
1179
|
+
throw new RangeError(`\uC54C \uC218 \uC5C6\uB294 inboundOffset \uC815\uCC45\uC785\uB2C8\uB2E4: ${String(policy)}`);
|
|
1180
|
+
}
|
|
1181
|
+
if (timeZone !== void 0) zoneFormatter(timeZone);
|
|
1182
|
+
const trimmed = value.trim();
|
|
1183
|
+
let wallText = trimmed;
|
|
1184
|
+
let offset;
|
|
1185
|
+
if (policy === "drop") {
|
|
1186
|
+
wallText = stripZone(trimmed);
|
|
1187
|
+
} else {
|
|
1188
|
+
const found = matchRfc3339Offset(trimmed);
|
|
1189
|
+
if (found) {
|
|
1190
|
+
if (policy === "reject") {
|
|
1191
|
+
throw new RangeError(`\uC624\uD504\uC14B\uC774 \uBD99\uC740 datetime \uC740 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(inboundOffset: "reject"): ${value}`);
|
|
1192
|
+
}
|
|
1193
|
+
offset = found.minutes;
|
|
1194
|
+
wallText = trimmed.slice(0, found.index);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
const f = parseWallText(wallText, value);
|
|
1198
|
+
if (offset !== void 0) {
|
|
1199
|
+
assertValidWall(f, value);
|
|
1200
|
+
return new Date(wallAsUtcMs(f) - offset * 6e4);
|
|
1201
|
+
}
|
|
1202
|
+
if (timeZone !== void 0) {
|
|
1203
|
+
assertValidWall(f, value);
|
|
1204
|
+
return new Date(wallToEpoch(f, timeZone));
|
|
1205
|
+
}
|
|
1206
|
+
const date = new Date(f.year, f.month - 1, f.day, f.hour, f.minute, f.second, f.millisecond);
|
|
1207
|
+
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) {
|
|
639
1208
|
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 datetime \uAC12\uC785\uB2C8\uB2E4: ${value}`);
|
|
640
1209
|
}
|
|
641
1210
|
return date;
|
|
@@ -1192,25 +1761,22 @@ function extensionOf(fileName) {
|
|
|
1192
1761
|
return fileName.slice(dot + 1).toLowerCase();
|
|
1193
1762
|
}
|
|
1194
1763
|
function validateUpload(input) {
|
|
1764
|
+
const message = (key, ...args) => getMessage(key, { locale: input.locale, args, overrides: input.messages });
|
|
1195
1765
|
if (input.size > input.maxSizeBytes) {
|
|
1196
|
-
return { ok: false, reason: "size", message: "
|
|
1766
|
+
return { ok: false, reason: "size", message: message("rscc.upload.size") };
|
|
1197
1767
|
}
|
|
1198
1768
|
const ext = extensionOf(input.fileName);
|
|
1199
1769
|
const allowed = input.allowedExtensions.map(
|
|
1200
1770
|
(e) => (e.startsWith(".") ? e.slice(1) : e).toLowerCase()
|
|
1201
1771
|
);
|
|
1202
1772
|
if (ext === "" || !allowed.includes(ext)) {
|
|
1203
|
-
return { ok: false, reason: "extension", message:
|
|
1773
|
+
return { ok: false, reason: "extension", message: message("rscc.upload.extension", ext) };
|
|
1204
1774
|
}
|
|
1205
1775
|
const extra = input.extraMappings?.[ext];
|
|
1206
1776
|
const kinds = extra !== void 0 ? new Set(extra) : kindsForExtension(ext);
|
|
1207
1777
|
const kind = sniffFile(input.head);
|
|
1208
1778
|
if (kind === null || !kinds.has(kind)) {
|
|
1209
|
-
return {
|
|
1210
|
-
ok: false,
|
|
1211
|
-
reason: "content-mismatch",
|
|
1212
|
-
message: "\uD30C\uC77C \uB0B4\uC6A9\uC774 \uD655\uC7A5\uC790\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."
|
|
1213
|
-
};
|
|
1779
|
+
return { ok: false, reason: "content-mismatch", message: message("rscc.upload.contentMismatch") };
|
|
1214
1780
|
}
|
|
1215
1781
|
return { ok: true };
|
|
1216
1782
|
}
|
|
@@ -1883,6 +2449,8 @@ function matchesHangul(query, target) {
|
|
|
1883
2449
|
CircuitOpenError,
|
|
1884
2450
|
DEFAULT_CSRF_COOKIE_NAME,
|
|
1885
2451
|
DEFAULT_CSRF_HEADER_NAME,
|
|
2452
|
+
FIXED_MESSAGE_KEYS,
|
|
2453
|
+
MESSAGES,
|
|
1886
2454
|
ResultCode,
|
|
1887
2455
|
WEBHOOK_SIGNATURE_HEADER,
|
|
1888
2456
|
abbreviateAmount,
|
|
@@ -1900,16 +2468,22 @@ function matchesHangul(query, target) {
|
|
|
1900
2468
|
createBusinessDays,
|
|
1901
2469
|
createCircuitBreaker,
|
|
1902
2470
|
createFeatureFlags,
|
|
2471
|
+
createSseEventParser,
|
|
1903
2472
|
createTokenBucket,
|
|
1904
2473
|
createTtlCache,
|
|
1905
2474
|
csrfHeaderFor,
|
|
1906
2475
|
decodeJwtPayload,
|
|
1907
2476
|
decomposeHangul,
|
|
2477
|
+
formatMessage,
|
|
1908
2478
|
formatPhoneNumber,
|
|
2479
|
+
formatSseComment,
|
|
2480
|
+
formatSseEvent,
|
|
1909
2481
|
generateIdempotencyKey,
|
|
2482
|
+
getMessage,
|
|
1910
2483
|
getTokenExpiry,
|
|
1911
2484
|
isBulkResult,
|
|
1912
2485
|
isChosungQuery,
|
|
2486
|
+
isCommonResultCode,
|
|
1913
2487
|
isForeignerRrn,
|
|
1914
2488
|
isRetryableStatus,
|
|
1915
2489
|
isTokenExpired,
|
|
@@ -1925,16 +2499,22 @@ function matchesHangul(query, target) {
|
|
|
1925
2499
|
maskPhone,
|
|
1926
2500
|
maskSecret,
|
|
1927
2501
|
matchesHangul,
|
|
2502
|
+
negotiateLanguage,
|
|
1928
2503
|
normalizeBusinessNumber,
|
|
1929
2504
|
normalizePhoneNumber,
|
|
1930
2505
|
normalizeRrn,
|
|
2506
|
+
parseChatPayload,
|
|
1931
2507
|
parseFlag,
|
|
1932
2508
|
parseRetryAfterMs,
|
|
2509
|
+
parseSseEvents,
|
|
1933
2510
|
parseSseFrame,
|
|
1934
2511
|
parseWireDateTime,
|
|
1935
2512
|
pickJosa,
|
|
1936
2513
|
readCookie,
|
|
2514
|
+
readSseChatEvents,
|
|
2515
|
+
readSseEvents,
|
|
1937
2516
|
readSseStream,
|
|
2517
|
+
resultCodeForStatus,
|
|
1938
2518
|
retry,
|
|
1939
2519
|
rrnBirthDate,
|
|
1940
2520
|
rrnChecksumOkLegacy,
|
|
@@ -1943,6 +2523,7 @@ function matchesHangul(query, target) {
|
|
|
1943
2523
|
sniffFile,
|
|
1944
2524
|
stripZone,
|
|
1945
2525
|
toChosung,
|
|
2526
|
+
toCommonResultCode,
|
|
1946
2527
|
toE164,
|
|
1947
2528
|
toFormalNotation,
|
|
1948
2529
|
toKoreanWords,
|