kordoc 4.14.4 → 4.15.1

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.
Files changed (174) hide show
  1. package/README.md +26 -1
  2. package/dist/{-Z4TJ3QGK.js → -HOFQN2B6.js} +15 -13
  3. package/dist/{chunk-DPWWXUOG.js → chunk-3WSNLH4B.js} +222 -36
  4. package/dist/chunk-3WSNLH4B.js.map +1 -0
  5. package/dist/{chunk-VJIL6PIL.js → chunk-5NU3ENDC.js} +2 -2
  6. package/dist/{chunk-7SE4EZ45.js → chunk-AMLVAG2D.js} +2 -2
  7. package/dist/{chunk-WJHAU73B.cjs → chunk-AMYZJ25W.cjs} +17 -6
  8. package/dist/chunk-AMYZJ25W.cjs.map +1 -0
  9. package/dist/{chunk-6CUIXJOC.js → chunk-ATGOFB7P.js} +1 -1
  10. package/dist/{chunk-6BWU6SQX.js.map → chunk-ATGOFB7P.js.map} +1 -1
  11. package/dist/{chunk-VDN7LMPL.js → chunk-CAVXZLCQ.js} +2 -2
  12. package/dist/{chunk-LCW7SEPG.js → chunk-E24LPQP7.js} +2 -2
  13. package/dist/{chunk-GS7T56RP.cjs → chunk-EKYI3Z6M.cjs} +2 -2
  14. package/dist/chunk-EKYI3Z6M.cjs.map +1 -0
  15. package/dist/{chunk-XVXHXQIO.js → chunk-EZO64OIB.js} +1741 -1715
  16. package/dist/chunk-EZO64OIB.js.map +1 -0
  17. package/dist/{chunk-YAKC46HZ.js → chunk-I6DJPTUG.js} +2 -2
  18. package/dist/{chunk-YAVHVFRY.js → chunk-IGVDBC3T.js} +127 -39
  19. package/dist/chunk-IGVDBC3T.js.map +1 -0
  20. package/dist/{chunk-TITDJWVZ.js → chunk-IKYSR5DO.js} +23 -3
  21. package/dist/chunk-IKYSR5DO.js.map +1 -0
  22. package/dist/{chunk-DCZVOIEO.cjs → chunk-J55UQC5H.cjs} +20 -2
  23. package/dist/chunk-J55UQC5H.cjs.map +1 -0
  24. package/dist/{chunk-QIF2VXBQ.js → chunk-K5JLJLBF.js} +41 -9
  25. package/dist/chunk-K5JLJLBF.js.map +1 -0
  26. package/dist/{chunk-D4U2TXDC.js → chunk-KTCMOJMO.js} +79 -68
  27. package/dist/chunk-KTCMOJMO.js.map +1 -0
  28. package/dist/{chunk-5LAZQCUJ.cjs → chunk-MZWXRAHL.cjs} +62 -30
  29. package/dist/chunk-MZWXRAHL.cjs.map +1 -0
  30. package/dist/{chunk-LFSQAMSM.js → chunk-NCSC7WHQ.js} +40 -29
  31. package/dist/chunk-NCSC7WHQ.js.map +1 -0
  32. package/dist/chunk-NTNZGLKD.js +32 -0
  33. package/dist/chunk-NTNZGLKD.js.map +1 -0
  34. package/dist/{chunk-USHQCF5T.cjs → chunk-PBKVXTQG.cjs} +158 -70
  35. package/dist/chunk-PBKVXTQG.cjs.map +1 -0
  36. package/dist/{chunk-VWUYVEFN.cjs → chunk-PY2M7YVN.cjs} +1 -1
  37. package/dist/chunk-PY2M7YVN.cjs.map +1 -0
  38. package/dist/{chunk-NLU6LBLY.js → chunk-R5IRAI37.js} +17 -9
  39. package/dist/chunk-R5IRAI37.js.map +1 -0
  40. package/dist/{chunk-6BWU6SQX.js → chunk-RIVHAAV4.js} +1 -1
  41. package/dist/{chunk-6CUIXJOC.js.map → chunk-RIVHAAV4.js.map} +1 -1
  42. package/dist/{chunk-QEJUY6SK.cjs → chunk-RK5O53QZ.cjs} +3 -3
  43. package/dist/chunk-RK5O53QZ.cjs.map +1 -0
  44. package/dist/{chunk-MG6DJEX4.js → chunk-STC2OLKL.js} +40 -29
  45. package/dist/chunk-STC2OLKL.js.map +1 -0
  46. package/dist/{chunk-HIJUKZHM.js → chunk-TSRZ2QYT.js} +11 -13
  47. package/dist/{chunk-HIJUKZHM.js.map → chunk-TSRZ2QYT.js.map} +1 -1
  48. package/dist/{chunk-GLSBEEZJ.js → chunk-V25RXRBG.js} +2 -2
  49. package/dist/{chunk-GE43BE46.js → chunk-V4BH5SRQ.js} +20 -2
  50. package/dist/chunk-V4BH5SRQ.js.map +1 -0
  51. package/dist/{chunk-MOL7MDBG.js → chunk-VQFA55IC.js} +20 -2
  52. package/dist/chunk-VQFA55IC.js.map +1 -0
  53. package/dist/{chunk-V3DX4DUF.js → chunk-WFGECRCK.js} +2 -2
  54. package/dist/{chunk-YT3GC62E.js → chunk-WN7TX63H.js} +127 -39
  55. package/dist/chunk-WN7TX63H.js.map +1 -0
  56. package/dist/{chunk-WJJGW5PL.js → chunk-XTVXSZLI.js} +2 -2
  57. package/dist/{chunk-GUJ4Q73I.js → chunk-XZDCCPXJ.js} +14 -3
  58. package/dist/chunk-XZDCCPXJ.js.map +1 -0
  59. package/dist/{chunk-IMAAFSAX.cjs → chunk-ZFFGEHEX.cjs} +44 -33
  60. package/dist/chunk-ZFFGEHEX.cjs.map +1 -0
  61. package/dist/chunks-7BPHK374.js +10 -0
  62. package/dist/cli.js +45 -45
  63. package/dist/cli.js.map +1 -1
  64. package/dist/{formula-5LOVHAKE.cjs → formula-GNI3H5DK.cjs} +26 -16
  65. package/dist/formula-GNI3H5DK.cjs.map +1 -0
  66. package/dist/{formula-7RANCW5L.js → formula-UU7MMWPQ.js} +20 -8
  67. package/dist/{formula-7RANCW5L.js.map → formula-UU7MMWPQ.js.map} +1 -1
  68. package/dist/{formula-7UYJOVTB.js → formula-X32XVHDP.js} +18 -10
  69. package/dist/{formula-7UYJOVTB.js.map → formula-X32XVHDP.js.map} +1 -1
  70. package/dist/{image-ocr-NYBJEOH2.cjs → image-ocr-6FFSJ453.cjs} +36 -22
  71. package/dist/image-ocr-6FFSJ453.cjs.map +1 -0
  72. package/dist/{image-ocr-ZV6PDBUQ.js → image-ocr-D2FGNPSN.js} +27 -13
  73. package/dist/image-ocr-D2FGNPSN.js.map +1 -0
  74. package/dist/{image-ocr-I6JOCATS.js → image-ocr-L3762E4X.js} +29 -15
  75. package/dist/image-ocr-L3762E4X.js.map +1 -0
  76. package/dist/index.cjs +1794 -1575
  77. package/dist/index.cjs.map +1 -1
  78. package/dist/index.d.cts +11 -17
  79. package/dist/index.d.ts +11 -17
  80. package/dist/index.js +2028 -1809
  81. package/dist/index.js.map +1 -1
  82. package/dist/mcp.js +46 -34
  83. package/dist/mcp.js.map +1 -1
  84. package/dist/{model-bundle-2C5IYDLE.js → model-bundle-NCHSUEEK.js} +5 -5
  85. package/dist/{models-SEQZHZFT.js → models-LD35H2GR.js} +5 -5
  86. package/dist/page-range-M5SRB2WK.js +9 -0
  87. package/dist/page-range-OKVE3DBS.js +10 -0
  88. package/dist/page-range-XPJTQHZK.cjs +10 -0
  89. package/dist/page-range-XPJTQHZK.cjs.map +1 -0
  90. package/dist/{parser-3FNXEP6T.js → parser-3Q4NKZA5.js} +94 -40
  91. package/dist/parser-3Q4NKZA5.js.map +1 -0
  92. package/dist/{parser-HVEKF73B.cjs → parser-UK5ITZUH.cjs} +163 -111
  93. package/dist/parser-UK5ITZUH.cjs.map +1 -0
  94. package/dist/{parser-FJBJXRJK.js → parser-ZX2DXJXE.js} +92 -40
  95. package/dist/parser-ZX2DXJXE.js.map +1 -0
  96. package/dist/pdf-ocr-DDAR6KEC.js +18 -0
  97. package/dist/pdf-ocr-OMLFE4MQ.js +15 -0
  98. package/dist/pdf-ocr-YMPMO4VB.cjs +16 -0
  99. package/dist/pdf-ocr-YMPMO4VB.cjs.map +1 -0
  100. package/dist/{profile-io-RUFZOZO6.js → profile-io-P2PX5RMA.js} +3 -3
  101. package/dist/{rasterize-IOYXR5SI.js → rasterize-XR6C6XXA.js} +3 -3
  102. package/dist/{redact-NMIKVHMP.js → redact-NV2QCDGE.js} +2 -2
  103. package/dist/{redact-doc-QBHNJ2BU.js → redact-doc-UCLPLJTY.js} +13 -12
  104. package/dist/{redact-doc-QBHNJ2BU.js.map → redact-doc-UCLPLJTY.js.map} +1 -1
  105. package/dist/{render-YZ2TEC3N.js → render-GDKZFZRO.js} +8 -8
  106. package/dist/seal-IYAANGGT.js +10 -0
  107. package/dist/{setup-MPIEP6XT.js → setup-D726M3LY.js} +3 -3
  108. package/dist/visual-QTIWR7TC.js +20 -0
  109. package/dist/{watch-RYO7MN5B.js → watch-D4INCK6G.js} +48 -24
  110. package/dist/watch-D4INCK6G.js.map +1 -0
  111. package/package.json +1 -1
  112. package/dist/chunk-5LAZQCUJ.cjs.map +0 -1
  113. package/dist/chunk-D4U2TXDC.js.map +0 -1
  114. package/dist/chunk-DCZVOIEO.cjs.map +0 -1
  115. package/dist/chunk-DPWWXUOG.js.map +0 -1
  116. package/dist/chunk-GE43BE46.js.map +0 -1
  117. package/dist/chunk-GS7T56RP.cjs.map +0 -1
  118. package/dist/chunk-GUJ4Q73I.js.map +0 -1
  119. package/dist/chunk-IMAAFSAX.cjs.map +0 -1
  120. package/dist/chunk-LFSQAMSM.js.map +0 -1
  121. package/dist/chunk-MG6DJEX4.js.map +0 -1
  122. package/dist/chunk-MOL7MDBG.js.map +0 -1
  123. package/dist/chunk-NLU6LBLY.js.map +0 -1
  124. package/dist/chunk-QEJUY6SK.cjs.map +0 -1
  125. package/dist/chunk-QIF2VXBQ.js.map +0 -1
  126. package/dist/chunk-TITDJWVZ.js.map +0 -1
  127. package/dist/chunk-USHQCF5T.cjs.map +0 -1
  128. package/dist/chunk-VWUYVEFN.cjs.map +0 -1
  129. package/dist/chunk-WJHAU73B.cjs.map +0 -1
  130. package/dist/chunk-XVXHXQIO.js.map +0 -1
  131. package/dist/chunk-YAVHVFRY.js.map +0 -1
  132. package/dist/chunk-YT3GC62E.js.map +0 -1
  133. package/dist/chunks-CCMRL4MJ.js +0 -10
  134. package/dist/formula-5LOVHAKE.cjs.map +0 -1
  135. package/dist/image-ocr-I6JOCATS.js.map +0 -1
  136. package/dist/image-ocr-NYBJEOH2.cjs.map +0 -1
  137. package/dist/image-ocr-ZV6PDBUQ.js.map +0 -1
  138. package/dist/page-range-737B4EZW.js +0 -8
  139. package/dist/page-range-LNMXJU6W.js +0 -7
  140. package/dist/page-range-P7SDW6LR.cjs +0 -8
  141. package/dist/page-range-P7SDW6LR.cjs.map +0 -1
  142. package/dist/parser-3FNXEP6T.js.map +0 -1
  143. package/dist/parser-FJBJXRJK.js.map +0 -1
  144. package/dist/parser-HVEKF73B.cjs.map +0 -1
  145. package/dist/pdf-ocr-JRDBJ6TA.cjs +0 -14
  146. package/dist/pdf-ocr-JRDBJ6TA.cjs.map +0 -1
  147. package/dist/pdf-ocr-UUBWBEHL.js +0 -16
  148. package/dist/pdf-ocr-VJSF2SQC.js +0 -13
  149. package/dist/seal-ECV44TUH.js +0 -10
  150. package/dist/visual-ICMOWUCR.js +0 -19
  151. package/dist/watch-RYO7MN5B.js.map +0 -1
  152. /package/dist/{-Z4TJ3QGK.js.map → -HOFQN2B6.js.map} +0 -0
  153. /package/dist/{chunk-VJIL6PIL.js.map → chunk-5NU3ENDC.js.map} +0 -0
  154. /package/dist/{chunk-7SE4EZ45.js.map → chunk-AMLVAG2D.js.map} +0 -0
  155. /package/dist/{chunk-VDN7LMPL.js.map → chunk-CAVXZLCQ.js.map} +0 -0
  156. /package/dist/{chunk-LCW7SEPG.js.map → chunk-E24LPQP7.js.map} +0 -0
  157. /package/dist/{chunk-YAKC46HZ.js.map → chunk-I6DJPTUG.js.map} +0 -0
  158. /package/dist/{chunk-GLSBEEZJ.js.map → chunk-V25RXRBG.js.map} +0 -0
  159. /package/dist/{chunk-V3DX4DUF.js.map → chunk-WFGECRCK.js.map} +0 -0
  160. /package/dist/{chunk-WJJGW5PL.js.map → chunk-XTVXSZLI.js.map} +0 -0
  161. /package/dist/{chunks-CCMRL4MJ.js.map → chunks-7BPHK374.js.map} +0 -0
  162. /package/dist/{model-bundle-2C5IYDLE.js.map → model-bundle-NCHSUEEK.js.map} +0 -0
  163. /package/dist/{models-SEQZHZFT.js.map → models-LD35H2GR.js.map} +0 -0
  164. /package/dist/{page-range-737B4EZW.js.map → page-range-M5SRB2WK.js.map} +0 -0
  165. /package/dist/{page-range-LNMXJU6W.js.map → page-range-OKVE3DBS.js.map} +0 -0
  166. /package/dist/{pdf-ocr-UUBWBEHL.js.map → pdf-ocr-DDAR6KEC.js.map} +0 -0
  167. /package/dist/{pdf-ocr-VJSF2SQC.js.map → pdf-ocr-OMLFE4MQ.js.map} +0 -0
  168. /package/dist/{profile-io-RUFZOZO6.js.map → profile-io-P2PX5RMA.js.map} +0 -0
  169. /package/dist/{rasterize-IOYXR5SI.js.map → rasterize-XR6C6XXA.js.map} +0 -0
  170. /package/dist/{redact-NMIKVHMP.js.map → redact-NV2QCDGE.js.map} +0 -0
  171. /package/dist/{render-YZ2TEC3N.js.map → render-GDKZFZRO.js.map} +0 -0
  172. /package/dist/{seal-ECV44TUH.js.map → seal-IYAANGGT.js.map} +0 -0
  173. /package/dist/{setup-MPIEP6XT.js.map → setup-D726M3LY.js.map} +0 -0
  174. /package/dist/{visual-ICMOWUCR.js.map → visual-QTIWR7TC.js.map} +0 -0
