@iyulab/chat-components 0.7.1 → 0.8.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.0] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **채팅 메시지의 이모지가 조용히 분해되던 결함 수정** — `stripZeroWidth` 가 보이지 않는 문자를 제거하면서 ZWJ(U+200D)까지 지웠다. ZWJ 는 이모지 시퀀스를 결합하는 조판 문자이므로, `UMarkedBlock` 이 메시지 본문에 이 함수를 적용할 때 가족·직업 이모지가 낱개로 쪼개졌다(👨👩👧 → 👨👩👧). 이제 표시 경로는 ZWJ 를 보존하고, URL 위생 경로(`escapeHtmlHref`)는 종전대로 ZWJ 까지 제거해 `java<ZWJ>script:` 형태의 protocol 우회를 계속 차단한다. 두 경로의 요구가 반대이므로 문자 집합을 분리했다. 회귀 테스트 7건 추가.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **이 패키지의 eslint 가 실제로 동작하기 시작했다.** `eslint.config.js` 의 두 결함 — (1) `files: ["src/**/*"]` 가 ESLint 9 에서 universal 패턴이라 `.ts` 를 opt-in 하지 못함, (2) 배열 프리셋(`tseslint.configs.recommended`)을 객체 스프레드해 프리셋이 무력화됨 — 을 수정했다. `build` 스크립트의 `eslint &&` 게이트는 매칭 파일이 0개라 항상 통과하고 있었다. 위 이모지 결함은 이 복구로 처음 드러났다.
|
|
10
|
+
- `npm run lint` / `npm run lint:fix` 스크립트 추가.
|
|
11
|
+
- **타입 정밀화(소비자 영향 가능)**: `BaseJsonSchema` 의 `default`/`enum`/`examples` 가 `any` → 새 `JsonValue` 타입으로, `FileBlockItem.data` 가 `any` → `unknown` 으로 좁혀졌다. 값을 그대로 사용하던 코드는 좁히기(narrowing)나 캐스팅이 필요할 수 있다.
|
|
12
|
+
- `UMarkedBlock`: 재할당 없는 `let` → `const`.
|
|
13
|
+
|
|
3
14
|
## [0.7.1] - 2026-07-12
|
|
4
15
|
|
|
5
16
|
### Fixed
|
|
@@ -68,7 +68,7 @@ var UMarkedBlock = class UMarkedBlock extends UElement {
|
|
|
68
68
|
this.placeholder.reset();
|
|
69
69
|
value = stripZeroWidth(value);
|
|
70
70
|
if (this.refs?.length) value = this.insertRefs(value, this.refs);
|
|
71
|
-
|
|
71
|
+
const html = this.parser.parse(value, { async: false });
|
|
72
72
|
return unsafeHTML(this.placeholder.restore(html));
|
|
73
73
|
}
|
|
74
74
|
/**
|
package/dist/types/Schema.d.ts
CHANGED
|
@@ -11,15 +11,22 @@
|
|
|
11
11
|
* - 조건부 스키마 if/then/else (위젯 속성에 불필요)
|
|
12
12
|
* - 고급 object/array 검증들 (단순한 구조만으로 충분)
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* JSON 으로 표현 가능한 임의의 값.
|
|
16
|
+
* 스키마의 default/enum/examples 는 임의 JSON 을 담으므로 `any` 대신 이 타입을 쓴다.
|
|
17
|
+
*/
|
|
18
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
19
|
+
[key: string]: JsonValue;
|
|
20
|
+
};
|
|
14
21
|
export interface BaseJsonSchema {
|
|
15
22
|
/** 속성에 대한 설명 (LLM용) */
|
|
16
23
|
description?: string;
|
|
17
24
|
/** 기본값 */
|
|
18
|
-
default?:
|
|
25
|
+
default?: JsonValue;
|
|
19
26
|
/** 허용 가능한 값들 (제한된 선택지) */
|
|
20
|
-
enum?:
|
|
27
|
+
enum?: JsonValue[];
|
|
21
28
|
/** 예시 값들 (LLM이 이해하기 쉽도록) */
|
|
22
|
-
examples?:
|
|
29
|
+
examples?: JsonValue[];
|
|
23
30
|
}
|
|
24
31
|
export interface BooleanJsonSchema extends BaseJsonSchema {
|
|
25
32
|
type: 'boolean';
|
|
@@ -7,10 +7,6 @@
|
|
|
7
7
|
* - escapeHtmlAttr : HTML attribute 값에 삽입할 때
|
|
8
8
|
* - escapeHtmlHref : href/src 같은 URL attribute에 삽입할 때
|
|
9
9
|
*/
|
|
10
|
-
/**
|
|
11
|
-
* Zero Width Space / LTR / RTL mark / BOM 등
|
|
12
|
-
* 화면에 표시되지 않는 유니코드 문자를 제거합니다.
|
|
13
|
-
*/
|
|
14
10
|
export declare function stripZeroWidth(value: string): string;
|
|
15
11
|
/**
|
|
16
12
|
* HTML 텍스트 노드(context)에 삽입할 문자열을 escape합니다.
|
|
@@ -12,9 +12,24 @@
|
|
|
12
12
|
* Zero Width Space / LTR / RTL mark / BOM 등
|
|
13
13
|
* 화면에 표시되지 않는 유니코드 문자를 제거합니다.
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* \uD45C\uC2DC \uACBD\uB85C\uC5D0\uC11C \uC81C\uAC70\uD560 zero-width \uBB38\uC790.
|
|
17
|
+
*
|
|
18
|
+
* ZWJ(U+200D)\uB294 **\uC758\uB3C4\uC801\uC73C\uB85C \uC81C\uC678**\uD55C\uB2E4. ZWJ \uB294 \uC774\uBAA8\uC9C0 \uC2DC\uD000\uC2A4\uB97C \uACB0\uD569\uD558\uB294
|
|
19
|
+
* \uC870\uD310 \uBB38\uC790\uB77C, \uC81C\uAC70\uD558\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uC785\uB825\uD55C \uAC00\uC871\u00B7\uC9C1\uC5C5 \uC774\uBAA8\uC9C0\uAC00 \uB0B1\uAC1C\uB85C \uBD84\uD574\uB41C\uB2E4
|
|
20
|
+
* (\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67 \u2192 \uD83D\uDC68\uD83D\uDC69\uD83D\uDC67). \uD45C\uC2DC \uACBD\uB85C\uC5D0\uC11C\uB294 \uCF58\uD150\uCE20 \uBCF4\uC874\uC774 \uC6B0\uC120\uC774\uB2E4.
|
|
21
|
+
*/
|
|
22
|
+
var ZERO_WIDTH_DISPLAY_REGEX = /[\u200B\u200C\u200E\u200F\uFEFF]/gu;
|
|
23
|
+
/**
|
|
24
|
+
* URL \uC704\uC0DD \uACBD\uB85C\uC5D0\uC11C \uC81C\uAC70\uD560 zero-width \uBB38\uC790.
|
|
25
|
+
*
|
|
26
|
+
* URL \uC548\uC5D0\uC11C\uB294 zero-width \uBB38\uC790\uC5D0 \uC815\uB2F9\uD55C \uC870\uD310 \uC758\uBBF8\uAC00 \uC5C6\uACE0 protocol \uC6B0\uD68C
|
|
27
|
+
* \uB09C\uB3C5\uD654\uC5D0 \uC4F0\uC77C \uC218 \uC788\uC73C\uBBC0\uB85C ZWJ \uAE4C\uC9C0 \uC804\uBD80 \uC81C\uAC70\uD55C\uB2E4 \u2014 \uACB0\uD569 \uC2DC\uD000\uC2A4 \uD574\uCCB4\uAC00
|
|
28
|
+
* \uC5EC\uAE30\uC11C\uB294 \uC758\uB3C4\uB41C \uB3D9\uC791\uC774\uBBC0\uB85C \uADDC\uCE59\uC744 \uBA85\uC2DC\uC801\uC73C\uB85C \uD574\uC81C\uD55C\uB2E4.
|
|
29
|
+
*/
|
|
30
|
+
var ZERO_WIDTH_STRICT_REGEX = /[\u200B\u200C\u200D\u200E\u200F\uFEFF]/gu;
|
|
15
31
|
function stripZeroWidth(value) {
|
|
16
|
-
|
|
17
|
-
return ZERO_WIDTH_REGEX.test(value) ? value.replace(ZERO_WIDTH_REGEX, "") : value;
|
|
32
|
+
return ZERO_WIDTH_DISPLAY_REGEX.test(value) ? value.replace(ZERO_WIDTH_DISPLAY_REGEX, "") : value;
|
|
18
33
|
}
|
|
19
34
|
/** HTML text context escape 대상 문자 */
|
|
20
35
|
var HTML_TEXT_ESCAPE_REGEX = /[&<>"']/g;
|
|
@@ -53,7 +68,10 @@ var HTML_ATTR_ESCAPE_MAP = {
|
|
|
53
68
|
function escapeHtmlAttr(value) {
|
|
54
69
|
return HTML_ATTR_ESCAPE_REGEX.test(value) ? value.replace(HTML_ATTR_ESCAPE_REGEX, (ch) => HTML_ATTR_ESCAPE_MAP[ch]) : value;
|
|
55
70
|
}
|
|
56
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* protocol 우회 공격 방지를 위한 공백/제어문자 제거.
|
|
73
|
+
* 제어문자 매칭이 이 상수의 목적 자체이므로 no-control-regex 를 의도적으로 해제한다.
|
|
74
|
+
*/
|
|
57
75
|
var HREF_STRIP_CHARS_REGEX = /[\u0000-\u001F\u007F\s]+/g;
|
|
58
76
|
/** href/src에서 차단해야 하는 위험 protocol */
|
|
59
77
|
var HREF_UNSAFE_PROTOCOL_REGEX = /^(?:javascript|data|vbscript):/i;
|
|
@@ -64,7 +82,7 @@ var HREF_UNSAFE_PROTOCOL_REGEX = /^(?:javascript|data|vbscript):/i;
|
|
|
64
82
|
* <a href="{value}">
|
|
65
83
|
*/
|
|
66
84
|
function escapeHtmlHref(value) {
|
|
67
|
-
let normalized =
|
|
85
|
+
let normalized = value.replace(ZERO_WIDTH_STRICT_REGEX, "").trim();
|
|
68
86
|
if (HREF_STRIP_CHARS_REGEX.test(normalized)) normalized = normalized.replace(HREF_STRIP_CHARS_REGEX, "");
|
|
69
87
|
normalized = normalized.replace(/\\/g, "/");
|
|
70
88
|
if (normalized.startsWith("//")) return "#";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/chat-components",
|
|
3
3
|
"description": "llm chat components for building chat interfaces",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"author": "iyulab",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -40,14 +40,16 @@
|
|
|
40
40
|
"start": "vite",
|
|
41
41
|
"build": "eslint && vite build",
|
|
42
42
|
"test": "vitest run",
|
|
43
|
-
"test:watch": "vitest"
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"lint": "eslint src/",
|
|
45
|
+
"lint:fix": "eslint src/ --fix"
|
|
44
46
|
},
|
|
45
47
|
"dependencies": {
|
|
46
|
-
"@iyulab/components": "^1.5.
|
|
48
|
+
"@iyulab/components": "^1.5.1",
|
|
47
49
|
"highlight.js": "^11.11.1",
|
|
48
50
|
"katex": "^0.17.0",
|
|
49
51
|
"lit": "^3.3.3",
|
|
50
|
-
"marked": "^18.0.
|
|
52
|
+
"marked": "^18.0.6",
|
|
51
53
|
"marked-katex-extension": "^5.1.10"
|
|
52
54
|
},
|
|
53
55
|
"peerDependencies": {
|
|
@@ -68,17 +70,17 @@
|
|
|
68
70
|
},
|
|
69
71
|
"devDependencies": {
|
|
70
72
|
"@eslint/js": "^9.39.4",
|
|
71
|
-
"@types/node": "^26.1.
|
|
73
|
+
"@types/node": "^26.1.1",
|
|
72
74
|
"@vitest/browser-playwright": "^4.1.10",
|
|
73
75
|
"chart.js": "^4.5.1",
|
|
74
76
|
"eslint": "^9.39.4",
|
|
75
77
|
"eslint-plugin-lit": "^2.3.1",
|
|
76
78
|
"glob": "^13.0.6",
|
|
77
79
|
"globals": "^17.7.0",
|
|
78
|
-
"openai": "^6.
|
|
80
|
+
"openai": "^6.47.0",
|
|
79
81
|
"typescript": "^5.9.3",
|
|
80
|
-
"typescript-eslint": "^8.
|
|
81
|
-
"vite": "^8.1.
|
|
82
|
+
"typescript-eslint": "^8.64.0",
|
|
83
|
+
"vite": "^8.1.4",
|
|
82
84
|
"vite-plugin-dts": "^5.0.3",
|
|
83
85
|
"vitest": "^4.1.10"
|
|
84
86
|
}
|