package/README.md CHANGED
@@ -89,6 +89,29 @@ MCP 등록 대신 스킬(SKILL.md) 형태로 쓰려면:
89
89
 
90
90
  ---
91
91
 
92
+ ## v4.15.1 변경사항
93
+
94
+ PDF 표 복원과 대형 입력의 안정성을 보완했습니다.
95
+
96
+ - **PDF 안정성과 속도**: 매우 긴 괘선 때문에 메모리가 폭증하던 문제를 고치고, 쪽 처리 후 자원 해제와 반복 그림 처리를 개선했습니다. 입력 ArrayBuffer를 재사용할 수 있으며, 쪽 수 상한으로 잘린 범위는 경고합니다.
97
+ - **PDF 글·표**: 원점이 이동하거나 회전된 쪽과 Form XObject 안의 좌표 처리를 바로잡았습니다. 좁은 빈 열, 쪽을 넘는 중첩표·비고를 복원하고 표 아래 주석·출처가 꼬리말로 삭제되지 않게 했습니다. 쪽별 마크다운도 제공합니다.
98
+ - **OCR**: JPEG의 EXIF 방향을 반영합니다. 큰 이미지는 최대 24 MP로 축소하고, 검출 상자는 3,000개까지 인식하며 축소·상한 초과는 경고합니다. 괘선 검출의 반복 계산도 줄였습니다.
99
+ - **인명 마스킹**: 명시적인 속기록·회의록 문맥의 이름 4건을 추가 탐지했습니다(opt-in). 같은 외부 정답에서 361/411, 오탐 0건이며 미탐 50건은 남아 있습니다.
100
+
101
+ 자세한 변경 내용은 [CHANGELOG](CHANGELOG.md)를 참고하세요.
102
+
103
+ ## v4.15.0 변경사항
104
+
105
+ 실문서 코퍼스를 보강하고 프로덕션 리뷰에서 확인한 파싱·보안·성능 문제를 고쳤습니다.
106
+
107
+ - **긴 스프레드시트**: XLSX·XLS의 1만 행 무경고 절단을 수정하고, 칸 예산을 초과하면 경고합니다. XLSX는 XML을 행 묶음으로 읽어 대형 시트 메모리를 줄이고, XLS는 희소 격자를 사용합니다. 동아시아 날짜와 DOCX 머리글·바닥글도 보완했습니다.
108
+ - **PDF 글**: 실제 낱말을 붙이던 균등배분 정리, 표 칸의 글자 조각, 날짜 빈칸, 넓은 줄간격의 문단 이음을 고쳤습니다.
109
+ - **보안**: HTML 표 칸의 원문을 이스케이프하고, 인쇄·렌더 PDF는 JavaScript를 끄고 `data:`·`about:` 외 요청을 차단합니다. webhook의 IPv6 SSRF 방어와 MCP 쓰기 경로 제한도 보강했습니다.
110
+ - **안정성과 속도**: CLI·MCP·parse-worker의 진단 메시지를 stderr로 보내 JSON 출력을 보호합니다. 확장자가 실제 형식과 다른 문서의 무손실 패치를 고치고, 문서 비교·여러 구간 치환의 반복 계산을 줄였습니다.
111
+ - **인명·주소 마스킹**: 회의록 발언자·기관 직함 앞 이름·결재란과 도로명 변형을 보강하고 오탐을 줄였습니다(opt-in).
112
+ - **디렉토리 감시**: `watch -d`가 하위 폴더 구조를 보존해 같은 파일명끼리 덮어쓰지 않습니다. [PR #82](https://github.com/chrisryugj/kordoc/pull/82), 기여: @ROTl24.
113
+ - **검증 코퍼스**: 정책브리핑 HWPX↔PDF 300쌍, DOCX↔LibreOffice PDF 36쌍과 XLS 정답지 트랙을 추가했습니다. 자세한 실측은 [CHANGELOG](CHANGELOG.md)를 참고하세요.
114
+
92
115
  ## v4.14.4 변경사항
93
116
 
94
117
  읽기 품질을 한 번 더 점검했습니다. 원본 HWPX 를 정답으로 PDF 글을 재는 벤치를 새로 세워 띄어쓰기·읽기 순서까지 보고, PDF 표·OCR·개인정보 마스킹을 고쳤습니다.
@@ -932,7 +955,7 @@ CLI 는 `kordoc 문서.pdf --format json --no-images`. 대량 변환은 `kordoc
932
955
  stdin 으로 한 줄씩 요청합니다 (한 줄 = 한 JSON).
933
956
 
934
957
  ```text
935
- 시작 {"ready":true,"version":"4.14.4","protocol":1}
958
+ 시작 {"ready":true,"version":"4.15.0","protocol":1}
936
959
  요청 {"id":1,"file":"문서.hwpx","images":false,"ocr":"off"}
937
960
  응답 {"id":1,"rss":183500800,"result":{ …--format json 과 같은 결과, 실패도 success:false 로… }}
938
961
  종료 {"cmd":"quit"} (또는 stdin 닫기)
@@ -1030,6 +1053,8 @@ npx kordoc watch ./수신함 -d ./변환결과 # 폴더 감시 모
1030
1053
  npx kordoc watch ./문서 --webhook https://api/hook # 웹훅 알림
1031
1054
  ```
1032
1055
 
1056
+ `watch -d` 는 감시 폴더 안의 하위 폴더 구조를 출력에도 그대로 둡니다: `수신함/팀/보고서.hwpx` → `변환결과/팀/보고서.md`(또는 `.json`). `수신함` 바로 아래 파일은 종전처럼 `변환결과` 바로 아래에 씁니다.
1057
+
1033
1058
  > `check-ocr-models` · `check-formula-models` 는 이름과 달리 **없거나 SHA 가 안 맞으면 내려받습니다**.
1034
1059
  > 상태만 보려면 `--status-only` 를 붙이세요 (수식 모델은 ~155MB).
1035
1060
  >
@@ -4,7 +4,6 @@ import {
4
4
  HwpxSession,
5
5
  PRESET_ALIAS,
6
6
  ValueCursor,
7
- blocksToPages,
8
7
  chooseTableRepresentation,
9
8
  classifyTable,
10
9
  classifyTableTree,
@@ -51,22 +50,25 @@ import {
51
50
  unknownFontWarnings,
52
51
  usesGaejosikMunche,
53
52
  validateHwpx
54
- } from "./chunk-XVXHXQIO.js";
53
+ } from "./chunk-EZO64OIB.js";
54
+ import {
55
+ blocksToPages
56
+ } from "./chunk-NTNZGLKD.js";
55
57
  import {
56
58
  applySplices,
57
59
  buildParagraphSplices,
58
60
  buildRangeSplices,
59
61
  placeSealHwpx,
60
62
  scanSectionXml
61
- } from "./chunk-HIJUKZHM.js";
63
+ } from "./chunk-TSRZ2QYT.js";
62
64
  import {
63
65
  DEFAULT_REDACT_RULES,
64
66
  redactMarkdown,
65
67
  redactText
66
- } from "./chunk-DPWWXUOG.js";
68
+ } from "./chunk-3WSNLH4B.js";
67
69
  import {
68
70
  blocksToChunks
69
- } from "./chunk-VJIL6PIL.js";
71
+ } from "./chunk-5NU3ENDC.js";
70
72
  import {
71
73
  SPACE_EM_FIXED,
72
74
  SPACE_EM_FONT,
@@ -91,20 +93,20 @@ import {
91
93
  renderSceneToHtml,
92
94
  simulateWrap,
93
95
  simulateWrapKeepWord
94
- } from "./chunk-D4U2TXDC.js";
96
+ } from "./chunk-KTCMOJMO.js";
95
97
  import "./chunk-7FCNOKOR.js";
96
- import "./chunk-6BWU6SQX.js";
97
- import "./chunk-MOL7MDBG.js";
98
- import "./chunk-YAKC46HZ.js";
98
+ import "./chunk-RIVHAAV4.js";
99
+ import "./chunk-VQFA55IC.js";
100
+ import "./chunk-I6DJPTUG.js";
99
101
  import {
100
102
  blocksToMarkdown,
101
103
  flattenLayoutTables,
102
104
  hasStructuredCellContent
103
- } from "./chunk-NLU6LBLY.js";
104
- import "./chunk-V3DX4DUF.js";
105
+ } from "./chunk-R5IRAI37.js";
106
+ import "./chunk-WFGECRCK.js";
105
107
  import {
106
108
  VERSION
107
- } from "./chunk-TITDJWVZ.js";
109
+ } from "./chunk-IKYSR5DO.js";
108
110
  export {
109
111
  BUILTIN_TEMPLATES,
110
112
  DEFAULT_REDACT_RULES,
@@ -194,4 +196,4 @@ export {
194
196
  usesGaejosikMunche,
195
197
  validateHwpx
196
198
  };
197
- //# sourceMappingURL=-Z4TJ3QGK.js.map
199
+ //# sourceMappingURL=-HOFQN2B6.js.map
@@ -318,18 +318,79 @@ var SIGUNGU = [
318
318
  var REGION = `(?:(?:${alt(SIDO)})(?:\\s{0,2}(?:${alt(SIGUNGU)})){0,2}|(?:${alt(SIGUNGU)})(?:\\s{0,2}(?:${alt(SIGUNGU)}))?)`;
319
319
  var EUPMYEON = "[\uAC00-\uD7A3]{1,4}(?:\uC74D|\uBA74)";
320
320
  var DONGRI = "[\uAC00-\uD7A3]{1,6}(?:\\d{0,2}(?:\uB3D9|\uB9AC)|\\d{1,2}\uAC00)";
321
- var ROAD = "[\uAC00-\uD7A30-9]{1,15}(?<!\uC73C)(?:\uB85C|\uAE38)(?:\\s\\d{1,4}(?:\uBC88|[\uAC00-\uD7A3])?\uAE38)?";
321
+ var ROAD = "(?:\\d{1,2}[\xB7.]\\d{1,2}[\uAC00-\uD7A3]{0,6}|[\uAC00-\uD7A3]{1,10}\\s\\d{1,3}|[\uAC00-\uD7A30-9]{1,15})(?<!\uC73C)(?:\uB85C|\uAE38)(?:\\s\\d{1,4}(?:\uBC88[\uAC00-\uD7A3]?|[\uAC00-\uD7A3])?\uAE38)?";
322
322
  var ADDR_END = "(?![0-9])(?!-\\d)(?![.,]\\d)(?![%\uFF05\u33A1\u339E\u339D\u339C\u33CA\u2103\xB0])(?=$|[^\uAC00-\uD7A3A-Za-z]|(?:\uC5D0\uC11C|\uC5D0\uAC8C|\uC5D0|\uC758|\uC740|\uB294|\uC774|\uAC00|\uC744|\uB97C|\uC640|\uACFC|\uB85C|\uC73C\uB85C|\uAE4C\uC9C0|\uBD80\uD130|\uBC88\uC9C0|\uC785\uB2C8\uB2E4|\uC774\uB2E4|\uC774\uBA70|\uC774\uACE0|\uC678|\uC77C\uB300|\uC77C\uC6D0|\uBD80\uADFC|\uC778\uADFC|\uC18C\uC7AC|\uC8FC\uBCC0|\uB0B4|\uC55E|\uC606|\uCABD))";
323
323
  var DETAIL = "(?:(?:\\s?,\\s?|\\s)(?:[\uAC00-\uD7A3A-Za-z0-9]{1,20}\\s){0,2}(?:\uC81C|\uC9C0\uD558|B)?\\s?\\d{1,5}(?:-\\d{1,4})?(?:\uD638|\uCE35)" + ADDR_END + ")?";
324
324
  var BLDG = "(?:\uC9C0\uD558\\s?)?\\d{1,5}(?:-\\d{1,5})?";
325
325
  var LOT = "(?:\uC0B0\\s?)?\\d{1,5}(?:-\\d{1,5})?(?:\\s?\uBC88\uC9C0)?";
326
- var ADDR_LABEL = spaced(["\uC8FC\uC18C", "\uC8FC\uC18C\uC9C0", "\uD604\uC8FC\uC18C", "\uAC70\uC8FC\uC9C0", "\uC18C\uC7AC\uC9C0", "\uBCF8\uC810", "\uC0AC\uC5C5\uC7A5", "\uC601\uC5C5\uC18C"]);
326
+ var ADDR_LABEL = spaced(["\uC8FC\uC18C", "\uC8FC\uC18C\uC9C0", "\uD604\uC8FC\uC18C", "\uAC70\uC8FC\uC9C0", "\uC18C\uC7AC\uC9C0", "\uBCF8\uC810", "\uC0AC\uC5C5\uC7A5", "\uC601\uC5C5\uC18C", "\uC704\uCE58"]);
327
+ var REF_NOUN = alt([
328
+ "\uC774\uB3D9",
329
+ "\uD65C\uB3D9",
330
+ "\uC6B4\uB3D9",
331
+ "\uD589\uB3D9",
332
+ "\uBCC0\uB3D9",
333
+ "\uD611\uB3D9",
334
+ "\uACF5\uB3D9",
335
+ "\uC790\uB3D9",
336
+ "\uC791\uB3D9",
337
+ "\uC5F0\uB3D9",
338
+ "\uAC00\uB3D9",
339
+ "\uCD9C\uB3D9",
340
+ "\uBC1C\uB3D9",
341
+ "\uB178\uB3D9",
342
+ "\uAC10\uB3D9",
343
+ "\uC18C\uB3D9",
344
+ "\uD30C\uB3D9",
345
+ "\uC9C4\uB3D9",
346
+ "\uAE30\uB3D9",
347
+ "\uC720\uB3D9",
348
+ "\uBD80\uB3D9",
349
+ "\uD569\uB3D9",
350
+ "\uAD00\uB9AC",
351
+ "\uCC98\uB9AC",
352
+ "\uC815\uB9AC",
353
+ "\uC218\uB9AC",
354
+ "\uC6D0\uB9AC",
355
+ "\uB17C\uB9AC",
356
+ "\uAC70\uB9AC",
357
+ "\uBB34\uB9AC",
358
+ "\uC694\uB9AC",
359
+ "\uC790\uB9AC",
360
+ "\uACBD\uB9AC",
361
+ "\uC2EC\uB9AC",
362
+ "\uC724\uB9AC",
363
+ "\uC9C4\uB9AC",
364
+ "\uAD8C\uB9AC",
365
+ "\uD3B8\uB9AC",
366
+ "\uD569\uB9AC",
367
+ "\uB300\uB9AC",
368
+ "\uCD1D\uB9AC"
369
+ ]);
370
+ var REF = `\\s?\\((?!(?:${REF_NOUN})[),])[\uAC00-\uD7A3]{1,6}(?:\\d{0,2}(?:\uB3D9|\uB9AC)|\\d{1,2}\uAC00)(?:,\\s?[^)]{1,30})?\\)`;
327
371
  var PLACEHOLDER_NO = /(?<![\d-])0+(?![\d])/;
328
372
  var realNumber = (x) => !PLACEHOLDER_NO.test(x.m.groups?.rest ?? "");
329
373
  var ADDRESS_VARIANTS = [
330
374
  {
331
- // 도로명 주소
332
- re: new RegExp(`(?<![\uAC00-\uD7A3])(?<region>${REGION})\\s{1,2}(?<rest>(?:${EUPMYEON}\\s{1,2})?${ROAD}\\s{0,2}${BLDG}${ADDR_END}${DETAIL})`, "dg"),
375
+ // 도로명 주소 — 읍·면, 동·리("서울시 도봉구 창동 우이천로 4다길 9"·"서천군 판교면 판교리 대백제로 1999") 뒤도, 참고항목까지
376
+ re: new RegExp(`(?<![\uAC00-\uD7A3])(?<region>${REGION})\\s{1,2}(?<rest>(?:${EUPMYEON}\\s{1,2})?(?:${DONGRI}\\s{1,2})?${ROAD}\\s{0,2}${BLDG}${ADDR_END}${DETAIL}(?:${REF})?)`, "dg"),
377
+ mask: ["rest"],
378
+ ok: realNumber
379
+ },
380
+ {
381
+ // 읍 뒤 도로명 (시·군 없이) — "청양읍 칠갑산로 1200", "보성읍 녹차로 999". 면은 받지 않는다 — 측면·전면·이면 같은 명사 뒤
382
+ // "도로 1," 모양이 주소와 갈리지 않는다. 읍부터 가린다(읍·면 단위는 늘 가림 쪽)
383
+ re: new RegExp(`(?<![\uAC00-\uD7A3])(?<rest>[\uAC00-\uD7A3]{1,4}\uC74D\\s{1,2}${ROAD}\\s{0,2}${BLDG}${ADDR_END}${DETAIL}(?:${REF})?)`, "dg"),
384
+ span: "rest",
385
+ mask: ["rest"],
386
+ ok: realNumber
387
+ },
388
+ {
389
+ // 행정구역·라벨 없이 도로명 + 건물번호 + 참고항목 — "도봉로 1234(쌍문동)", "해변길 77(서정동)"(시설 안내). 참고항목이
390
+ // 도로명주소 표기의 문법 증거다. 괄호 없는 "세종대로 209"는 받지 않는다 — 같은 모양의 명사+조사+수("전과별로 2~20년",
391
+ // "위로 800 ㎜", "경사로 1,")와 교육과정 기호("12진로01-01")가 코퍼스의 그 모양(미탐 672곳) 절반이었다
392
+ re: new RegExp(`(?<![\uAC00-\uD7A30-9\xB7.])(?<rest>${ROAD}\\s{0,2}${BLDG}${ADDR_END}${DETAIL}${REF})`, "dg"),
393
+ span: "rest",
333
394
  mask: ["rest"],
334
395
  ok: realNumber
335
396
  },
@@ -346,8 +407,8 @@ var ADDRESS_VARIANTS = [
346
407
  ok: realNumber
347
408
  },
348
409
  {
349
- // 라벨 뒤 — 행정구역 없이 도로명·동리부터 ("주소: 자양로 117", "소재지 구월동 1138")
350
- re: new RegExp(`(?:${ADDR_LABEL})\\s{0,2}[:\uFF1A]?\\s{0,2}(?<rest>(?:${EUPMYEON}\\s{1,2})?(?:${ROAD}\\s{0,2}${BLDG}|${DONGRI}\\s{1,2}${LOT})${ADDR_END}${DETAIL})`, "dg"),
410
+ // 라벨 뒤 — 행정구역 없이 도로명·동리부터 ("주소: 자양로 117", "소재지 구월동 1138", 표 칸 "| 위 치 | 해변길 77(서정동) |")
411
+ re: new RegExp(`(?:${ADDR_LABEL})(?:\\s{0,2}[:\uFF1A]?\\s{0,2}|\\s*(?:\\||<\\/t[dh]>\\s*<t[dh][^>]*>)\\s*)(?<rest>(?:${EUPMYEON}\\s{1,2})?(?:${ROAD}\\s{0,2}${BLDG}|${DONGRI}\\s{1,2}${LOT})${ADDR_END}${DETAIL}(?:${REF})?)`, "dg"),
351
412
  span: "rest",
352
413
  mask: ["rest"],
353
414
  ok: realNumber
@@ -416,7 +477,7 @@ var P_ALL = alt([...PARTICLE_ALWAYS, ...PARTICLE_COND]);
416
477
  var P_ALWAYS = alt(PARTICLE_ALWAYS);
417
478
  var NAME_AFTER_RE = new RegExp(`^(?:\uC528|\uB2D8)?(?:${P_ALL})?(?![\uAC00-\uD7A3])`);
418
479
  var nameParticleAt = (s, i) => NAME_AFTER_RE.test(s.slice(i, i + 6));
419
- var NOT_NAME_FINAL = "\uC758\uC744\uB97C\uB294\uC640\uACFC\uC5D0\uAED8\uB85C\uC73C\uB41C\uB2E4\uBC1B\uB4E4\uD588\uBA74\uD574\uC5C6\uC788";
480
+ var NOT_NAME_FINAL = "\uC758\uC744\uB97C\uB294\uC640\uACFC\uC5D0\uAED8\uB85C\uC73C\uB41C\uB2E4\uBC1B\uB4E4\uD588\uBA74\uD574\uC5C6\uC788\uBA70";
420
481
  function hangulName(surnames, tail) {
421
482
  return `(?:(?<dsur>${alt(DOUBLE_SURNAMES)})(?<dgiven>[\uAC00-\uD7A3]{1,2}?)(?=${tail(P_ALL)})|(?<sur>[${surnames}])(?:(?<given>[\uAC00-\uD7A3]{2})(?<![${NOT_NAME_FINAL}])(?=${tail(P_ALL)})|(?<given1>(?![${NOT_NAME_FINAL}])[\uAC00-\uD7A3])(?=${tail(P_ALWAYS)})))`;
422
483
  }
@@ -573,7 +634,7 @@ var DOUBLE_SURNAME_HANJA = {
573
634
  };
574
635
  var LATIN_NAME = "(?<latin>(?<l1>[A-Z])(?<lrest>[a-z]{1,15}(?:[ -][A-Z][a-z]{1,15}){1,2}))";
575
636
  var CJK_NAME = "(?<cjk>(?<c1>[\\u4E00-\\u9FFF])(?<crest>[\\u4E00-\\u9FFF]{1,3}))";
576
- var FOREIGN_FOLLOW = `(?![A-Za-z\\u4E00-\\u9FFF])(?=(?:${P_ALL})?(?![\uAC00-\uD7A3]))`;
637
+ var FOREIGN_FOLLOW = `(?![A-Za-z\\u4E00-\\u9FFF])(?![ -][A-Z])(?=(?:${P_ALL})(?![\uAC00-\uD7A3])|(?![\uAC00-\uD7A3])(?![ \\t]+[\uAC00-\uD7A3]))`;
577
638
  var PARTY_ROLES = [
578
639
  "\uD53C\uACE0\uC778",
579
640
  "\uD53C\uC758\uC790",
@@ -1348,14 +1409,75 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
1348
1409
  "\uC5F0\uD569",
1349
1410
  "\uC1A1\uB2EC",
1350
1411
  "\uCD5C\uC885",
1351
- "\uB3C4\uCCAD"
1412
+ "\uB3C4\uCCAD",
1413
+ // 코퍼스 5,891건(2026-09 보강분 포함) 실측 — 연락 수단("문자(010-…)"), 결재란 칸 "공석", 약칭·관용구("공정위"는 ORG_FINAL, "전경련 팀장",
1414
+ // "담당자 한자리에", "동·이업종 팀장"), 인사 표 머리칸("신보직 | 성 명 | 현보직"), 직함 뒤 문서·행사("장관 연설문",
1415
+ // "자문위원 위촉식"), 접미 규칙(SUFFIX_FINAL)의 앞 낱말("원우회 부회장"·"연례회의"·"선박명"·"기계명")
1416
+ "\uBB38\uC790",
1417
+ "\uACF5\uC11D",
1418
+ "\uC804\uACBD\uB828",
1419
+ "\uD55C\uC790\uB9AC",
1420
+ "\uC774\uC5C5\uC885",
1421
+ "\uD604\uBCF4\uC9C1",
1422
+ "\uC804\uBCF4\uC9C1",
1423
+ "\uC2E0\uBCF4\uC9C1",
1424
+ "\uC5F0\uC124\uBB38",
1425
+ "\uC704\uCD09\uC2DD",
1426
+ "\uC6D0\uC6B0",
1427
+ "\uC5F0\uB840",
1428
+ "\uC120\uBC15",
1429
+ "\uAE30\uACC4",
1430
+ // 기관 직함 앞 이름 자리("주요국 재무장관 면담", "운영의 주체인 행정안전부장관", "고위직 기관장의", "오늘날 상공부장관이",
1431
+ // "우리가 상공부장관의", "안보리 이사국 외교장관", "IMF 조사국 부국장", 띄어 쓴 "정보통 신부장 관")
1432
+ "\uC8FC\uC694\uAD6D",
1433
+ "\uC8FC\uCCB4",
1434
+ "\uACE0\uC704\uC9C1",
1435
+ "\uC704\uCD09\uC9C1",
1436
+ "\uC784\uBA85\uC9C1",
1437
+ "\uC120\uCD9C\uC9C1",
1438
+ "\uC815\uBB34\uC9C1",
1439
+ "\uC624\uB298\uB0A0",
1440
+ "\uC6B0\uB9AC",
1441
+ "\uC774\uC0AC\uAD6D",
1442
+ "\uC870\uC0AC\uAD6D",
1443
+ "\uC815\uBCF4\uD1B5",
1444
+ // 역할어 뒤 두 글자 명사 — 법정·신상("피고인 구속을"·"피해자 신체를"·"증인 신문을"·"원고 지위를")과 가족·관계
1445
+ // ("피해자 남편과"·"피해자 지인에게"·"피해자 조모의"·"회장 장남에게"), "운전자 방향의"(코퍼스)
1446
+ "\uAD6C\uC18D",
1447
+ "\uC11D\uBC29",
1448
+ "\uC120\uACE0",
1449
+ "\uC2E0\uBB38",
1450
+ "\uC2E0\uC6D0",
1451
+ "\uC2E0\uBD84",
1452
+ "\uC2E0\uCCB4",
1453
+ "\uC9C0\uC704",
1454
+ "\uBC29\uD5A5",
1455
+ "\uB0A8\uD3B8",
1456
+ "\uC7A5\uB0A8",
1457
+ "\uC7A5\uB140",
1458
+ "\uCC28\uB0A8",
1459
+ "\uCC28\uB140",
1460
+ "\uC190\uC790",
1461
+ "\uC190\uB140",
1462
+ "\uC870\uBAA8",
1463
+ "\uC870\uBD80",
1464
+ "\uC870\uCE74",
1465
+ "\uBAA8\uCE5C",
1466
+ "\uC624\uBE60",
1467
+ "\uB3D9\uC0DD",
1468
+ "\uC9C0\uC778",
1469
+ "\uC774\uC6C3",
1470
+ "\uB3D9\uB8CC",
1471
+ "\uD615\uC81C",
1472
+ "\uACE0\uBAA8",
1473
+ "\uC774\uBAA8"
1352
1474
  ]);
1353
1475
  var PARTICLE_GIVEN = /* @__PURE__ */ new Set(["\uC5D0\uC11C", "\uC5D0\uAC8C", "\uBD80\uD130", "\uAE4C\uC9C0", "\uB77C\uB3C4", "\uCC98\uB7FC", "\uBCF4\uB2E4", "\uAED8\uC11C", "\uD55C\uD14C", "\uC73C\uB85C", "\uC774\uB2E4", "\uC5D0\uB294", "\uC5D0\uB3C4", "\uC774\uB098", "\uC774\uB791"]);
1354
- var SUFFIX_FINAL = "\uC740\uB294\uC774\uAC00\uC744\uB97C\uC758\uC640\uACFC\uB3C4\uB9CC\uC5D0\uB85C\uB098\uB791\uAED8\uD558\uD55C\uB420\uD560\uBCC4\uC801\uD654\uB960\uC728\uCE35\uACC4\uB825\uBC95\uD615\uC81C\uB300\uC778\uC790\uC2DC\uAD70\uAD6C\uC131\uAD8C\uC77C\uBCF8\uD6C4\uC11C\uB8CC\uC232\uC18D";
1355
- var ORG_FINAL = /(?:과|팀|실|계|처|청|단|반|부|장|시|군|센터|협회|조합|공단|공사|재단|법인|병원|학교|대학|회사|은행|위원회|사무소|지청|본부)$/;
1476
+ var SUFFIX_FINAL = "\uC740\uB294\uC774\uAC00\uC744\uB97C\uC758\uC640\uACFC\uB3C4\uB9CC\uC5D0\uB85C\uB098\uB791\uAED8\uD558\uD55C\uB420\uD560\uBCC4\uC801\uD654\uB960\uC728\uCE35\uACC4\uB825\uBC95\uD615\uC81C\uB300\uC778\uC790\uC2DC\uAD70\uAD6C\uC131\uAD8C\uC77C\uBCF8\uD6C4\uC11C\uB8CC\uC232\uC18D\uD68C\uBA85\uC804\uD559\uC7A5";
1477
+ var ORG_FINAL = /(?:과|팀|실|계|처|청|단|반|부|장|시|군|위|점|센터|협회|조합|공단|공사|재단|법인|병원|학교|대학|회사|은행|위원회|사무소|지청|본부)$/;
1356
1478
  var REGION_WORDS = /* @__PURE__ */ new Set([...SIDO, ...SIGUNGU, ...SIGUNGU.filter((w) => w.length >= 3).map((w) => w.slice(0, -1))]);
1357
1479
  var NON_NAME_END = /(?:적|하여|하는|하고|하며|하게|하기|하면|하다|한다|했다|는데|니다|습니|다고|해야|해서|되는|되어|된다|지만|니까|와서|워서)$/;
1358
- var FOREIGN_SYLLABLE = /[스트크드브프즈츠끄뜨쁘쓰쯔플클블러케퍼커터텔톤튼픈핀펜헨앤엔엠켓렛]/;
1480
+ var FOREIGN_SYLLABLE = /[스트크드브프즈츠끄뜨쁘쓰쯔플클블러케퍼커터텔톤튼픈핀펜헨앤엔엠켓렛쇼핑]/;
1359
1481
  var TITLE_SET = /* @__PURE__ */ new Set([...TITLES, ...RANKS, ...PARTY_ROLES]);
1360
1482
  var g = (x, name) => x.m.groups?.[name] ?? "";
1361
1483
  var nameOf = (x) => g(x, "dsur") + g(x, "dgiven") + g(x, "sur") + g(x, "given") + g(x, "given1");
@@ -1364,13 +1486,27 @@ var nextIs = (x, re) => {
1364
1486
  const n = span(x);
1365
1487
  return !!n && re.test(x.m.input.slice(n[1], n[1] + 1));
1366
1488
  };
1367
- var closedAfter = (x) => nextIs(x, /^[가-힣,,))((]/);
1489
+ var afterName = (x, k = 2) => {
1490
+ const n = span(x);
1491
+ return n ? x.m.input.slice(n[1], n[1] + k) : "";
1492
+ };
1493
+ var closedAfter = (x) => /^(?:[은는이가의도와과,,))((]|께|에게|한테)/.test(afterName(x));
1368
1494
  var parenAfter = (x) => nextIs(x, /^[((]/);
1369
- function nameOk(x, level, two = false) {
1495
+ var dativeAfter = (x) => /^(?:에게|께|한테)/.test(afterName(x));
1496
+ var CELL_HEAD_RE = /(?:\||<t[dh][^>]*>|<br>)\s*(?:[((][^))]{1,10}[))]\s*)?(?:[가-힣]{2,4}\s*[,.·、]\s*)*$/;
1497
+ var phoneCellOk = (x) => {
1498
+ const n = span(x);
1499
+ const s = x.m.input;
1500
+ if (!/^\s*(?:\||<\/t[dh]>)/.test(s.slice(n[1], n[1] + 8))) return true;
1501
+ const before2 = s.slice(Math.max(0, n[0] - 200), n[0]);
1502
+ return CELL_HEAD_RE.test(n[0] <= 200 ? `|${before2}` : before2);
1503
+ };
1504
+ var universityAbbr = (x, t) => nameOf(x).endsWith("\uB300") && /교\s?수|총\s?장|학\s?장/.test(t);
1505
+ function nameOk(x, level, two = false, titled = false) {
1370
1506
  if (g(x, "latin") || g(x, "cjk")) return level === 2;
1371
1507
  const n = nameOf(x);
1372
1508
  const given = g(x, "dgiven") + g(x, "given") + g(x, "given1");
1373
- if (STOP_WORDS.has(n) || TITLE_SET.has(n) || REGION_WORDS.has(n) || ORG_FINAL.test(n) || NON_NAME_END.test(n) || FOREIGN_SYLLABLE.test(given) || PARTICLE_GIVEN.has(given)) return false;
1509
+ if (STOP_WORDS.has(n) || TITLE_SET.has(n) || REGION_WORDS.has(n) || ORG_FINAL.test(n) && !(titled && n.endsWith("\uC7A5")) || NON_NAME_END.test(n) || FOREIGN_SYLLABLE.test(given) || PARTICLE_GIVEN.has(given)) return false;
1374
1510
  if (n.length === 3 && SUFFIX_FINAL.includes(n[2]) && (STOP_WORDS.has(n.slice(0, 2)) || TITLE_SET.has(n.slice(0, 2)) || REGION_WORDS.has(n.slice(0, 2)))) return false;
1375
1511
  if (g(x, "dsur")) return true;
1376
1512
  if (g(x, "given1")) return level === 2 || two && COMMON_SURNAMES.includes(g(x, "sur"));
@@ -1380,7 +1516,7 @@ var NB = "(?<![\uAC00-\uD7A3A-Za-z0-9])(?<!(?:\\(\uC8FC\\)|\u321C)\\s?)";
1380
1516
  var NOT_ORG_AFTER = "(?!\\s?(?:\uC8FC\uC2DD\uD68C\uC0AC|\uC720\uD55C\uD68C\uC0AC|\\(\uC8FC\\)|\u321C|\\(\uC720\\)|\uBC95\uC778|\uC7AC\uB2E8|\uD611\uD68C|\uC870\uD569|\uACF5\uC0AC|\uACF5\uB2E8|\uC740\uD589|\uBCD1\uC6D0))";
1381
1517
  var NO_SPACING = /* @__PURE__ */ new Set(["\uC774\uC7A5", "\uB3D9\uC7A5", "\uBA74\uC7A5", "\uC74D\uC7A5"]);
1382
1518
  var titleAlt = (ts) => alt(ts.map((t) => NO_SPACING.has(t) ? t : t.split("").join("\\s?")));
1383
- var ORG_TITLE = `[\uAC00-\uD7A3]{1,12}(?:${alt([
1519
+ var ORG_TITLES = [
1384
1520
  "\uC7A5\uAD00",
1385
1521
  "\uCC28\uAD00",
1386
1522
  "\uCCAD\uC7A5",
@@ -1420,7 +1556,8 @@ var ORG_TITLE = `[\uAC00-\uD7A3]{1,12}(?:${alt([
1420
1556
  "\uC2EC\uC0AC\uAD00",
1421
1557
  "\uB2F4\uB2F9",
1422
1558
  "\uB2F4\uB2F9\uC790"
1423
- ])})`;
1559
+ ];
1560
+ var ORG_TITLE = `[\uAC00-\uD7A3]{1,12}(?:${alt(ORG_TITLES)})`;
1424
1561
  var NEXT_OK = `${titleAlt(TITLES)}|${ORG_TITLE}|${alt([...PARTY_ROLES, "\uC528", "\uB2D8", "\uC0AC\uC774", "\uB4F1", "\uC678", "\uBCF8\uC778", "\uBA85\uC758", "\uCE21", "\uADC0\uD558"])}`;
1425
1562
  var tailPlain = (p) => `(?:${p})?(?![\uAC00-\uD7A3])${NOT_ORG_AFTER}`;
1426
1563
  var tailRole = () => `(?:(?:${P_ALL})(?![\uAC00-\uD7A3])|(?![\uAC00-\uD7A3])(?![ \\t]+[\uAC00-\uD7A3])|[ \\t]+(?:${NEXT_OK})(?:${P_ALL})?(?![\uAC00-\uD7A3]))${NOT_ORG_AFTER}`;
@@ -1463,6 +1600,10 @@ var NAME_MASK = ["dgiven", "given", "given1", "lrest", "crest"];
1463
1600
  var ROLE_RE = alt(PARTY_ROLES);
1464
1601
  var TITLE_RE = titleAlt(TITLES);
1465
1602
  var TITLE_AFTER_RE = `(?:\uC81C\\d{1,2}\\s?)?(?:${titleAlt(TITLES_AFTER)})`;
1603
+ var NAME_ORG_TITLE = `[\uAC00-\uD7A3]{1,12}(?:${alt(ORG_TITLES.filter((t) => !["\uC704\uC6D0", "\uB2F4\uB2F9", "\uB2F4\uB2F9\uC790"].includes(t)))})|[\uAC00-\uD7A3]{2,12}(?:\uBD80|\uCC98|\uCCAD|\uC6D0|\uC704\uC6D0\uD68C|\uACF5\uC0AC|\uACF5\uB2E8|\uC7AC\uB2E8|\uD611\uD68C|\uBCF8\uBD80|\uC13C\uD130|\uC740\uD589)\\s${TITLE_AFTER_RE}`;
1604
+ var SPEAKER_MARK = "(?<![\u25CB\u25EF])[\u25CB\u25EF]";
1605
+ var SPEAKER_TITLE = `[\uAC00-\uD7A3]{0,12}?(?:${alt(TITLES)})(?:\uC9C1\uBB34\uB300\uB9AC|\uC9C1\uBB34\uB300\uD589|\uAD8C\uD55C\uB300\uD589)?`;
1606
+ var SPEAKER_TITLE_AFTER = alt(["\uC704\uC6D0\uC7A5", "\uBD80\uC704\uC6D0\uC7A5", "\uC18C\uC704\uC6D0\uC7A5", "\uC704\uC6D0", "\uC758\uC6D0", "\uC758\uC7A5", "\uBD80\uC758\uC7A5", "\uAC04\uC0AC"]);
1466
1607
  var NAME_HEADER_RE = new RegExp(`^\\s*(?:${spaced(["\uC131\uBA85", "\uC774\uB984", "\uC131\uD568", "\uB2F4\uB2F9\uC790", "\uB300\uD45C\uC790", "\uC608\uAE08\uC8FC", "\uC791\uC131\uC790", "\uC2E0\uCCAD\uC778", "\uCCAD\uAD6C\uC778", "\uBBFC\uC6D0\uC778", "\uC18C\uC720\uC790", "\uC218\uB839\uC778", "\uB300\uB9AC\uC778", "\uBCF4\uD638\uC790", "\uC138\uB300\uC8FC", "\uCC38\uC11D\uC790"])})(?:\\s*\\([^)]*\\))?\\s*$`);
1467
1608
  var LIST_GAP_RE = /^(?:와|과|이나|나|이랑|랑)?\s{0,2}[,、·]?\s{0,2}$/;
1468
1609
  var hanjaMatches = (x) => {
@@ -1475,7 +1616,7 @@ var clanName = (x) => {
1475
1616
  const n = nameOf(x);
1476
1617
  return n.length === 3 && ALL_SURNAMES.includes(n[2]) && REGION_WORDS.has(n.slice(0, 2)) && nextIs(x, /^\s?씨/);
1477
1618
  };
1478
- var NOT_TITLE_TAIL = /(?:세면장|운동장|주차장|수영장|경기장|공연장|시험장|작업장|사업장|회의장|행사장|전시장|선착장|정류장|야영장)$/;
1619
+ var NOT_TITLE_TAIL = /(?:세면장|운동장|주차장|수영장|경기장|공연장|시험장|작업장|사업장|회의장|행사장|전시장|선착장|정류장|야영장|초청장)$/;
1479
1620
  var sepHasColon = (x) => /[::]/.test(g(x, "sep"));
1480
1621
  var colonAfter = (x) => {
1481
1622
  const n = span(x);
@@ -1506,27 +1647,29 @@ var NAME_VARIANTS = [
1506
1647
  ok: (x) => nameOk(x, g(x, "latin") || g(x, "cjk") ? 2 : 1, closedAfter(x))
1507
1648
  },
1508
1649
  {
1509
- // 직함 뒤 — "과장 홍길동", "회장 : 도서연"(쌍점·발언자 쌍점이면 강한 문맥). 직함이 다른 낱말 꼬리인 자리
1510
- // ("세면장 조도임"의 면장, "주차장"의 차장)는 직함이 아니다
1511
- re: new RegExp(`(?<t>${TITLE_RE})${SEP}(?<n>${hangulName(ALL_SURNAMES, tailRole)})`, "dg"),
1650
+ // 직함 뒤 — "과장 홍길동", "회장 : 도서연"(쌍점·발언자 쌍점이면 강한 문맥), 두 글자는 여격 조사가 붙을 때만
1651
+ // ("주무관 한솔에게"). 직함이 다른 낱말 꼬리인 자리("세면장 조도임"의 면장, "주차장"의 차장)는 직함이 아니다.
1652
+ // 빗금은 "…장" 직함 뒤만("학생회장/서지안"·"사회분과위원장/배수아") — 직종/분야("연구원/고고학")와 같은 모양이다(코퍼스 채용공고)
1653
+ re: new RegExp(`(?<t>${TITLE_RE})(?<sep>\\s{0,2}[:\uFF1A]\\s{0,2}|\\s{1,3}|(?<=\uC7A5)\\s?/\\s?)(?<n>${hangulName(ALL_SURNAMES, tailRole)})`, "dg"),
1512
1654
  span: "n",
1513
1655
  mask: NAME_MASK,
1514
- ok: (x) => !NOT_TITLE_TAIL.test(x.m.input.slice(Math.max(0, x.m.index - 3), x.m.index + g(x, "t").length)) && nameOk(x, sepHasColon(x) || colonAfter(x) ? 1 : 0)
1656
+ ok: (x) => !NOT_TITLE_TAIL.test(x.m.input.slice(Math.max(0, x.m.index - 3), x.m.index + g(x, "t").length)) && nameOk(x, sepHasColon(x) || colonAfter(x) ? 1 : 0, dativeAfter(x))
1515
1657
  },
1516
1658
  {
1517
- // 호칭 앞 — "홍길동 씨", "홍길동님께", "Daniel Brown 씨". "기업날씨"·"솜씨"·"글씨" 같은 씨 낱말은 호칭이 아니다
1518
- re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => `(?:(?<![\uB0A0\uC19C\uB9D0\uAE00\uBD88\uB9D8])\\s?\uC528|\\s?\uB2D8)(?:${P_ALL})?(?![\uAC00-\uD7A3])(?!\\s?(?:\u321C|\\(\uC8FC\\)))`)}|${LATIN_NAME}(?=\\s?(?:\uC528|\uB2D8)(?![\uAC00-\uD7A3]))|${CJK_NAME}(?=\\s?(?:\uC528|\uB2D8)(?![\uAC00-\uD7A3])))`, "dg"),
1659
+ // 호칭 앞 — "홍길동 씨", "홍길동님께", "Daniel Brown 씨가". "기업날씨"·"솜씨"·"글씨" 같은 씨 낱말, 회사 이름
1660
+ // ("한빛씨 주식회사")은 호칭이 아니다
1661
+ re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => `(?:(?<![\uB0A0\uC19C\uB9D0\uAE00\uBD88\uB9D8])\\s?\uC528|\\s?\uB2D8)(?:${P_ALL})?(?![\uAC00-\uD7A3])(?!\\s?(?:\u321C|\\(\uC8FC\\)|\uC8FC\uC2DD\uD68C\uC0AC|\uC720\uD55C\uD68C\uC0AC))`)}|${LATIN_NAME}(?=\\s?(?:\uC528|\uB2D8)(?:${P_ALL})?(?![\uAC00-\uD7A3]))|${CJK_NAME}(?=\\s?(?:\uC528|\uB2D8)(?:${P_ALL})?(?![\uAC00-\uD7A3])))`, "dg"),
1519
1662
  span: "n",
1520
1663
  mask: NAME_MASK,
1521
1664
  ok: (x) => nameOk(x, 2) && !clanName(x)
1522
1665
  },
1523
1666
  {
1524
1667
  // 연락처 앞 — "유다온(010-…)", "홍길동 ☎ 02-…", "| 김지유 | (051-…) |". 두 글자는 괄호가 바로 붙을 때만 —
1525
- // 기관 안내 표의 "마음상담센터 한별 | 031-…" 같은 기관 이름 칸(코퍼스 실측)
1668
+ // 기관 안내 표의 "마음상담센터 한별 | 031-…" 같은 기관 이름 칸(코퍼스 실측). 옆 칸 연락처면 칸 머리만(phoneCellOk)
1526
1669
  re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => PHONE_NEXT)})`, "dg"),
1527
1670
  span: "n",
1528
1671
  mask: NAME_MASK,
1529
- ok: (x) => nameOk(x, 1, parenAfter(x))
1672
+ ok: (x) => nameOk(x, 1, parenAfter(x)) && phoneCellOk(x)
1530
1673
  },
1531
1674
  {
1532
1675
  // 나이·성별 괄호 앞 — "박도현(만 52세)", "서하(여, 31세)"
@@ -1536,18 +1679,29 @@ var NAME_VARIANTS = [
1536
1679
  ok: (x) => nameOk(x, 2)
1537
1680
  },
1538
1681
  {
1539
- // 직함 앞 — "홍길동 과장", "김민수 국장에게"(약한 문맥). 직함 뒤에 연락처가 이어지면 강한 문맥("편도윤 담당자(☎ …)")
1540
- re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => `\\s(?:\uC81C\\d{1,2}\\s?)?(?:${TITLE_RE})(?:\uB2D8)?(?:${P_ALL})?(?![\uAC00-\uD7A3])`)})(?:(?<after>\\s(?:${TITLE_RE})${PHONE_NEXT})|(?<weak>\\s(?:${TITLE_AFTER_RE})(?:\uB2D8)?(?:${P_ALL})?(?![\uAC00-\uD7A3])))`, "dg"),
1682
+ // 직함 앞 — "홍길동 과장", "김민수 국장에게"(약한 문맥), 칸 안 줄바꿈 "문가람<br>연구사". 직함 뒤에 연락처가 이어지면
1683
+ // 강한 문맥("편도윤 담당자(☎ …)")
1684
+ re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => `(?:\\s|<br>)(?:\uC81C\\d{1,2}\\s?)?(?:${TITLE_RE})(?:\uB2D8)?(?:${P_ALL})?(?![\uAC00-\uD7A3])`)})(?:(?<after>(?:\\s|<br>)(?:${TITLE_RE})${PHONE_NEXT})|(?<weak>(?:\\s|<br>)(?:${TITLE_AFTER_RE})(?:\uB2D8)?(?:${P_ALL})?(?![\uAC00-\uD7A3])))`, "dg"),
1685
+ span: "n",
1686
+ mask: NAME_MASK,
1687
+ ok: (x) => nameOk(x, g(x, "after") ? 1 : 0) && !universityAbbr(x, g(x, "after") + g(x, "weak"))
1688
+ },
1689
+ {
1690
+ // 이름 뒤 기관 붙은 직함 — "한도윤 농촌진흥청장은", "서지안 기후에너지환경부 장관은", "신도현 법무부장관 직무대행은"
1691
+ // (NAME_ORG_TITLE). 보도자료가 기관장·간부를 부르는 방식으로, 직함 앞 변형은 기관 없는 직함만 본다. 약한 문맥 — 이름 자리의
1692
+ // 코퍼스 모집단(세 글자 뒤 기관 직함 400여 곳)은 이름과 이미 걸러지는 낱말(위하여·조사된·공정위)뿐이었다. 직함 모양 명사
1693
+ // ("전시회 초청장")·대학 약칭("고려대 대학원 교수")은 아니다
1694
+ re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => "\\s")})(?<ot>\\s(?:${NAME_ORG_TITLE}))(?:\\s?\uC9C1\uBB34(?:\uB300\uD589|\uB300\uB9AC))?(?:\uB2D8)?(?:${P_ALL})?(?![\uAC00-\uD7A3])`, "dg"),
1541
1695
  span: "n",
1542
1696
  mask: NAME_MASK,
1543
- ok: (x) => nameOk(x, g(x, "after") ? 1 : 0)
1697
+ ok: (x) => nameOk(x, 0) && !NOT_TITLE_TAIL.test(g(x, "ot")) && !universityAbbr(x, g(x, "ot"))
1544
1698
  },
1545
1699
  {
1546
- // 당사자 번호 나열 — "피고 1. ○○은행(대표자 오세진) 2. 윤지호는"
1547
- re: new RegExp(`(?:${ROLE_RE})\\s{1,2}(?:1[.)]\\s?[^\\n]{1,60}?\\s)?\\d{1,2}[.)]\\s?(?<n>${hangulName(ALL_SURNAMES, tailRole)})`, "dg"),
1700
+ // 당사자 번호 나열 — "피고 1. ○○은행(대표자 오세진) 2. 윤지호는", "2. Paul Kim은"
1701
+ re: new RegExp(`(?:${ROLE_RE})\\s{1,2}(?:1[.)]\\s?[^\\n]{1,60}?\\s)?\\d{1,2}[.)]\\s?(?<n>${hangulName(ALL_SURNAMES, tailRole)}|${LATIN_NAME}${FOREIGN_FOLLOW}|${CJK_NAME}${FOREIGN_FOLLOW})`, "dg"),
1548
1702
  span: "n",
1549
1703
  mask: NAME_MASK,
1550
- ok: (x) => nameOk(x, 1)
1704
+ ok: (x) => nameOk(x, g(x, "latin") || g(x, "cjk") ? 2 : 1)
1551
1705
  },
1552
1706
  {
1553
1707
  // 표 칸 전체가 이름 — 열 머리글이 성명·담당자·대표자 …
@@ -1558,12 +1712,44 @@ var NAME_VARIANTS = [
1558
1712
  },
1559
1713
  {
1560
1714
  // 직함 칸 바로 옆 칸이 이름 하나 — 결재란 "<td>주무관</td><td>김서준</td><td>복지기획팀장</td><td>유재민</td>".
1561
- // 두 칸 모두 그 낱말뿐일 때만, 약한 문맥(흔한 성 + 세 글자)으로 — 옆 칸은 "귀하"·"직인"·"성명"·"사무관" 같은 서식 낱말이
1562
- // 흔하다. 머리글 행은 이름 칸이 아니다 — HTML 은 <th>, GFM 은 첫 행(열 머리글이 없는 자리): "| 치과의원 | 한의원 |"
1715
+ // 두 칸 모두 그 낱말뿐일 때만, 약한 문맥(흔한 성 + 세 글자, 두 글자도 흔한 성이면)으로 — 옆 칸은 "귀하"·"직인"·"성명"·
1716
+ // "사무관" 같은 서식 낱말이 흔하다(코퍼스 직함 옆 두 글자 칸 40여 종 중 비인명은 전부 직함·계급·서식 낱말이라 걸러진다).
1717
+ // 머리글 행은 이름 칸이 아니다 — HTML 은 <th>, GFM 은 첫 행(열 머리글이 없는 자리): "| 치과의원 | 한의원 |"
1563
1718
  re: new RegExp(`(?:^|\\||<t[dh][^>]*>)\\s*(?:${TITLE_RE}|${ORG_TITLE})\\s*(?:(?<pipe>\\|)|<\\/t[dh]>\\s*<td[^>]*>)\\s*(?<n>${hangulName(ALL_SURNAMES, () => "\\s*(?:\\||<\\/td>)")})`, "dg"),
1564
1719
  span: "n",
1565
1720
  mask: NAME_MASK,
1566
- ok: (x) => (!g(x, "pipe") || x.header() !== void 0) && nameOk(x, 0)
1721
+ ok: (x) => (!g(x, "pipe") || x.header() !== void 0) && nameOk(x, 0, true)
1722
+ },
1723
+ {
1724
+ // 결재란 대결 표지 — 칸 글이 "代" + 이름뿐 ("<td>代한도윤</td>": 결재권자 대신 서명한 사람, 코퍼스 실결재 문서)
1725
+ re: new RegExp(`(?:^|\\||<t[dh][^>]*>)\\s*\u4EE3\\s?(?<n>${hangulName(ALL_SURNAMES, () => "\\s*(?:\\||<\\/t[dh]>|$)")})`, "dg"),
1726
+ span: "n",
1727
+ mask: NAME_MASK,
1728
+ ok: (x) => nameOk(x, 0, true)
1729
+ },
1730
+ {
1731
+ // 회의록 화자 표지(직함 먼저) — "○위원장 한도윤 의석을 정돈하여 …", "◯부의장 서지안 지금부터 …". 발언이 곧 이어져
1732
+ // 직함 뒤 변형의 끝 조건(뒤에 한글 낱말이면 명사구)에 걸리던 자리다 — 코퍼스 회의록 3건의 화자 표지 845곳 중 9곳만
1733
+ // 잡혔다. 약한 문맥(흔한 성 세 글자): 붙여 쓴 동그라미 항목("○위원장 인사말")은 드문 성·두 글자로 갈린다
1734
+ re: new RegExp(`${SPEAKER_MARK}(?:${SPEAKER_TITLE})\\s(?<n>${hangulName(ALL_SURNAMES, () => "(?![\uAC00-\uD7A3])")})`, "dg"),
1735
+ span: "n",
1736
+ mask: NAME_MASK,
1737
+ ok: (x) => nameOk(x, 0, false, true) && !NOT_TITLE_TAIL.test(nameOf(x))
1738
+ },
1739
+ {
1740
+ // 회의록 화자 표지(이름 먼저) — "○오하람 위원 그런데 …", "◯문가람 의원 여기에 …"
1741
+ re: new RegExp(`${SPEAKER_MARK}(?<n>${hangulName(ALL_SURNAMES, () => `\\s(?:${SPEAKER_TITLE_AFTER})(?![\uAC00-\uD7A3])`)})`, "dg"),
1742
+ span: "n",
1743
+ mask: NAME_MASK,
1744
+ ok: (x) => nameOk(x, 0)
1745
+ },
1746
+ {
1747
+ // 명시적인 속기록·회의록 라벨 바로 뒤의 발언자 + 의회 직함. 일반 본문의 "상임 위원"과 구분되는
1748
+ // 문맥에서만 두 글자 이름·드문 성을 허용한다. 라벨만 있는 "회의록: 신규 위원 ..."은 명사 필터를 유지한다.
1749
+ re: new RegExp(`(?<![\uAC00-\uD7A3])(?:\uC18D\uAE30\uB85D|\uD68C\uC758\uB85D)[ \\t]{0,2}[:\uFF1A][ \\t]{0,2}(?<n>${hangulName(ALL_SURNAMES, () => `[ \\t](?:${SPEAKER_TITLE_AFTER})(?:${P_ALL})?(?![\uAC00-\uD7A3])`)})`, "dg"),
1750
+ span: "n",
1751
+ mask: NAME_MASK,
1752
+ ok: (x) => nameOk(x, 2)
1567
1753
  },
1568
1754
  {
1569
1755
  // 나열 — 앞에서 찾은 이름에 쉼표·와/과로 이어진 이름 ("문가윤, 방시우, 엄다인 등")
@@ -2219,4 +2405,4 @@ export {
2219
2405
  mdMarkerRanges,
2220
2406
  redactMarkdown
2221
2407
  };
2222
- //# sourceMappingURL=chunk-DPWWXUOG.js.map
2408
+ //# sourceMappingURL=chunk-3WSNLH4B.js.map