kordoc 4.14.2 → 4.14.4

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 (151) hide show
  1. package/README.md +51 -6
  2. package/dist/{-AQKXLFIM.js → -Z4TJ3QGK.js} +13 -11
  3. package/dist/{chunk-QOMNEWCQ.cjs → chunk-5LAZQCUJ.cjs} +305 -31
  4. package/dist/chunk-5LAZQCUJ.cjs.map +1 -0
  5. package/dist/{chunk-7IAQ4HOK.js → chunk-6BWU6SQX.js} +1 -1
  6. package/dist/chunk-6BWU6SQX.js.map +1 -0
  7. package/dist/{chunk-FOJZNMMK.js → chunk-6CUIXJOC.js} +1 -1
  8. package/dist/chunk-6CUIXJOC.js.map +1 -0
  9. package/dist/{chunk-ZWD734EY.js → chunk-7SE4EZ45.js} +2 -2
  10. package/dist/{chunk-TSFS5VKD.js → chunk-D4U2TXDC.js} +332 -141
  11. package/dist/chunk-D4U2TXDC.js.map +1 -0
  12. package/dist/chunk-DPWWXUOG.js +2222 -0
  13. package/dist/chunk-DPWWXUOG.js.map +1 -0
  14. package/dist/{chunk-GDW4YP7K.js → chunk-GLSBEEZJ.js} +2 -2
  15. package/dist/{chunk-JQEFP45L.js → chunk-GUJ4Q73I.js} +2 -2
  16. package/dist/{chunk-ZWOFQRIB.js → chunk-HIJUKZHM.js} +3 -3
  17. package/dist/chunk-IMAAFSAX.cjs +1352 -0
  18. package/dist/chunk-IMAAFSAX.cjs.map +1 -0
  19. package/dist/{chunk-TNCKOL2O.js → chunk-LCW7SEPG.js} +2 -2
  20. package/dist/chunk-LFSQAMSM.js +1352 -0
  21. package/dist/chunk-LFSQAMSM.js.map +1 -0
  22. package/dist/chunk-MG6DJEX4.js +1299 -0
  23. package/dist/chunk-MG6DJEX4.js.map +1 -0
  24. package/dist/{chunk-QC5T6D4J.js → chunk-NLU6LBLY.js} +299 -25
  25. package/dist/chunk-NLU6LBLY.js.map +1 -0
  26. package/dist/{chunk-EQZI77PP.cjs → chunk-QEJUY6SK.cjs} +3 -3
  27. package/dist/{chunk-EQZI77PP.cjs.map → chunk-QEJUY6SK.cjs.map} +1 -1
  28. package/dist/{chunk-VQ5W4MYO.js → chunk-QIF2VXBQ.js} +298 -24
  29. package/dist/chunk-QIF2VXBQ.js.map +1 -0
  30. package/dist/{chunk-UET462HB.js → chunk-TITDJWVZ.js} +2 -2
  31. package/dist/{chunk-247ANM72.cjs → chunk-USHQCF5T.cjs} +870 -147
  32. package/dist/chunk-USHQCF5T.cjs.map +1 -0
  33. package/dist/{chunk-C7FQBSON.js → chunk-V3DX4DUF.js} +2 -2
  34. package/dist/{chunk-DOGPOY4B.js → chunk-VDN7LMPL.js} +2 -2
  35. package/dist/{chunk-EDNJLUSE.js → chunk-VJIL6PIL.js} +2 -2
  36. package/dist/{chunk-L7DMSMAG.cjs → chunk-VWUYVEFN.cjs} +1 -1
  37. package/dist/chunk-VWUYVEFN.cjs.map +1 -0
  38. package/dist/{chunk-HR2XNERB.cjs → chunk-WJHAU73B.cjs} +2 -2
  39. package/dist/{chunk-HR2XNERB.cjs.map → chunk-WJHAU73B.cjs.map} +1 -1
  40. package/dist/{chunk-NS55SM7A.js → chunk-WJJGW5PL.js} +2 -2
  41. package/dist/{chunk-EISO3POT.js → chunk-XVXHXQIO.js} +788 -194
  42. package/dist/chunk-XVXHXQIO.js.map +1 -0
  43. package/dist/{chunk-ILU76UF3.js → chunk-YAKC46HZ.js} +2 -2
  44. package/dist/chunk-YAKC46HZ.js.map +1 -0
  45. package/dist/{chunk-3DUADAY2.js → chunk-YAVHVFRY.js} +851 -128
  46. package/dist/chunk-YAVHVFRY.js.map +1 -0
  47. package/dist/{chunk-A7QC2ZVP.js → chunk-YT3GC62E.js} +851 -128
  48. package/dist/chunk-YT3GC62E.js.map +1 -0
  49. package/dist/chunks-CCMRL4MJ.js +10 -0
  50. package/dist/cli.js +228 -97
  51. package/dist/cli.js.map +1 -1
  52. package/dist/{formula-PJJ6S3Z2.cjs → formula-5LOVHAKE.cjs} +9 -9
  53. package/dist/{formula-PJJ6S3Z2.cjs.map → formula-5LOVHAKE.cjs.map} +1 -1
  54. package/dist/{formula-JGFTXWYT.js → formula-7RANCW5L.js} +3 -3
  55. package/dist/{formula-5S7WWXGH.js → formula-7UYJOVTB.js} +4 -4
  56. package/dist/{image-ocr-WCAYXLOA.js → image-ocr-I6JOCATS.js} +34 -16
  57. package/dist/image-ocr-I6JOCATS.js.map +1 -0
  58. package/dist/{image-ocr-ZTYAGV3W.cjs → image-ocr-NYBJEOH2.cjs} +36 -18
  59. package/dist/image-ocr-NYBJEOH2.cjs.map +1 -0
  60. package/dist/{image-ocr-FLIL7MGN.js → image-ocr-ZV6PDBUQ.js} +32 -14
  61. package/dist/image-ocr-ZV6PDBUQ.js.map +1 -0
  62. package/dist/index.cjs +4048 -1216
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +66 -28
  65. package/dist/index.d.ts +66 -28
  66. package/dist/index.js +3487 -655
  67. package/dist/index.js.map +1 -1
  68. package/dist/mcp.js +74 -53
  69. package/dist/mcp.js.map +1 -1
  70. package/dist/{model-bundle-FSPZZ2EM.js → model-bundle-2C5IYDLE.js} +5 -5
  71. package/dist/{models-CJPAQK5R.js → models-SEQZHZFT.js} +5 -5
  72. package/dist/{parser-SCUTZAIJ.js → parser-3FNXEP6T.js} +708 -250
  73. package/dist/parser-3FNXEP6T.js.map +1 -0
  74. package/dist/{parser-A4TMPJAM.js → parser-FJBJXRJK.js} +708 -250
  75. package/dist/parser-FJBJXRJK.js.map +1 -0
  76. package/dist/{parser-VCXE4I5A.cjs → parser-HVEKF73B.cjs} +733 -275
  77. package/dist/parser-HVEKF73B.cjs.map +1 -0
  78. package/dist/pdf-ocr-JRDBJ6TA.cjs +14 -0
  79. package/dist/{pdf-ocr-MBZZ57LD.cjs.map → pdf-ocr-JRDBJ6TA.cjs.map} +1 -1
  80. package/dist/pdf-ocr-UUBWBEHL.js +16 -0
  81. package/dist/pdf-ocr-VJSF2SQC.js +13 -0
  82. package/dist/{profile-io-LBM263K7.js → profile-io-RUFZOZO6.js} +3 -3
  83. package/dist/{rasterize-46RG4ZF6.js → rasterize-IOYXR5SI.js} +3 -3
  84. package/dist/redact-NMIKVHMP.js +18 -0
  85. package/dist/redact-doc-QBHNJ2BU.js +1284 -0
  86. package/dist/redact-doc-QBHNJ2BU.js.map +1 -0
  87. package/dist/{render-NGP5T24S.js → render-YZ2TEC3N.js} +7 -7
  88. package/dist/seal-ECV44TUH.js +10 -0
  89. package/dist/{setup-AU4OCHNH.js → setup-MPIEP6XT.js} +3 -3
  90. package/dist/visual-ICMOWUCR.js +19 -0
  91. package/dist/{watch-MKPUCPXM.js → watch-RYO7MN5B.js} +12 -12
  92. package/package.json +2 -2
  93. package/dist/chunk-247ANM72.cjs.map +0 -1
  94. package/dist/chunk-3DUADAY2.js.map +0 -1
  95. package/dist/chunk-422427XJ.cjs +0 -571
  96. package/dist/chunk-422427XJ.cjs.map +0 -1
  97. package/dist/chunk-7IAQ4HOK.js.map +0 -1
  98. package/dist/chunk-A7QC2ZVP.js.map +0 -1
  99. package/dist/chunk-DZIXKL7E.js +0 -145
  100. package/dist/chunk-DZIXKL7E.js.map +0 -1
  101. package/dist/chunk-EISO3POT.js.map +0 -1
  102. package/dist/chunk-EP7KOI2V.js +0 -571
  103. package/dist/chunk-EP7KOI2V.js.map +0 -1
  104. package/dist/chunk-FOJZNMMK.js.map +0 -1
  105. package/dist/chunk-ILU76UF3.js.map +0 -1
  106. package/dist/chunk-KVWV4ULP.js +0 -516
  107. package/dist/chunk-KVWV4ULP.js.map +0 -1
  108. package/dist/chunk-L7DMSMAG.cjs.map +0 -1
  109. package/dist/chunk-QC5T6D4J.js.map +0 -1
  110. package/dist/chunk-QOMNEWCQ.cjs.map +0 -1
  111. package/dist/chunk-TSFS5VKD.js.map +0 -1
  112. package/dist/chunk-VQ5W4MYO.js.map +0 -1
  113. package/dist/chunks-SX77P5CV.js +0 -10
  114. package/dist/image-ocr-FLIL7MGN.js.map +0 -1
  115. package/dist/image-ocr-WCAYXLOA.js.map +0 -1
  116. package/dist/image-ocr-ZTYAGV3W.cjs.map +0 -1
  117. package/dist/parser-A4TMPJAM.js.map +0 -1
  118. package/dist/parser-SCUTZAIJ.js.map +0 -1
  119. package/dist/parser-VCXE4I5A.cjs.map +0 -1
  120. package/dist/pdf-ocr-76SAUXMZ.js +0 -13
  121. package/dist/pdf-ocr-CPU55CVF.js +0 -16
  122. package/dist/pdf-ocr-MBZZ57LD.cjs +0 -14
  123. package/dist/redact-7ILEAUTS.js +0 -12
  124. package/dist/seal-RGUPOLXD.js +0 -10
  125. package/dist/visual-NZLYIYEM.js +0 -19
  126. /package/dist/{-AQKXLFIM.js.map → -Z4TJ3QGK.js.map} +0 -0
  127. /package/dist/{chunk-ZWD734EY.js.map → chunk-7SE4EZ45.js.map} +0 -0
  128. /package/dist/{chunk-GDW4YP7K.js.map → chunk-GLSBEEZJ.js.map} +0 -0
  129. /package/dist/{chunk-JQEFP45L.js.map → chunk-GUJ4Q73I.js.map} +0 -0
  130. /package/dist/{chunk-ZWOFQRIB.js.map → chunk-HIJUKZHM.js.map} +0 -0
  131. /package/dist/{chunk-TNCKOL2O.js.map → chunk-LCW7SEPG.js.map} +0 -0
  132. /package/dist/{chunk-UET462HB.js.map → chunk-TITDJWVZ.js.map} +0 -0
  133. /package/dist/{chunk-C7FQBSON.js.map → chunk-V3DX4DUF.js.map} +0 -0
  134. /package/dist/{chunk-DOGPOY4B.js.map → chunk-VDN7LMPL.js.map} +0 -0
  135. /package/dist/{chunk-EDNJLUSE.js.map → chunk-VJIL6PIL.js.map} +0 -0
  136. /package/dist/{chunk-NS55SM7A.js.map → chunk-WJJGW5PL.js.map} +0 -0
  137. /package/dist/{chunks-SX77P5CV.js.map → chunks-CCMRL4MJ.js.map} +0 -0
  138. /package/dist/{formula-JGFTXWYT.js.map → formula-7RANCW5L.js.map} +0 -0
  139. /package/dist/{formula-5S7WWXGH.js.map → formula-7UYJOVTB.js.map} +0 -0
  140. /package/dist/{model-bundle-FSPZZ2EM.js.map → model-bundle-2C5IYDLE.js.map} +0 -0
  141. /package/dist/{models-CJPAQK5R.js.map → models-SEQZHZFT.js.map} +0 -0
  142. /package/dist/{pdf-ocr-76SAUXMZ.js.map → pdf-ocr-UUBWBEHL.js.map} +0 -0
  143. /package/dist/{pdf-ocr-CPU55CVF.js.map → pdf-ocr-VJSF2SQC.js.map} +0 -0
  144. /package/dist/{profile-io-LBM263K7.js.map → profile-io-RUFZOZO6.js.map} +0 -0
  145. /package/dist/{rasterize-46RG4ZF6.js.map → rasterize-IOYXR5SI.js.map} +0 -0
  146. /package/dist/{redact-7ILEAUTS.js.map → redact-NMIKVHMP.js.map} +0 -0
  147. /package/dist/{render-NGP5T24S.js.map → render-YZ2TEC3N.js.map} +0 -0
  148. /package/dist/{seal-RGUPOLXD.js.map → seal-ECV44TUH.js.map} +0 -0
  149. /package/dist/{setup-AU4OCHNH.js.map → setup-MPIEP6XT.js.map} +0 -0
  150. /package/dist/{visual-NZLYIYEM.js.map → visual-ICMOWUCR.js.map} +0 -0
  151. /package/dist/{watch-MKPUCPXM.js.map → watch-RYO7MN5B.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/redact-name-address.ts","../src/redact-rules.ts","../src/redact.ts"],"sourcesContent":["/**\n * redact 인명·주소 룰 (opt-in) — 사전(성씨·행정구역)과 문맥 게이트. 엔진·번호형 룰은 redact.ts·redact-rules.ts.\n *\n * 번호형과 달리 모양만으로는 판정이 안 되는 PII 라 정밀도가 먼저다 — 문맥이 없으면 잡지 않는다.\n * - 주소: 행정구역 사전(시·도·시·군·구)으로 시작해 도로명+건물번호, 동·리+번지, 또는 동 + 건물 이름 + N동 N호로\n * 끝나야 한다. \"창녕군 길곡면 전역\"·\"서울 강남구 일대\"처럼 행정구역만 있는 글은 주소가 아니다(스키프트 N02).\n * 스팬은 주소 전체, 가리는 곳은 읍·면·동·도로명부터 끝까지 — 시·도·시·군·구는 남긴다(주민번호가 생년월일을\n * 남기듯 식별력 낮은 앞부분은 두는 서식 보존 원칙, 비식별 조치의 \"시·군·구까지\" 범주화와 같은 선).\n * - 인명: 성씨 사전 + 문맥 — 라벨(\"성명:\"·표 머리글 \"성명\"), 당사자 역할어(피고인·원고·신청인 …), 호칭(씨·님),\n * 직함(앞·뒤), 연락처 괄호, 나이·성별 괄호, 나열(\"A, B, C 등\"). 성은 남기고 이름 글자만 가린다(\"홍●●\").\n * 문맥 없는 맨 이름(\"이수아는 거래처 대표에게 …\")은 잡지 않는다. 드문 성(기·인·제·국 …)은 낱말 첫 글자와 겹쳐\n * 강한 문맥(라벨·당사자·호칭·연락처·나이 괄호)에서만, 약한 문맥(직함)은 흔한 성 + 세 글자 이상만.\n * 근거·오탐 실측은 bench/redact-bench.mjs (합성 cases.md·스키프트 외부 정답·--corpus).\n */\n\nimport type { Match, Variant } from \"./redact-rules.js\"\n\nconst alt = (words: readonly string[]): string => [...words].sort((a, b) => b.length - a.length).join(\"|\")\n/** 균등배분으로 띄어 쓴 라벨(\"성 명\") — 글자 사이 공백 허용 */\nconst spaced = (words: readonly string[]): string => alt(words.map((w) => w.split(\"\").join(\"\\\\s?\")))\n\n// ─── 주소: 행정구역 사전 (2026-08 행정구역 + 옛 이름 — 옛 문서에 남아 있다) ─────────\n\nconst SIDO = [\n \"서울특별시\", \"부산광역시\", \"대구광역시\", \"인천광역시\", \"광주광역시\", \"대전광역시\", \"울산광역시\", \"세종특별자치시\",\n \"경기도\", \"강원특별자치도\", \"강원도\", \"충청북도\", \"충청남도\", \"전북특별자치도\", \"전라북도\", \"전라남도\", \"경상북도\",\n \"경상남도\", \"제주특별자치도\", \"제주도\", \"전남광주통합특별시\",\n \"서울시\", \"부산시\", \"대구시\", \"인천시\", \"광주시\", \"대전시\", \"울산시\", \"세종시\",\n \"서울\", \"부산\", \"대구\", \"인천\", \"광주\", \"대전\", \"울산\", \"세종\", \"경기\", \"강원\", \"충북\", \"충남\", \"전북\", \"전남\", \"경북\", \"경남\", \"제주\",\n]\n\n/** 시·군·구 + 일반구 (평면 목록 — 상하 관계는 보지 않는다. 정밀도는 뒤의 도로명+건물번호·동리+번지가 낸다) */\nconst SIGUNGU = [\n // 서울\n \"종로구\", \"중구\", \"용산구\", \"성동구\", \"광진구\", \"동대문구\", \"중랑구\", \"성북구\", \"강북구\", \"도봉구\", \"노원구\", \"은평구\",\n \"서대문구\", \"마포구\", \"양천구\", \"강서구\", \"구로구\", \"금천구\", \"영등포구\", \"동작구\", \"관악구\", \"서초구\", \"강남구\", \"송파구\", \"강동구\",\n // 부산·대구·인천·광주·대전·울산\n \"서구\", \"동구\", \"남구\", \"북구\", \"영도구\", \"부산진구\", \"동래구\", \"해운대구\", \"사하구\", \"금정구\", \"연제구\", \"수영구\", \"사상구\", \"기장군\",\n \"수성구\", \"달서구\", \"달성군\", \"군위군\",\n \"제물포구\", \"영종구\", \"미추홀구\", \"연수구\", \"남동구\", \"부평구\", \"계양구\", \"서해구\", \"검단구\", \"강화군\", \"옹진군\",\n \"광산구\", \"유성구\", \"대덕구\", \"울주군\",\n // 경기\n \"수원시\", \"성남시\", \"의정부시\", \"안양시\", \"광명시\", \"평택시\", \"동두천시\", \"안산시\", \"고양시\", \"과천시\", \"구리시\", \"남양주시\",\n \"오산시\", \"시흥시\", \"군포시\", \"의왕시\", \"하남시\", \"용인시\", \"파주시\", \"이천시\", \"안성시\", \"김포시\", \"화성시\", \"광주시\", \"양주시\",\n \"포천시\", \"여주시\", \"부천시\", \"연천군\", \"가평군\", \"양평군\", \"여주군\",\n \"장안구\", \"권선구\", \"팔달구\", \"영통구\", \"수정구\", \"중원구\", \"분당구\", \"만안구\", \"동안구\", \"상록구\", \"단원구\", \"덕양구\",\n \"일산동구\", \"일산서구\", \"처인구\", \"기흥구\", \"수지구\", \"효행구\", \"만세구\", \"동탄구\", \"병점구\", \"원미구\", \"소사구\", \"오정구\",\n // 강원\n \"춘천시\", \"원주시\", \"강릉시\", \"동해시\", \"태백시\", \"속초시\", \"삼척시\", \"홍천군\", \"횡성군\", \"영월군\", \"평창군\", \"정선군\",\n \"철원군\", \"화천군\", \"양구군\", \"인제군\", \"고성군\", \"양양군\",\n // 충북·충남\n \"충주시\", \"제천시\", \"청주시\", \"보은군\", \"옥천군\", \"영동군\", \"진천군\", \"괴산군\", \"음성군\", \"단양군\", \"증평군\", \"청원군\",\n \"상당구\", \"서원구\", \"흥덕구\", \"청원구\",\n \"천안시\", \"공주시\", \"보령시\", \"아산시\", \"서산시\", \"논산시\", \"계룡시\", \"당진시\", \"금산군\", \"부여군\", \"서천군\", \"청양군\",\n \"홍성군\", \"예산군\", \"태안군\", \"당진군\", \"연기군\", \"동남구\", \"서북구\",\n // 전북·전남\n \"전주시\", \"군산시\", \"익산시\", \"정읍시\", \"남원시\", \"김제시\", \"완주군\", \"진안군\", \"무주군\", \"장수군\", \"임실군\", \"순창군\",\n \"고창군\", \"부안군\", \"완산구\", \"덕진구\",\n \"목포시\", \"여수시\", \"순천시\", \"나주시\", \"광양시\", \"담양군\", \"곡성군\", \"구례군\", \"고흥군\", \"보성군\", \"화순군\", \"장흥군\",\n \"강진군\", \"해남군\", \"영암군\", \"무안군\", \"함평군\", \"영광군\", \"장성군\", \"완도군\", \"진도군\", \"신안군\",\n // 경북·경남\n \"포항시\", \"경주시\", \"김천시\", \"안동시\", \"구미시\", \"영주시\", \"영천시\", \"상주시\", \"문경시\", \"경산시\", \"의성군\", \"청송군\",\n \"영양군\", \"영덕군\", \"청도군\", \"고령군\", \"성주군\", \"칠곡군\", \"예천군\", \"봉화군\", \"울진군\", \"울릉군\",\n \"창원시\", \"진주시\", \"통영시\", \"사천시\", \"김해시\", \"밀양시\", \"거제시\", \"양산시\", \"의령군\", \"함안군\", \"창녕군\", \"남해군\",\n \"하동군\", \"산청군\", \"함양군\", \"거창군\", \"합천군\", \"의창구\", \"성산구\", \"마산합포구\", \"마산회원구\", \"진해구\", \"마산시\", \"진해시\",\n // 제주·세종\n \"제주시\", \"서귀포시\", \"세종시\",\n]\n\nconst REGION = `(?:(?:${alt(SIDO)})(?:\\\\s{0,2}(?:${alt(SIGUNGU)})){0,2}|(?:${alt(SIGUNGU)})(?:\\\\s{0,2}(?:${alt(SIGUNGU)}))?)`\n/** 읍·면 */\nconst EUPMYEON = \"[가-힣]{1,4}(?:읍|면)\"\n/** 법정동·리·N가 (행정동 \"신림1동\" 꼴 숫자 포함) — \"가\" 동은 늘 숫자 뒤(\"종로1가\") */\nconst DONGRI = \"[가-힣]{1,6}(?:\\\\d{0,2}(?:동|리)|\\\\d{1,2}가)\"\n/** 도로명 — \"세종대로\"·\"연세로2다길\"·\"중앙로12번길\", 띄어 쓴 번길(\"우포2로 914길\") 포함. \"기준으로\" 같은 조사 끝은 제외 */\nconst ROAD = \"[가-힣0-9]{1,15}(?<!으)(?:로|길)(?:\\\\s\\\\d{1,4}(?:번|[가-힣])?길)?\"\n/** 건물번호·번지 뒤 경계 — 숫자·소수·천 단위가 이어지거나, 단위·세는 말(명·개·년·%·㎡ …)이 붙으면 주소가 아니다 */\nconst ADDR_END = \"(?![0-9])(?!-\\\\d)(?![.,]\\\\d)(?![%%㎡㎞㎝㎜㏊℃°])(?=$|[^가-힣A-Za-z]|(?:에서|에게|에|의|은|는|이|가|을|를|와|과|로|으로|까지|부터|번지|입니다|이다|이며|이고|외|일대|일원|부근|인근|소재|주변|내|앞|옆|쪽))\"\n/** 상세주소 — \", 202-3호\"·\", 101동 1001호\"·\" 3층\"·\", 하늘아파트 204동 1103호\" (낱말 둘까지 + 끝이 N호·N층) */\nconst DETAIL = \"(?:(?:\\\\s?,\\\\s?|\\\\s)(?:[가-힣A-Za-z0-9]{1,20}\\\\s){0,2}(?:제|지하|B)?\\\\s?\\\\d{1,5}(?:-\\\\d{1,4})?(?:호|층)\" + ADDR_END + \")?\"\nconst BLDG = \"(?:지하\\\\s?)?\\\\d{1,5}(?:-\\\\d{1,5})?\"\nconst LOT = \"(?:산\\\\s?)?\\\\d{1,5}(?:-\\\\d{1,5})?(?:\\\\s?번지)?\"\n/** 주소 라벨 — 행정구역 없이 읍·면·동·도로명부터 적은 값도 이 라벨 뒤면 주소 */\nconst ADDR_LABEL = spaced([\"주소\", \"주소지\", \"현주소\", \"거주지\", \"소재지\", \"본점\", \"사업장\", \"영업소\"])\n\n/** 자리표시자 번호 — \"자양로 000\"(전화 010-0000-0000 과 같은 취급). 부번 0(\"시흥대로 517-0\")은 번호다 */\nconst PLACEHOLDER_NO = /(?<![\\d-])0+(?![\\d])/\nconst realNumber = (x: Match): boolean => !PLACEHOLDER_NO.test(x.m.groups?.rest ?? \"\")\n\n/** 주소 룰 — 스팬은 전체, 가리는 곳은 rest(읍·면·동·도로명 이하) */\nexport const ADDRESS_VARIANTS: readonly Variant[] = [\n { // 도로명 주소\n re: new RegExp(`(?<![가-힣])(?<region>${REGION})\\\\s{1,2}(?<rest>(?:${EUPMYEON}\\\\s{1,2})?${ROAD}\\\\s{0,2}${BLDG}${ADDR_END}${DETAIL})`, \"dg\"),\n mask: [\"rest\"], ok: realNumber,\n },\n { // 지번 주소 (동·리 + 번지)\n re: new RegExp(`(?<![가-힣])(?<region>${REGION})\\\\s{1,2}(?<rest>(?:${EUPMYEON}\\\\s{1,2})?${DONGRI}\\\\s{1,2}${LOT}${ADDR_END}${DETAIL})`, \"dg\"),\n mask: [\"rest\"], ok: realNumber,\n },\n { // 동 + 건물 이름 + N동 N호 (번지 없이 공동주택만 적은 꼴)\n re: new RegExp(`(?<![가-힣])(?<region>${REGION})\\\\s{1,2}(?<rest>(?:${EUPMYEON}\\\\s{1,2})?${DONGRI}\\\\s{1,2}(?:[가-힣A-Za-z0-9]{2,20}\\\\s)?\\\\d{1,4}동\\\\s?\\\\d{1,5}호${ADDR_END})`, \"dg\"),\n mask: [\"rest\"], ok: realNumber,\n },\n { // 라벨 뒤 — 행정구역 없이 도로명·동리부터 (\"주소: 자양로 117\", \"소재지 구월동 1138\")\n re: new RegExp(`(?:${ADDR_LABEL})\\\\s{0,2}[::]?\\\\s{0,2}(?<rest>(?:${EUPMYEON}\\\\s{1,2})?(?:${ROAD}\\\\s{0,2}${BLDG}|${DONGRI}\\\\s{1,2}${LOT})${ADDR_END}${DETAIL})`, \"dg\"),\n span: \"rest\",\n mask: [\"rest\"], ok: realNumber,\n },\n]\n\n// ─── 인명: 성씨·조사·역할어·직함 사전 ─────────────────────────\n\nconst DOUBLE_SURNAMES = [\"남궁\", \"황보\", \"제갈\", \"선우\", \"독고\", \"사공\", \"서문\", \"동방\"]\n/** 흔한 성 (인구 상위 — 약한 문맥에서도) */\nconst COMMON_SURNAMES = \"김이박최정강조윤장임한오서신권황안송전홍유고문양손배백허남심노하곽성차주우구민류나진지엄채원천방공현함변염여추도소석선설마길연위표명\"\n/**\n * 드문 성 — 낱말 첫 글자와 겹쳐(기획·인사·제출·국가·예산·경우·복지·목적·피해·감사·동의·평가 …) 강한 문맥에서만.\n * 사·부·가·대·상·시·수·아·영·운·후 같은 성은 넣지 않는다 — 문맥이 있어도 뒤따르는 명사(\"신청인 사이\"·\"원고 부담\")와\n * 구별이 안 된다\n */\nconst RARE_SURNAMES = \"기반라왕금옥육인맹제모탁국어은편용예경봉복태목형피두감음빈동온호범좌팽승간갈단견당화창옹평빙리\"\nconst ALL_SURNAMES = COMMON_SURNAMES + RARE_SURNAMES\n\n/** 이름 뒤 조사 — 언제나 떼는 것 (이름 끝 글자로 드문 것) */\nconst PARTICLE_ALWAYS = [\n \"에게서\", \"께서는\", \"께서도\", \"께서\", \"에게는\", \"에게도\", \"에게\", \"한테\", \"으로서\", \"으로써\", \"으로\", \"로서\", \"로써\",\n \"이라고\", \"이라는\", \"이라며\", \"이랑\", \"이며\", \"이고\", \"이다\", \"이었\", \"였다\", \"입니다\", \"라고\", \"라는\",\n \"에서\", \"부터\", \"까지\", \"처럼\", \"보다\", \"조차\", \"마저\", \"밖에\", \"에는\", \"에도\", \"와는\", \"과는\", \"와의\", \"과의\", \"의\",\n \"을\", \"를\", \"는\", \"와\", \"과\", \"께\", \"에\", \"로\", \"만\", \"가\", \"도\", \"랑\",\n]\n/** 이름 끝 글자로도 흔한 조사 (지은·세나·영이) — 떼고 남는 이름이 세 글자일 때만 뗀다 (\"서윤은\"은 세 글자 이름으로 — 가리는 쪽) */\nconst PARTICLE_COND = [\"은\", \"이\", \"나\"]\nconst P_ALL = alt([...PARTICLE_ALWAYS, ...PARTICLE_COND])\nconst P_ALWAYS = alt(PARTICLE_ALWAYS)\n/** 이름 뒤에 붙은 조사·호칭으로 낱말이 끝남 (\"홍길동은\", \"홍길동님께\") — 파일 안 이름 리터럴 경계 (redact-scrub) */\nconst NAME_AFTER_RE = new RegExp(`^(?:씨|님)?(?:${P_ALL})?(?![가-힣])`)\nexport const nameParticleAt = (s: string, i: number): boolean => NAME_AFTER_RE.test(s.slice(i, i + 6))\n/**\n * 이름 끝 글자로 오지 않는 글자 — 조사(\"문솔의\"는 \"문솔\"+\"의\")와 용언 꼴 조각(\"이상으[로]\"·\"명시된\"·\"나온다\"·\"진료받\"·\n * \"지적했\"·\"나오면\"·\"방문해\"·\"방문없[이]\")·복수(\"이분들\").\n * 이름 끝에도 흔한 만(성만)·랑(하랑)은 넣지 않는다\n */\nconst NOT_NAME_FINAL = \"의을를는와과에께로으된다받들했면해없있\"\n\n/**\n * 한글 이름 패턴 — 홑성 + 이름 1~2글자, 복성 + 이름 1~2글자. 홑성 네 글자 이름은 드물고 \"인적사항\"·\"제출서류\"\n * 같은 네 글자 명사와 갈리지 않아 받지 않는다(코퍼스 실측). tail(p) 는 이름 뒤 lookahead 내용 — p 는 뗄 수 있는\n * 조사 묶음(이름 두 글자 뒤는 늘 떼는 조사만). 그룹: sur·given·given1 (복성 dsur·dgiven)\n */\nfunction hangulName(surnames: string, tail: (p: string) => string): string {\n return `(?:(?<dsur>${alt(DOUBLE_SURNAMES)})(?<dgiven>[가-힣]{1,2}?)(?=${tail(P_ALL)})` +\n `|(?<sur>[${surnames}])(?:(?<given>[가-힣]{2})(?<![${NOT_NAME_FINAL}])(?=${tail(P_ALL)})` +\n `|(?<given1>(?![${NOT_NAME_FINAL}])[가-힣])(?=${tail(P_ALWAYS)})))`\n}\n\n/**\n * 성씨 한자 → 한글 독음 — 한자 병기(\"서지안(徐智安)\")가 이름인지 가린다. 한자어 병기(\"공인(公印)\"·\"원격지(遠隔地)\"·\n * \"신상(身上)\")는 첫 한자가 성씨 한자가 아니거나 독음이 다르다(코퍼스 실측)\n */\nconst SURNAME_HANJA: Record<string, string> = {\n 金: \"김\", 李: \"이리\", 朴: \"박\", 崔: \"최\", 鄭: \"정\", 丁: \"정\", 程: \"정\", 姜: \"강\", 康: \"강\", 江: \"강\", 趙: \"조\", 曺: \"조\",\n 曹: \"조\", 尹: \"윤\", 張: \"장\", 蔣: \"장\", 章: \"장\", 林: \"임\", 任: \"임\", 韓: \"한\", 吳: \"오\", 徐: \"서\", 申: \"신\", 辛: \"신\",\n 愼: \"신\", 權: \"권\", 黃: \"황\", 安: \"안\", 宋: \"송\", 全: \"전\", 田: \"전\", 錢: \"전\", 洪: \"홍\", 柳: \"유류\", 劉: \"유류\", 兪: \"유\",\n 庾: \"유\", 高: \"고\", 文: \"문\", 梁: \"양\", 楊: \"양\", 孫: \"손\", 裵: \"배\", 裴: \"배\", 白: \"백\", 許: \"허\", 南: \"남\", 沈: \"심\",\n 盧: \"노로\", 魯: \"노\", 河: \"하\", 郭: \"곽\", 成: \"성\", 車: \"차\", 朱: \"주\", 周: \"주\", 禹: \"우\", 具: \"구\", 丘: \"구\", 閔: \"민\",\n 羅: \"나라\", 陳: \"진\", 秦: \"진\", 晋: \"진\", 池: \"지\", 嚴: \"엄\", 蔡: \"채\", 元: \"원\", 千: \"천\", 方: \"방\", 房: \"방\", 孔: \"공\",\n 玄: \"현\", 咸: \"함\", 卞: \"변\", 邊: \"변\", 廉: \"염\", 呂: \"여\", 余: \"여\", 秋: \"추\", 都: \"도\", 蘇: \"소\", 石: \"석\", 宣: \"선\",\n 薛: \"설\", 馬: \"마\", 吉: \"길\", 延: \"연\", 魏: \"위\", 韋: \"위\", 表: \"표\", 明: \"명\", 奇: \"기\", 潘: \"반\", 王: \"왕\", 琴: \"금\",\n 玉: \"옥\", 陸: \"육\", 印: \"인\", 孟: \"맹\", 諸: \"제\", 牟: \"모\", 毛: \"모\", 卓: \"탁\", 鞠: \"국\", 國: \"국\", 魚: \"어\", 殷: \"은\",\n 片: \"편\", 龍: \"용\", 芮: \"예\", 慶: \"경\", 景: \"경\", 奉: \"봉\", 卜: \"복\", 太: \"태\", 睦: \"목\", 邢: \"형\", 皮: \"피\", 杜: \"두\",\n 甘: \"감\", 陰: \"음\", 賓: \"빈\", 董: \"동\", 溫: \"온\", 扈: \"호\", 范: \"범\", 左: \"좌\", 彭: \"팽\", 承: \"승\", 簡: \"간\", 葛: \"갈\",\n 段: \"단\", 甄: \"견\", 唐: \"당\", 昌: \"창\", 邕: \"옹\", 平: \"평\", 氷: \"빙\",\n}\nconst DOUBLE_SURNAME_HANJA: Record<string, string> = {\n 南宮: \"남궁\", 皇甫: \"황보\", 諸葛: \"제갈\", 鮮于: \"선우\", 獨孤: \"독고\", 司空: \"사공\", 西門: \"서문\", 東方: \"동방\",\n}\n\n/** 라틴 문자 이름(Daniel Brown·Anna Lee)·한자 이름(劉洋·山田花子) — 강한 문맥에서만. 첫 글자는 남긴다 */\nconst LATIN_NAME = \"(?<latin>(?<l1>[A-Z])(?<lrest>[a-z]{1,15}(?:[ -][A-Z][a-z]{1,15}){1,2}))\"\nconst CJK_NAME = \"(?<cjk>(?<c1>[\\\\u4E00-\\\\u9FFF])(?<crest>[\\\\u4E00-\\\\u9FFF]{1,3}))\"\nconst FOREIGN_FOLLOW = `(?![A-Za-z\\\\u4E00-\\\\u9FFF])(?=(?:${P_ALL})?(?![가-힣]))`\n\n/** 당사자 역할어 — 뒤에 오는 말이 사람 이름인 자리 */\nconst PARTY_ROLES = [\n \"피고인\", \"피의자\", \"피해자\", \"가해자\", \"원고\", \"피고\", \"참고인\", \"증인\", \"고소인\", \"피고소인\", \"고발인\", \"피고발인\",\n \"진정인\", \"피진정인\", \"신고인\", \"신고자\", \"제보자\", \"민원인\", \"신청인\", \"피신청인\", \"청구인\", \"피청구인\", \"청원인\",\n \"항고인\", \"항소인\", \"상고인\", \"채권자\", \"채무자\", \"임차인\", \"임대인\", \"매도인\", \"매수인\", \"상속인\", \"피상속인\",\n \"후견인\", \"피후견인\", \"법정대리인\", \"소송대리인\", \"대리인\", \"변호인\", \"변호사\", \"법무사\", \"감정인\", \"감정의\", \"담당의\",\n \"주치의\", \"환자\", \"보호자\", \"피보험자\", \"보험계약자\", \"보험수익자\", \"수익자\", \"사고자\", \"운전자\", \"목격자\", \"세대주\",\n \"예금주\", \"수령인\", \"수취인\", \"작성자\", \"대표자\", \"대표이사\", \"소유자\", \"보증인\", \"연대보증인\", \"입회인\", \"담당자\",\n]\n/**\n * 직함 — 이름 앞(\"과장 홍길동\")·뒤(\"홍길동 과장\"). 보통명사와 겹치는 직함(검사·의사·시장·지사·통장·고문·경사·경위·\n * 경감·경정)은 뺀다 — \"안전성 검사\"·\"노동시장 이중구조\"·\"횡경사 우력정\"(코퍼스 실측)\n */\nconst TITLES = [\n \"회장\", \"부회장\", \"사장\", \"부사장\", \"이사장\", \"이사\", \"감사\", \"의장\", \"부의장\", \"공동의장\", \"위원장\", \"부위원장\", \"위원\",\n \"대표\", \"대표이사\", \"장관\", \"차관\", \"청장\", \"처장\", \"실장\", \"국장\", \"과장\", \"팀장\", \"계장\", \"부장\", \"차장\", \"대리\", \"주임\",\n \"주무관\", \"사무관\", \"서기관\", \"연구관\", \"연구사\", \"연구원\", \"지도사\", \"담당\", \"담당자\", \"교수\", \"부교수\", \"조교수\", \"강사\",\n \"교사\", \"교장\", \"교감\", \"원장\", \"소장\", \"센터장\", \"관장\", \"총장\", \"학장\", \"의원\", \"군수\", \"구청장\", \"도지사\", \"면장\", \"읍장\",\n \"동장\", \"이장\", \"기자\", \"특파원\", \"박사\", \"선생\", \"선생님\", \"간호사\", \"약사\", \"판사\", \"수사관\", \"조사관\", \"비서관\", \"보좌관\",\n \"상무\", \"전무\", \"대변인\", \"사무국장\", \"사무총장\", \"본부장\", \"조합장\", \"지점장\", \"정책관\", \"심의관\", \"기획관\", \"담당관\",\n \"조정관\", \"협력관\",\n]\n/** 이름 자리에 오면 안 되는 직급·계급 낱말 (직함 목록 밖: \"담당자 서기관 김지유\"의 \"서기관\") */\nconst RANKS = [\n \"전문위원\", \"지도관\", \"전문관\", \"행정관\", \"조정관\", \"협력관\", \"소방위\", \"소방경\", \"소방장\", \"소방교\", \"소방사\", \"소방령\",\n \"소방정\", \"경정\", \"경감\", \"경위\", \"경사\", \"경장\", \"순경\", \"총경\", \"검사\", \"의사\", \"시장\", \"지사\", \"통장\", \"반장\", \"고문\",\n \"수석\", \"단장\", \"노무사\", \"세무사\", \"회계사\", \"변리사\", \"간사\", \"목사\", \"신부\", \"기사\", \"주사\", \"서기\", \"주무\", \"선임\",\n \"권사\", \"장로\", \"집사\", \"장학관\", \"장학사\", \"심사관\", \"감독관\", \"감사관\",\n]\n\n/**\n * 성씨로 시작하지만 이름이 아닌 낱말 — 문맥어 앞뒤 이름 자리에 오는 흔한 명사. 서식 라벨(성명·주소·연락처 — 역할어 뒤\n * 하위 칸), 직함을 꾸미는 말(국회 의원·명예 교수·정책 과장), 호칭 자리 명사(고객님·하느님), 역할어 뒤 명사구(피해자 구제·\n * 대표자 신분증)와, 조사를 떼면 세 글자가 되는 네 글자 명사의 앞(전체회+의)을 코퍼스 5,081건 실측으로 모았다\n */\nconst STOP_WORDS = new Set([\n // 서식 라벨·항목\n \"성명\", \"성함\", \"성별\", \"주소\", \"주소지\", \"소재지\", \"연락처\", \"전화\", \"전송\", \"휴대폰\", \"이메일\", \"이름\", \"주민\", \"생년\",\n \"서명\", \"날인\", \"소속\", \"직위\", \"직급\", \"직책\", \"명의\", \"명칭\", \"상호\", \"도장\", \"기타\", \"기관\", \"부서\", \"국적\", \"나이\", \"연령\",\n \"학년\", \"학교\", \"학과\", \"번호\", \"본인\", \"귀하\", \"여러분\", \"일동\", \"전원\", \"모두\", \"각각\", \"명단\", \"정보\", \"현황\", \"관계\",\n \"한글\", \"영문\", \"국문\", \"한자\", \"구분\", \"전공\", \"주거\", \"현물\", \"현금\", \"설정\", \"문자열\", \"리스트\", \"하나\", \"강제\", \"최초\",\n \"여러\", \"조합명\", \"은행명\", \"기재란\", \"신분증\", \"지정서\", \"제출인\", \"위임인\", \"정규직\", \"정년\", \"성년자\", \"유선\", \"예제\",\n \"지출원\", \"이사회\", \"연구직\", \"공무직\", \"임기제\", \"전문직\",\n // 역할어·호칭 자리 명사\n \"고객\", \"회원\", \"국민\", \"시민\", \"구민\", \"도민\", \"군민\", \"학생\", \"학부모\", \"부모\", \"선생\", \"교사\", \"공무원\", \"직원\", \"관리자\",\n \"이용자\", \"사용자\", \"신청자\", \"신고자\", \"소유자\", \"보호자\", \"전문가\", \"지원자\", \"제출자\", \"조합원\", \"임원\", \"장애인\", \"노인\",\n \"노약자\", \"고령자\", \"배우자\", \"유족\", \"유가족\", \"가족\", \"자녀\", \"아동\", \"여성\", \"남성\", \"청년\", \"주주\", \"하느\", \"하나님\",\n \"도련\", \"마음\", \"형수\", \"제수\", \"주인\", \"공주\", \"왕자\", \"도령\", \"장군\", \"조상\", \"방장\", \"선배\", \"기사\", \"간호사\", \"목사\",\n // 직함·역할어를 꾸미는 말 (정책 과장·국회 의원·명예 교수·지역 주민·교장 공모제)\n \"국회\", \"지방\", \"지역\", \"정부\", \"정책\", \"기획\", \"홍보\", \"인사\", \"민원\", \"총무\", \"문화\", \"도시\", \"주택\", \"교통\", \"안전\",\n \"소방\", \"경제\", \"국제\", \"조사\", \"조직\", \"지원\", \"공원\", \"공보\", \"예산\", \"계약\", \"진흥\", \"정보화\", \"전산\", \"통계\", \"법무\",\n \"감찰\", \"평가\", \"운영\", \"관리\", \"시설\", \"자치\", \"행정\", \"재무\", \"회계\", \"세무\", \"복지\", \"보건\", \"환경\", \"위생\", \"산림\",\n \"녹지\", \"건설\", \"건축\", \"도로\", \"하천\", \"농업\", \"축산\", \"해양\", \"수산\", \"관광\", \"체육\", \"교육\", \"기술\", \"연구\", \"개발\",\n \"기업\", \"산업\", \"투자\", \"노동\", \"고용\", \"인권\", \"기록\", \"정원\", \"장학\", \"전통\", \"주식\", \"금융\", \"국내\", \"국외\", \"해외\",\n \"공설\", \"신규\", \"정기\", \"임시\", \"특별\", \"일반\", \"전체\", \"공공\", \"민간\", \"공익\", \"공동\", \"명예\", \"전임\", \"상임\", \"공모\",\n \"공모제\", \"임용\", \"임용제\", \"주재\", \"전체회\", \"조사결\", \"안전성\", \"전문성\", \"편의성\", \"유사도\", \"위하여\", \"하는데\",\n // 역할어 뒤에 흔히 오는 명사\n \"사이\", \"본점\", \"지점\", \"회사\", \"법인\", \"단체\", \"전결\", \"선출\", \"선거\", \"선정\", \"승인\", \"지정\", \"변경\", \"교체\", \"임명\",\n \"위촉\", \"해촉\", \"해임\", \"사임\", \"사퇴\", \"취임\", \"퇴임\", \"결과\", \"보고\", \"의견\", \"진술\", \"동의\", \"확인\", \"제출\", \"신청\",\n \"신고\", \"청구\", \"접수\", \"처리\", \"조치\", \"검토\", \"통보\", \"안내\", \"연락\", \"문의\", \"문의처\", \"상담\", \"면담\", \"방문\", \"출석\",\n \"참석\", \"소환\", \"조서\", \"진단\", \"치료\", \"입원\", \"퇴원\", \"수술\", \"진료\", \"상태\", \"부담\", \"책임\", \"의무\", \"권리\", \"권한\",\n \"자격\", \"요건\", \"여부\", \"대상\", \"해당\", \"경우\", \"관련\", \"내용\", \"사항\", \"목록\", \"서류\", \"증명\", \"증빙\", \"원본\", \"사본\",\n \"주장\", \"청구서\", \"신청서\", \"신고서\", \"동의서\", \"확인서\", \"위임장\", \"고소장\", \"소장\", \"정도\", \"이상\", \"이하\", \"이내\",\n \"이후\", \"이전\", \"이외\", \"공고\", \"공문\", \"공개\", \"공유\", \"공지\", \"공급\", \"공사\", \"공단\", \"주관\", \"주최\", \"주요\", \"주의\",\n \"소관\", \"소통\", \"소개\", \"석사\", \"박사\", \"학사\", \"성과\", \"성적\", \"연수\", \"연장\", \"연간\", \"연도\", \"전년\", \"전월\", \"전일\",\n \"전입\", \"전출\", \"기간\", \"기준\", \"기한\", \"정산\", \"정리\", \"정지\", \"정비\", \"조정\", \"조회\", \"조례\", \"조항\", \"지급\", \"지출\",\n \"지침\", \"지시\", \"지도\", \"진행\", \"진정\", \"차량\", \"차원\", \"강화\", \"강조\", \"강의\", \"장소\", \"장기\", \"장비\", \"장부\", \"장애\",\n \"임대\", \"임금\", \"임기\", \"임무\", \"한도\", \"한계\", \"한국\", \"허가\", \"허위\", \"심사\", \"심의\", \"심리\", \"방법\", \"방안\", \"방침\",\n \"공무\", \"공간\", \"현장\", \"현재\", \"현행\", \"함께\", \"변동\", \"변동이\", \"여건\", \"추진\", \"추가\", \"추천\", \"도입\", \"도움\", \"도서\",\n \"도서관\", \"소득\", \"소유\", \"소재\", \"선택\", \"선발\", \"설명\", \"설치\", \"설계\", \"설립\", \"마련\", \"마을\", \"마감\", \"연계\", \"위반\",\n \"위험\", \"위치\", \"위탁\", \"위임\", \"표시\", \"표준\", \"표창\", \"표현\", \"명시\", \"반영\", \"반납\", \"금액\", \"금지\", \"인가\", \"인원\",\n \"인정\", \"인증\", \"인구\", \"인근\", \"인수\", \"인재\", \"제공\", \"제도\", \"제한\", \"제외\", \"제시\", \"제안\", \"모집\", \"국가\", \"은행\",\n \"편의\", \"용도\", \"용역\", \"예정\", \"예외\", \"예방\", \"경찰\", \"경찰서\", \"소방서\", \"복지관\", \"체육관\", \"문화원\", \"유치원\",\n \"연구소\", \"진료소\", \"우체국\", \"봉사\", \"피해\", \"감독\", \"동안\", \"동일\", \"범위\", \"범죄\", \"간담회\", \"간부\", \"단계\", \"단위\",\n \"단속\", \"당시\", \"당해\", \"당사자\", \"평일\", \"구제\", \"이송\", \"배치\", \"배석\", \"간의\", \"주식회사\", \"유한회사\",\n \"이용\", \"이익\", \"연구자\", \"방식\", \"유형\", \"지표\", \"분야\", \"현지\", \"전문\", \"이동\", \"연결\", \"문학\", \"이미지\", \"공무직\",\n \"원어민\", \"서명서\", \"길이\", \"채용\", \"여기서\", \"하반기\", \"상반기\", \"설명회\", \"심의회\", \"협의회\", \"공청회\",\n \"토론회\", \"박람회\", \"연찬회\", \"추모식\", \"방과후\", \"진입도\", \"기재\", \"기안\", \"지자체\", \"고문서\", \"선거권\", \"이식술\",\n \"노숙\", \"신선\", \"구의회\", \"도의회\", \"시의회\", \"위원회\", \"조절\", \"현직\", \"소지\", \"어머\", \"예수\", \"황제\", \"우주\",\n \"현수막\", \"박물관\", \"미술관\", \"기념관\", \"전시관\", \"회관\", \"성공회\", \"연구진\", \"남해안\", \"당연직\", \"연합\", \"송달\", \"최종\", \"도청\",\n])\n/** 이름 뒤 조사로 보이는 조각이 이름 자리를 다 차지한 것 (\"주에서\"의 \"에서\", \"지라도\"의 \"라도\") */\nconst PARTICLE_GIVEN = new Set([\"에서\", \"에게\", \"부터\", \"까지\", \"라도\", \"처럼\", \"보다\", \"께서\", \"한테\", \"으로\", \"이다\", \"에는\", \"에도\", \"이나\", \"이랑\"])\n/**\n * 두 글자 보통명사 + 조사·접미 한 글자 — \"원장+은\", \"정부+가\", \"신청+하[는]\", \"유형+별\", \"연령+대\", \"위임+인\", \"지정+한\".\n * 앞 두 글자가 사전 낱말이고 끝 글자가 이것이면 이름이 아니다 (이름 끝에 흔한 호·민·수·영 등은 없다 — \"이상호\"는 이름)\n */\nconst SUFFIX_FINAL = \"은는이가을를의와과도만에로나랑께하한될할별적화률율층계력법형제대인자시군구성권일본후서료숲속\"\n\n/** 조직·부서·지역 꼬리 (\"도로팀(055-…)\", \"민원실 02-…\", \"정책과장\", \"고흥군\") — 이름 끝 글자로는 거의 없다 */\nconst ORG_FINAL = /(?:과|팀|실|계|처|청|단|반|부|장|시|군|센터|협회|조합|공단|공사|재단|법인|병원|학교|대학|회사|은행|위원회|사무소|지청|본부)$/\n/** 행정구역 이름과 그 줄기(\"금천\"·\"고양\"·\"서울\") — 연락처·호칭 앞 지명 (\"에코플랫폼 서울(010-…)\", \"금천씨\") */\nconst REGION_WORDS = new Set([...SIDO, ...SIGUNGU, ...SIGUNGU.filter((w) => w.length >= 3).map((w) => w.slice(0, -1))])\n/** 용언·파생 꼬리 (\"위하여\", \"하는데\", \"정기적\", \"민주적\") */\nconst NON_NAME_END = /(?:적|하여|하는|하고|하며|하게|하기|하면|하다|한다|했다|는데|니다|습니|다고|해야|해서|되는|되어|된다|지만|니까|와서|워서)$/\n/** 이름에 거의 안 쓰는 음절 — 외래어 표기(도플러·피케이씨·피앤씨·리스트) */\nconst FOREIGN_SYLLABLE = /[스트크드브프즈츠끄뜨쁘쓰쯔플클블러케퍼커터텔톤튼픈핀펜헨앤엔엠켓렛]/\n\nconst TITLE_SET = new Set([...TITLES, ...RANKS, ...PARTY_ROLES])\nconst g = (x: Match, name: string): string => x.m.groups?.[name] ?? \"\"\n/** 매치된 한글 이름 (성 + 이름) */\nconst nameOf = (x: Match): string => g(x, \"dsur\") + g(x, \"dgiven\") + g(x, \"sur\") + g(x, \"given\") + g(x, \"given1\")\n\n/** 이름 스팬 (span 그룹 n) */\nconst span = (x: Match): [number, number] | undefined => x.m.indices?.groups?.n\n/** 이름 바로 뒤 글자가 이것인가 */\nconst nextIs = (x: Match, re: RegExp): boolean => {\n const n = span(x)\n return !!n && re.test(x.m.input!.slice(n[1], n[1] + 1))\n}\n/** 이름에 조사·쉼표·괄호가 바로 붙음 (\"피해자 문솔의\", \"(대표이사 하윤, 본점\") — 역할어 뒤 두 글자 이름의 조건 */\nconst closedAfter = (x: Match): boolean => nextIs(x, /^[가-힣,,))((]/)\n/** 이름 바로 뒤 여는 괄호 (\"유다온(010-…)\") */\nconst parenAfter = (x: Match): boolean => nextIs(x, /^[((]/)\n\n/**\n * 이름 후보 검증. level — 2 강함(라벨·호칭·나이·연락처: 두 글자 이름·드문 성 허용), 1 중간(당사자 역할어·표 머리글:\n * 세 글자, 모든 성), 0 약함(직함·나열: 흔한 성 + 세 글자). 복성 이름은 어느 문맥이든. two — 중간 문맥에서도 흔한 성\n * 두 글자 이름을 받는다 (역할어 뒤 조사·쉼표·괄호가 바로 붙은 자리)\n */\nfunction nameOk(x: Match, level: 0 | 1 | 2, two = false): boolean {\n if (g(x, \"latin\") || g(x, \"cjk\")) return level === 2\n const n = nameOf(x)\n const given = g(x, \"dgiven\") + g(x, \"given\") + g(x, \"given1\")\n if (STOP_WORDS.has(n) || TITLE_SET.has(n) || REGION_WORDS.has(n) || ORG_FINAL.test(n) || NON_NAME_END.test(n)\n || FOREIGN_SYLLABLE.test(given) || PARTICLE_GIVEN.has(given)) return false\n 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\n if (g(x, \"dsur\")) return true\n if (g(x, \"given1\")) return level === 2 || (two && COMMON_SURNAMES.includes(g(x, \"sur\")))\n return level > 0 || COMMON_SURNAMES.includes(g(x, \"sur\"))\n}\n\n// ─── 인명 룰 ─────────────────────────────────────────\n\n/** 이름 앞 경계 — 낱말 한가운데(\"공원녹지팀\"의 \"지팀\")·회사 이름 안(\"(주)피앤씨\")에서 시작하지 않는다 */\nconst NB = \"(?<![가-힣A-Za-z0-9])(?<!(?:\\\\(주\\\\)|㈜)\\\\s?)\"\n/** 이름 뒤 — 회사 이름 앞(\"피고 한빛물산 주식회사\")이면 사람이 아니다 */\nconst NOT_ORG_AFTER = \"(?!\\\\s?(?:주식회사|유한회사|\\\\(주\\\\)|㈜|\\\\(유\\\\)|법인|재단|협회|조합|공사|공단|은행|병원))\"\n/**\n * 직함은 균등배분(\"팀 장\"·\"과 장\" — 보도자료 담당자 표)도, 이름 뒤에서는 차수(\"제2차관\")도. 띄우면 흔한 말이 되는\n * 직함(이 장·동 장·면 장·읍 장 — \"이 장의 규정\")은 붙여 쓴 것만\n */\nconst NO_SPACING = new Set([\"이장\", \"동장\", \"면장\", \"읍장\"])\nconst titleAlt = (ts: readonly string[]): string => alt(ts.map((t) => (NO_SPACING.has(t) ? t : t.split(\"\").join(\"\\\\s?\"))))\n/**\n * 기관 이름이 붙은 직함 — 결재란·담당자 줄에서 이름 뒤에 온다(\"재난안전과장 정종민 자연재난팀장 임희정\"). 낱말 전체가\n * 직함으로 끝나야 하고, 보통명사 꼬리와 겹치는 직함(차장 — 주차장, 이사·감사 — 종합감사, 대표 — 국가대표, 의원 — 치과의원)은\n * 넣지 않는다\n */\nconst ORG_TITLE = `[가-힣]{1,12}(?:${alt([\n \"장관\", \"차관\", \"청장\", \"처장\", \"실장\", \"국장\", \"과장\", \"팀장\", \"계장\", \"부장\", \"원장\", \"소장\", \"센터장\", \"관장\", \"총장\",\n \"학장\", \"단장\", \"본부장\", \"위원장\", \"위원\", \"정책관\", \"심의관\", \"기획관\", \"담당관\", \"조정관\", \"협력관\", \"비서관\", \"대변인\",\n \"주무관\", \"사무관\", \"서기관\", \"연구관\", \"연구사\", \"조사관\", \"수사관\", \"감독관\", \"심사관\", \"담당\", \"담당자\",\n])})`\n/** 이름 뒤에 와도 되는 낱말 — 직함(기관 붙은 직함 포함)·역할어·호칭·\"사이\"·\"등\" */\nconst NEXT_OK = `${titleAlt(TITLES)}|${ORG_TITLE}|${alt([...PARTY_ROLES, \"씨\", \"님\", \"사이\", \"등\", \"외\", \"본인\", \"명의\", \"측\", \"귀하\"])}`\n/** 조사 떼고 끝 — 라벨 뒤 (\"성명 홍길동 주소 …\" 처럼 뒤에 한글 낱말이 와도 된다) */\nconst tailPlain = (p: string): string => `(?:${p})?(?![가-힣])${NOT_ORG_AFTER}`\n/**\n * 역할어·직함 뒤 이름의 끝 — 조사가 붙거나, 한글이 아닌 것(괄호·쉼표·표 칸·연락처·끝)이 오거나, 직함·호칭이 온다.\n * 맨 이름 뒤에 한글 낱말이 이어지면 명사구다 (\"피해자 구제 조치\", \"대표자 신분증 사본\", \"담당자 전문성 강화\").\n * 여기선 두 글자 이름 뒤 은·이·나도 뗀다 — 세 글자 해석(\"서윤은\")은 뒤에 낱말이 와서 이미 떨어졌다\n */\nconst tailRole = (): string =>\n `(?:(?:${P_ALL})(?![가-힣])|(?![가-힣])(?![ \\\\t]+[가-힣])|[ \\\\t]+(?:${NEXT_OK})(?:${P_ALL})?(?![가-힣]))${NOT_ORG_AFTER}`\n/** 연락처가 곧 이어짐 — 괄호·전화 표지·표 칸 건너 전화번호 전체 모양 (\"예제 02-01.\" 같은 번호 조각은 아님) */\nconst PHONE_NEXT = \"(?:\\\\s{0,3}|\\\\s*(?:\\\\||<\\\\/t[dh]>\\\\s*<t[dh][^>]*>)\\\\s*)[((]?\\\\s?(?:☎|☏|TEL|Tel|tel|전화|연락처|H\\\\.?P|휴대폰|핸드폰)?\\\\s?[:.]?\\\\s?\" +\n \"(?:0\\\\d{1,2}[-).. ]\\\\s?\\\\d{3,4}[-.. ]\\\\d{4}|\\\\+82|1[568]\\\\d\\\\d-\\\\d{4})\"\n/** 문맥어와 이름 사이 — 쌍점 또는 공백 (표 칸 경계는 라벨만: 역할어 칸 옆은 대개 하위 라벨 칸 \"신고인 | 조합명\") */\nconst SEP = \"(?<sep>\\\\s{0,2}[::]\\\\s{0,2}|\\\\s{1,3})\"\nconst CELL_SEP = \"(?<sep>\\\\s{0,2}[::]\\\\s{0,2}|\\\\s{1,3}|\\\\s*(?:\\\\||<\\\\/t[dh]>\\\\s*<t[dh][^>]*>)\\\\s*)\"\n/**\n * \"이름\" 앞에 오면 사람 이름 라벨이 아닌 낱말 — 한컴 그림 개체 설명(\"원본 그림의 이름: 공정위로고.png\",\n * \"프로그램 이름 : Adobe Photoshop\")이 문단 밖 속성·레코드 문자열에 흔하다(코퍼스 HWPX·HWP 파일 단위 실측)\n */\nconst NOT_PERSON_OF = alt([\"그림의\", \"프로그램\", \"파일\", \"모델\", \"글꼴\", \"필드\", \"개체\", \"카메라\", \"제조사\", \"장치\", \"기기\", \"폴더\",\n \"문서\", \"양식\", \"서식\", \"항목\", \"제품\", \"상품\", \"회사\", \"업체\", \"기관\", \"단체\", \"시트\", \"변수\", \"함수\", \"도메인\", \"서버\", \"계정\"])\n/** 이름 라벨 — \"성명\"·\"성함\"은 공백 뒤도, 보통명사로도 흔한 \"이름\"(글꼴 이름·필드 이름)은 쌍점·표 칸 뒤만 */\nconst NAME_LABEL = `(?:(?:${spaced([\"성명\", \"성함\", \"한글성명\", \"대표자성명\"])})(?:\\\\s?\\\\(\\\\s?한\\\\s?글\\\\s?\\\\))?${CELL_SEP}|(?<!(?:${NOT_PERSON_OF})\\\\s?)(?:${spaced([\"이름\"])})(?<sep2>\\\\s{0,2}[::]\\\\s{0,2}|\\\\s*(?:\\\\||<\\\\/t[dh]>\\\\s*<t[dh][^>]*>)\\\\s*))`\n/**\n * 이름 뒤 직함(약한 문맥)에서 빼는 직함 — 꾸밈말을 흔히 받는 일반 명사형 직함 (\"우수한 교장\", \"지정한 담당자\",\n * \"해당 교사\", \"외부 위원\", \"하반기 강사\" — 코퍼스 실측). 이름 앞 직함(\"교장 홍길동\")으로는 그대로 쓴다\n */\nconst TITLES_AFTER = TITLES.filter((t) => ![\"교장\", \"교감\", \"교사\", \"강사\", \"담당\", \"담당자\", \"위원\", \"이사\", \"감사\", \"선생\", \"선생님\", \"간호사\", \"약사\", \"연구원\", \"주임\"].includes(t))\n/** 가리는 그룹 — 성·첫 글자는 남긴다 */\nconst NAME_MASK = [\"dgiven\", \"given\", \"given1\", \"lrest\", \"crest\"]\nconst ROLE_RE = alt(PARTY_ROLES)\nconst TITLE_RE = titleAlt(TITLES)\nconst TITLE_AFTER_RE = `(?:제\\\\d{1,2}\\\\s?)?(?:${titleAlt(TITLES_AFTER)})`\n/** 표 열 머리글이 이것이면 칸 전체가 이름인 자리 */\nconst NAME_HEADER_RE = new RegExp(`^\\\\s*(?:${spaced([\"성명\", \"이름\", \"성함\", \"담당자\", \"대표자\", \"예금주\", \"작성자\", \"신청인\", \"청구인\", \"민원인\", \"소유자\", \"수령인\", \"대리인\", \"보호자\", \"세대주\", \"참석자\"])})(?:\\\\s*\\\\([^)]*\\\\))?\\\\s*$`)\n/** 나열 이음 — 앞 이름과 이 이름 사이 (\"A, B\", \"A와 B\") */\nconst LIST_GAP_RE = /^(?:와|과|이나|나|이랑|랑)?\\s{0,2}[,、·]?\\s{0,2}$/\n\n/** 한자 병기가 같은 이름인가 — 글자 수가 같고, 첫 한자(복성은 둘)가 성씨 한자이며 독음이 한글 성과 같다 */\nconst hanjaMatches = (x: Match): boolean => {\n const han = g(x, \"h1\") + g(x, \"hrest\")\n if (han.length !== nameOf(x).length) return false\n if (g(x, \"dsur\")) return DOUBLE_SURNAME_HANJA[han.slice(0, 2)] === g(x, \"dsur\")\n return (SURNAME_HANJA[han[0]] ?? \"\").includes(g(x, \"sur\"))\n}\n/** 본관 + 성 + 씨 — \"김해김씨\"·\"밀양박씨\"·\"전주이씨\"는 사람이 아니라 씨족 */\nconst clanName = (x: Match): boolean => {\n const n = nameOf(x)\n return n.length === 3 && ALL_SURNAMES.includes(n[2]) && REGION_WORDS.has(n.slice(0, 2)) && nextIs(x, /^\\s?씨/)\n}\n/** 직함 모양 꼬리를 가진 보통명사 — 세면장·운동장·주차장·수영장·경기장·공연장·시험장·작업장·사업장·공장장 아님 */\nconst NOT_TITLE_TAIL = /(?:세면장|운동장|주차장|수영장|경기장|공연장|시험장|작업장|사업장|회의장|행사장|전시장|선착장|정류장|야영장)$/\nconst sepHasColon = (x: Match): boolean => /[::]/.test(g(x, \"sep\"))\n/** 이름 바로 뒤(조사 건너) 쌍점 — \"위원 탁서준: …\" 발언자 표기 */\nconst colonAfter = (x: Match): boolean => {\n const n = span(x)\n return !!n && new RegExp(`^(?:${P_ALL})?\\\\s?[::]`).test(x.m.input!.slice(n[1], n[1] + 6))\n}\n\nexport const NAME_VARIANTS: readonly Variant[] = [\n { // 직함·역할어 뒤 한자 병기 — \"검찰연구관 박서진(朴瑞辰)\", \"검사 최하준(崔夏俊)\" (인사 발령·판결문의 인명 표기).\n // 한자도 같은 이름이라 스팬에 넣고 첫 글자(성)만 남긴다 — \"박●●(朴●●)\". 병기만으로는 문맥이 아니다 — 한자어 병기\n // (\"고층(高層)\"·\"황하(黃河)\"·\"표출(表出)\")도 첫 한자가 성씨 한자다(코퍼스 실측). 맨 앞 — 직함 변형이 한글만 먼저 잡지 않게\n re: new RegExp(`(?:${TITLE_RE}|${ROLE_RE}|${spaced(RANKS)})${SEP}(?<n>${hangulName(ALL_SURNAMES, () => \"\\\\s?[((][\\\\u4E00-\\\\u9FFF]{2,4}[))]\")}\\\\s?[((](?<h1>[\\\\u4E00-\\\\u9FFF])(?<hrest>[\\\\u4E00-\\\\u9FFF]{1,3})[))])`, \"dg\"),\n span: \"n\", mask: [...NAME_MASK, \"hrest\"],\n ok: (x) => hanjaMatches(x) && nameOk(x, 2),\n },\n { // 라벨 뒤 — \"성명: 홍길동\", \"| 성명 | 홍길동 |\", \"이름: 남궁현\"\n re: new RegExp(`${NAME_LABEL}(?<n>${hangulName(ALL_SURNAMES, tailPlain)}|${LATIN_NAME}${FOREIGN_FOLLOW}|${CJK_NAME}${FOREIGN_FOLLOW})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, /[::]/.test(g(x, \"sep\") + g(x, \"sep2\")) ? 2 : 1),\n },\n { // 당사자 역할어 뒤 — \"피고인 최지훈은\", \"대표이사 오세진)\", \"작성자 한서율,\"\n re: new RegExp(`(?:${ROLE_RE})${SEP}(?<n>${hangulName(ALL_SURNAMES, tailRole)}|${LATIN_NAME}${FOREIGN_FOLLOW}|${CJK_NAME}${FOREIGN_FOLLOW})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, g(x, \"latin\") || g(x, \"cjk\") ? 2 : 1, closedAfter(x)),\n },\n { // 직함 뒤 — \"과장 홍길동\", \"회장 : 도서연\"(쌍점·발언자 쌍점이면 강한 문맥). 직함이 다른 낱말 꼬리인 자리\n // (\"세면장 조도임\"의 면장, \"주차장\"의 차장)는 직함이 아니다\n re: new RegExp(`(?<t>${TITLE_RE})${SEP}(?<n>${hangulName(ALL_SURNAMES, tailRole)})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => !NOT_TITLE_TAIL.test(x.m.input!.slice(Math.max(0, x.m.index! - 3), x.m.index! + g(x, \"t\").length))\n && nameOk(x, sepHasColon(x) || colonAfter(x) ? 1 : 0),\n },\n { // 호칭 앞 — \"홍길동 씨\", \"홍길동님께\", \"Daniel Brown 씨\". \"기업날씨\"·\"솜씨\"·\"글씨\" 같은 씨 낱말은 호칭이 아니다\n re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => `(?:(?<![날솜말글불맘])\\\\s?씨|\\\\s?님)(?:${P_ALL})?(?![가-힣])(?!\\\\s?(?:㈜|\\\\(주\\\\)))`)}|${LATIN_NAME}(?=\\\\s?(?:씨|님)(?![가-힣]))|${CJK_NAME}(?=\\\\s?(?:씨|님)(?![가-힣])))`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, 2) && !clanName(x),\n },\n { // 연락처 앞 — \"유다온(010-…)\", \"홍길동 ☎ 02-…\", \"| 김지유 | (051-…) |\". 두 글자는 괄호가 바로 붙을 때만 —\n // 기관 안내 표의 \"마음상담센터 한별 | 031-…\" 같은 기관 이름 칸(코퍼스 실측)\n re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => PHONE_NEXT)})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, 1, parenAfter(x)),\n },\n { // 나이·성별 괄호 앞 — \"박도현(만 52세)\", \"서하(여, 31세)\"\n re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => \"\\\\s?[((]\\\\s?(?:만\\\\s?\\\\d{1,3}\\\\s?세|\\\\d{1,3}\\\\s?세|여|남|女|男)\\\\s?[,,))]\")})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, 2),\n },\n { // 직함 앞 — \"홍길동 과장\", \"김민수 국장에게\"(약한 문맥). 직함 뒤에 연락처가 이어지면 강한 문맥(\"편도윤 담당자(☎ …)\")\n re: new RegExp(`${NB}(?<n>${hangulName(ALL_SURNAMES, () => `\\\\s(?:제\\\\d{1,2}\\\\s?)?(?:${TITLE_RE})(?:님)?(?:${P_ALL})?(?![가-힣])`)})(?:(?<after>\\\\s(?:${TITLE_RE})${PHONE_NEXT})|(?<weak>\\\\s(?:${TITLE_AFTER_RE})(?:님)?(?:${P_ALL})?(?![가-힣])))`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, g(x, \"after\") ? 1 : 0),\n },\n { // 당사자 번호 나열 — \"피고 1. ○○은행(대표자 오세진) 2. 윤지호는\"\n re: new RegExp(`(?:${ROLE_RE})\\\\s{1,2}(?:1[.)]\\\\s?[^\\\\n]{1,60}?\\\\s)?\\\\d{1,2}[.)]\\\\s?(?<n>${hangulName(ALL_SURNAMES, tailRole)})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => nameOk(x, 1),\n },\n { // 표 칸 전체가 이름 — 열 머리글이 성명·담당자·대표자 …\n re: new RegExp(`(?:^|\\\\||<t[dh][^>]*>)\\\\s*(?<n>${hangulName(ALL_SURNAMES, () => \"\\\\s*(?:\\\\||<\\\\/t[dh]>|$)\")})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => NAME_HEADER_RE.test(x.header() ?? \"\") && nameOk(x, 1),\n },\n { // 직함 칸 바로 옆 칸이 이름 하나 — 결재란 \"<td>주무관</td><td>김서준</td><td>복지기획팀장</td><td>유재민</td>\".\n // 두 칸 모두 그 낱말뿐일 때만, 약한 문맥(흔한 성 + 세 글자)으로 — 옆 칸은 \"귀하\"·\"직인\"·\"성명\"·\"사무관\" 같은 서식 낱말이\n // 흔하다. 머리글 행은 이름 칸이 아니다 — HTML 은 <th>, GFM 은 첫 행(열 머리글이 없는 자리): \"| 치과의원 | 한의원 |\"\n re: new RegExp(`(?:^|\\\\||<t[dh][^>]*>)\\\\s*(?:${TITLE_RE}|${ORG_TITLE})\\\\s*(?:(?<pipe>\\\\|)|<\\\\/t[dh]>\\\\s*<td[^>]*>)\\\\s*(?<n>${hangulName(ALL_SURNAMES, () => \"\\\\s*(?:\\\\||<\\\\/td>)\")})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => (!g(x, \"pipe\") || x.header() !== undefined) && nameOk(x, 0),\n },\n { // 나열 — 앞에서 찾은 이름에 쉼표·와/과로 이어진 이름 (\"문가윤, 방시우, 엄다인 등\")\n re: new RegExp(`(?:[,、·]|(?<=[가-힣])(?:와|과))\\\\s{0,2}(?<n>${hangulName(ALL_SURNAMES, tailRole)})`, \"dg\"),\n span: \"n\", mask: NAME_MASK,\n ok: (x) => {\n const p = x.prev()\n const n = span(x)\n return !!p && !!n && p.rule === \"name\" && LIST_GAP_RE.test(x.m.input!.slice(p.end, n[0])) && nameOk(x, 0)\n },\n },\n]\n","/**\n * redact 룰 정의 — 룰별 정규식 변형·검증기(생년월일 세기·Luhn·사업자/법인 체크섬·전화 국번)·라벨 사전.\n * 엔진(정규화·겹침 처리·마스킹·마크다운 표 문맥)은 redact.ts. 룰별 근거·오탐 실측은 bench/redact-bench.mjs.\n */\n\nimport type { RedactRule } from \"./redact.js\"\nimport { ADDRESS_VARIANTS, NAME_VARIANTS } from \"./redact-name-address.js\"\n\n/**\n * 룰 우선순위 (앞이 높음). crn 을 rrn 앞에 — 법인 라벨이 붙은 6-7 번호는 법인번호로.\n * email 을 번호 룰들 앞에(로컬파트 숫자 오탐 방지), card·brn 을 phone·account 앞에,\n * driver 를 account 앞에(면허번호 12자리 4그룹이 계좌 패턴에 포섭됨).\n * 주소·인명(opt-in)은 번호 뒤 — 번호가 먼저 자리를 잡고, 도로명이 사람 이름(\"세종대로\")과 겹치면 주소가 이긴다.\n */\nexport const RULE_PRIORITY: readonly RedactRule[] = [\n \"crn\", \"rrn\", \"email\", \"card\", \"brn\", \"phone\", \"driver\", \"passport\", \"account\", \"ip\", \"address\", \"name\",\n]\n\n// ─── 검증기 ───────────────────────────────────────────\n\n/** Luhn 체크섬 (카드번호) */\nfunction luhnValid(digits: string): boolean {\n let sum = 0\n for (let i = 0; i < digits.length; i++) {\n let d = digits.charCodeAt(digits.length - 1 - i) - 48\n if (i % 2 === 1) {\n d *= 2\n if (d > 9) d -= 9\n }\n sum += d\n }\n return sum % 10 === 0\n}\n\nconst DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n\n/**\n * 주민·외국인등록번호 앞 6자리 생년월일 검증 — 성별 자리로 세기를 정한다\n * (1·2·5·6 → 1900년대, 3·4·7·8 → 2000년대). 2000년대생은 올해를 넘지 못한다.\n */\nfunction birthdateValid(front6: string, genderDigit: string): boolean {\n const yy = Number(front6.slice(0, 2))\n const mm = Number(front6.slice(2, 4))\n const dd = Number(front6.slice(4, 6))\n if (mm < 1 || mm > 12 || dd < 1) return false\n const g = Number(genderDigit)\n const year = (g === 3 || g === 4 || g === 7 || g === 8 ? 2000 : 1900) + yy\n if (year > new Date().getFullYear()) return false\n const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)\n const max = mm === 2 ? (leap ? 29 : 28) : DAYS_IN_MONTH[mm - 1]\n return dd <= max\n}\n\n/** 사업자등록번호 체크섬 (국세청 공개 가중치 1,3,7,1,3,7,1,3,5) */\nfunction brnChecksumValid(d: string): boolean {\n const w = [1, 3, 7, 1, 3, 7, 1, 3, 5]\n let sum = 0\n for (let i = 0; i < 9; i++) sum += w[i] * (d.charCodeAt(i) - 48)\n sum += Math.floor(((d.charCodeAt(8) - 48) * 5) / 10)\n return (10 - (sum % 10)) % 10 === d.charCodeAt(9) - 48\n}\n\n/** 법인등록번호 체크섬 (가중치 1,2 반복, 모듈러 10) */\nfunction crnChecksumValid(d: string): boolean {\n let sum = 0\n for (let i = 0; i < 12; i++) sum += (i % 2 === 0 ? 1 : 2) * (d.charCodeAt(i) - 48)\n return (10 - (sum % 10)) % 10 === d.charCodeAt(12) - 48\n}\n\nconst digitsOf = (s: string): string => s.replace(/\\D/g, \"\")\n/** 같은 숫자 반복(0000…, 1111…) — 서식 예시·자리표시자 */\nconst allSameDigit = (d: string): boolean => d.length > 0 && /^(\\d)\\1*$/.test(d)\n\n// ─── 라벨 문맥 ────────────────────────────────────────\n\nexport type Ctx = \"rrn\" | \"crn\" | \"brn\" | \"account\" | \"card\" | \"phone\" | \"passport\" | \"driver\" | \"ip\" | \"other\"\n\n/**\n * 라벨 사전. 값 앞 창(현재 셀 + 왼쪽 셀, 태그 제거 후 40자, 앞선 값 뒤부터)에 있는 라벨 유형 **집합**이\n * 문맥이다. \"주민등록번호(법인등록번호)\"처럼 한 칸에 두 라벨이 붙은 서식은 둘 다 문맥이 된다 — 모호하면\n * 가리는 쪽(주민번호)으로 판정한다. \"other\" 는 PII 가 아닌 식별번호 라벨(출원번호·과제번호·ISBN·예산과목…).\n */\nconst LABELS: ReadonlyArray<readonly [Ctx, RegExp]> = [\n [\"crn\", /법\\s*인\\s*(?:등\\s*록\\s*)?번\\s*호/g],\n [\"rrn\", /주\\s*민\\s*(?:등\\s*록\\s*)?번\\s*호|주\\s*민\\s*등\\s*록|외\\s*국\\s*인\\s*(?:등\\s*록\\s*)?번\\s*호|거\\s*소\\s*신\\s*고\\s*번\\s*호|생\\s*년\\s*월\\s*일|resident\\s*registration|\\bRRN\\b/gi],\n [\"brn\", /사\\s*업\\s*자\\s*(?:등\\s*록\\s*)?번\\s*호|고\\s*유\\s*번\\s*호|business\\s*registration/gi],\n [\"account\", /계\\s*좌|예\\s*금\\s*주|입\\s*금|은\\s*행|뱅\\s*크|농\\s*협|신\\s*협|수\\s*협|우\\s*체\\s*국|금\\s*고|bank\\s*account|account\\s*(?:no|number)/gi],\n [\"card\", /카\\s*드|credit\\s*card|card\\s*(?:no|number)/gi],\n [\"phone\", /전\\s*화|연\\s*락\\s*처|휴\\s*대|핸\\s*드\\s*폰|팩\\s*스|☎|☏|콜\\s*센\\s*터|고\\s*객\\s*센\\s*터|대\\s*표\\s*번\\s*호|상\\s*담|문\\s*의|\\b(?:tel|fax|mobile|phone|h\\.?p|ARS)\\b/gi],\n [\"passport\", /여\\s*권|passport/gi],\n [\"driver\", /운\\s*전\\s*면\\s*허|면\\s*허\\s*번\\s*호|driver'?s?\\s*licen[cs]e/gi],\n [\"ip\", /\\bIP\\b|아\\s*이\\s*피/g],\n [\"other\", /출\\s*원\\s*번\\s*호|과\\s*제\\s*번\\s*호|사\\s*업\\s*번\\s*호|접\\s*수\\s*번\\s*호|민\\s*원\\s*번\\s*호|신\\s*청\\s*번\\s*호|문\\s*서\\s*번\\s*호|관\\s*리\\s*번\\s*호|주\\s*문\\s*번\\s*호|일\\s*련\\s*번\\s*호|발\\s*간\\s*등\\s*록|등\\s*록\\s*번\\s*호\\s*\\(\\s*기\\s*록|ISBN|ISSN|예\\s*산|과\\s*목|코\\s*드|모\\s*델|제\\s*품|부\\s*품|시\\s*리\\s*얼|serial|model|\\bS\\/N\\b|바\\s*코\\s*드|기\\s*록\\s*물|사\\s*건\\s*번\\s*호/gi],\n]\n\n/** 셀 경계 — GFM 파이프(이스케이프 제외)와 HTML 셀 닫는 태그 */\nconst CELL_SEP_RE = /(?<!\\\\)\\||<\\/t[dh]>/gi\n/** 앞선 값 — 창 안에서 이 뒤부터만 본다 (앞 값의 라벨이 뒤 값으로 번지지 않게) */\nconst PREV_VALUE_RE = /\\d[\\d\\-.\\s]{4,}\\d/g\n\n/** 텍스트 안 라벨 유형 집합 */\nexport function labelsIn(s: string): Set<Ctx> {\n const out = new Set<Ctx>()\n for (const [ctx, re] of LABELS) {\n re.lastIndex = 0\n if (re.test(s)) out.add(ctx)\n }\n return out\n}\n\n/** 매치 앞 문맥 라벨 집합 — 현재 셀 + 바로 왼쪽 셀, 태그 제거 후 40자, 앞선 값 뒤부터 */\nexport function labelsBefore(norm: string, start: number): Set<Ctx> {\n let win = norm.slice(Math.max(0, start - 120), start)\n const seps = [...win.matchAll(CELL_SEP_RE)]\n if (seps.length >= 2) {\n const s = seps[seps.length - 2]\n win = win.slice((s.index as number) + s[0].length)\n }\n win = win.replace(/<[^>]*>/g, \" \").slice(-40)\n const prev = [...win.matchAll(PREV_VALUE_RE)].pop()\n if (prev) win = win.slice((prev.index as number) + prev[0].length)\n return labelsIn(win)\n}\n\n/** 값 바로 뒤 비개인 식별번호 명사 — \"11-24-123456-01 (접수)\", \"S123A4567 부품\" */\nconst OTHER_AFTER_RE = /^\\s{0,2}[(\\[]?\\s{0,2}(?:접\\s*수|신\\s*청|주\\s*문|사\\s*건|과\\s*제|문\\s*서|관\\s*리|부\\s*품|제\\s*품|모\\s*델|코\\s*드|시\\s*리\\s*얼|serial|model)/i\n\n// ─── 룰 정의 ──────────────────────────────────────────\n\nexport interface Match {\n /** 정규화 텍스트 매치 (d 플래그 — 그룹 인덱스 포함) */\n m: RegExpMatchArray\n /** 라벨 문맥 집합 (인라인, 비었으면 표 머리글 — 지연 계산) */\n ctx: () => ReadonlySet<Ctx>\n /** 이 자리가 표 칸이면 그 열 머리글 원문 — 인명 룰의 \"성명\" 열 */\n header: () => string | undefined\n /** 이 매치 앞에서 가장 가까이 끝난 탐지 — 인명 나열(\"A, B, C 등\") 잇기 */\n prev: () => { rule: RedactRule; start: number; end: number } | undefined\n}\n\nexport interface Variant {\n re: RegExp\n /** 이 유형 라벨이 문맥에 있을 때만 인정 (모호한 모양) */\n needs?: Ctx\n /** 추가 검증 — false면 스킵 */\n ok?: (x: Match) => boolean\n /** 마스킹할 이름 그룹 — 그 범위의 글자·숫자를 maskChar 로 (구분자·공백·기존 가림표는 유지) */\n mask: readonly string[]\n /** 그룹 안 글자를 구분자까지 전부 가린다 (이메일 로컬파트 — 이름 마디 길이도 숨김) */\n maskAll?: boolean\n /** 탐지 스팬이 되는 이름 그룹 — 문맥어(\"피고인 \")를 매치에 넣고 값만 스팬으로 (없으면 매치 전체) */\n span?: string\n}\n\nconst g = (x: Match, name: string): string => x.m.groups?.[name] ?? \"\"\nconst has = (x: Match, c: Ctx): boolean => x.ctx().has(c)\n/** 법인 라벨만 있고 주민 라벨은 없다 — 이때만 6-7 번호를 주민번호에서 뺀다 (모호하면 가린다) */\nconst crnOnly = (x: Match): boolean => has(x, \"crn\") && !has(x, \"rrn\")\n/** 매치 뒤 원문 (정규화 텍스트) */\nconst after = (x: Match, n = 16): string => {\n const end = (x.m.index as number) + x.m[0].length\n return x.m.input!.slice(end, end + n)\n}\nconst before = (x: Match, n = 2): string => {\n const start = x.m.index as number\n return x.m.input!.slice(Math.max(0, start - n), start)\n}\n/** 비개인 식별번호 라벨이 앞에 있거나(해당 PII 라벨 없이) 뒤에 \"(접수)\"류 명사가 붙은 값 */\nconst otherCtx = (x: Match, own: Ctx): boolean => (has(x, \"other\") && !has(x, own)) || OTHER_AFTER_RE.test(after(x))\n/**\n * 값 바로 앞이 일련번호 라벨(\"S/N 0507-9978-0522\", \"바코드: 1588-…\") — 전화 모양이어도 기기·상품 번호.\n * 전화는 \"예산\"·\"과목\" 같은 넓은 other 라벨로는 막지 않는다(예산담당관 044-… 실번호) — 바로 앞 좁은 라벨만\n */\nconst SERIAL_BEFORE_RE = /(?:\\bS\\/N|\\bserial(?:\\s*(?:no|number))?|시\\s*리\\s*얼(?:\\s*번\\s*호)?|일\\s*련\\s*번\\s*호|바\\s*코\\s*드)\\s*[:.]?\\s*$/i\nconst serialBefore = (x: Match): boolean => SERIAL_BEFORE_RE.test(before(x, 20)) && !has(x, \"phone\")\n\n// 공통 경계 — 숫자·영문·@·하이픈에 붙은 번호(더 긴 코드의 일부)는 제외\nconst PHONE_PREFIX = \"0(?:1[016789]|2|3[1-3]|4[1-4]|5[1-5]|6[1-4]|70|80|60|50[2-8])\"\nconst PHONE_AREA = \"0(?:1[016789]|2|3[1-3]|4[1-4]|5[1-5]|6[1-4]|70|80|50[2-8])\"\n/**\n * 전화번호 구분자 한 자리 — 하이픈(앞뒤 공백 0~2칸: \"02- 3668-1330\", \"031- 405 -8255\"), 공백 1~2칸, 온점.\n * 두 자리는 따로 고르되 온점은 온점끼리만(phoneSepsOk) — \"010.1234-5678\" 혼합은 미탐 계약 유지\n */\nconst PHONE_SEP = \"(?: {0,2}- {0,2}| {1,2}|\\\\.)\"\n/** 전화번호 끝 경계 — 영숫자·@·하이픈이 붙으면 더 긴 코드(예산과목 064-1100-1131-301-210) */\nconst PHONE_END = \"(?![\\\\w@-])\"\n\n/** 두 구분자 조합 — 둘 다 없거나 둘 다 있고, 온점은 온점끼리. 온점 구분이면 뒤 \".숫자\"(더 긴 점 코드) 거부 */\nfunction phoneSepsOk(x: Match, s1: string, s2: string): boolean {\n if ((s1 === \"\") !== (s2 === \"\")) return false\n if ((s1 === \".\") !== (s2 === \".\")) return false\n return s1 !== \".\" || !/^\\.\\d/.test(after(x, 2))\n}\n\n/**\n * 전화번호 숫자 검증 — 가입자 번호 전부 0(010-0000-0000 자리표시자) 제외. 구분자 없는 지역번호는\n * 국번이 0으로 시작하면 코드로 본다(0202030201 NCS 능력단위 류) — 구분자가 있으면 형식을 믿는다\n * (실결재 결문 팩스 02-02xx-xxxx 실측). 02-123-4567·010-1234-5678 같은 관용 예시는 실번호처럼 가린다.\n */\nfunction phoneDigitsOk(pre: string, mid: string, last: string, bare: boolean): boolean {\n if (/^0+$/.test(mid + last)) return false\n return !bare || !/^0(?:2|3[1-3]|4[1-4]|5[1-5]|6[1-4])$/.test(pre) || mid[0] !== \"0\"\n}\n\n/** 이메일 시작 경계 — 로컬파트 한가운데(gd.hong 의 hong)·이미 가린 로컬파트 꼬리(j*********5@…)에서는 시작하지\n * 않는다. 글머리·강조 가림표 뒤(*hong@…, ●hong@…)와 문장 부호 뒤(.hong@…)는 허용 */\nconst EMAIL_START = \"(?<![\\\\w%+])(?<!\\\\w[.\\\\-])(?<![A-Za-z0-9][*●○◯×#]+)\"\nconst EMAIL_DOMAIN = \"(?<domain>[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\\\\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)*\\\\.(?<tld>[A-Za-z]{2,24}))(?![\\\\w-])\"\nconst FILE_EXT_TLD = /^(?:png|jpe?g|gif|bmp|svg|webp|tiff?|hwpx?|hwp|pdf|docx?|xlsx?|pptx?|txt|zip|csv|json|xml|html?|js|css|mp[34]|wav|avi|exe)$/i\n\nexport const RULES: Record<RedactRule, readonly Variant[]> = {\n // 주민·외국인등록번호 — 앞 6자리(생년월일) 유지, 뒤 7자리 마스크\n rrn: [\n { // 하이픈형 (앞뒤 공백 0~2칸, 유니코드 대시는 정규화로 흡수). 뒤에 \"-글자\"는 허용, \"-숫자\"는 더 긴 코드.\n // 법인 라벨만 있으면 제외\n re: /(?<![\\w-])(?<front>\\d{6})(?<sep> {0,2}- {0,2})(?<back>[1-8]\\d{6})(?![\\w])(?!-\\d)/dg,\n ok: (x) => birthdateValid(g(x, \"front\"), g(x, \"back\")[0]) && !crnOnly(x),\n mask: [\"back\"],\n },\n { // 뒷자리 일부만 가린 형태 (900101-1******, 900101-1694***) — 남은 숫자를 가린다\n re: /(?<![\\w-])(?<front>\\d{6})(?<sep> {0,2}- {0,2})(?<back>[1-8][0-9*●○◯×xX#]{6})(?![\\w*●○◯×#])(?!-\\d)/dg,\n ok: (x) => /[*●○◯×xX#]/.test(g(x, \"back\")) && birthdateValid(g(x, \"front\"), g(x, \"back\")[0]) && !crnOnly(x),\n mask: [\"back\"],\n },\n { // 구분자 없음·공백/온점/가운뎃점/빗금/물결 구분 — 주민/외국인등록번호(생년월일) 라벨이 있을 때만\n re: /(?<![\\w@.-])(?<front>\\d{6})(?<sep>[ .·/~]?)(?<back>[1-8]\\d{6})(?![\\w@-])/dg,\n needs: \"rrn\",\n ok: (x) => birthdateValid(g(x, \"front\"), g(x, \"back\")[0]),\n mask: [\"back\"],\n },\n ],\n // 법인등록번호 (opt-in) — 법인 라벨이 있거나, 뒷자리가 0으로 시작(주민번호 불가)하고 체크섬 유효\n crn: [\n {\n re: /(?<![\\w-])(?<front>\\d{6})(?<sep> {0,2}- {0,2})(?<back>\\d{7})(?![\\w-])/dg,\n ok: (x) => !allSameDigit(g(x, \"front\") + g(x, \"back\")) && (crnOnly(x)\n || (g(x, \"back\")[0] === \"0\" && crnChecksumValid(g(x, \"front\") + g(x, \"back\")))),\n mask: [\"back\"],\n },\n {\n re: /(?<![\\w@.-])(?<front>\\d{6})(?<back>\\d{7})(?![\\w@-])/dg,\n needs: \"crn\",\n ok: (x) => !allSameDigit(g(x, \"front\") + g(x, \"back\")) && !has(x, \"rrn\"),\n mask: [\"back\"],\n },\n ],\n // 이메일 — 로컬파트 첫 글자만 남기고 마스크, 도메인 유지. 파일명(image@2x.png)은 제외\n // 이미 가린 로컬파트(j*********5@…) 뒤꼬리에서 시작하지 않는다 — 가림표 바로 뒤 매치 금지\n email: [\n {\n re: new RegExp(`${EMAIL_START}(?<l1>[A-Za-z0-9])(?<lrest>[A-Za-z0-9._%+-]+)@${EMAIL_DOMAIN}`, \"dg\"),\n ok: (x) => !FILE_EXT_TLD.test(g(x, \"tld\")),\n mask: [\"lrest\"],\n maskAll: true,\n },\n { // 한 글자 로컬파트 — 첫 글자를 남기면 가린 게 없으므로 전부 가린다\n re: new RegExp(`${EMAIL_START}(?<l1>[A-Za-z0-9])@${EMAIL_DOMAIN}`, \"dg\"),\n ok: (x) => !FILE_EXT_TLD.test(g(x, \"tld\")),\n mask: [\"l1\"],\n },\n ],\n // 카드번호 — 가운데 마스크 + Luhn. 구분자 없는 15~16자리는 카드 라벨이 있을 때만\n card: [\n { // 4-4-4-4 (같은 구분자 강제)\n re: /(?<![\\w-])(?<g1>\\d{4})(?<sep>[- ])(?<g2>\\d{4})\\k<sep>(?<g3>\\d{4})\\k<sep>(?<g4>\\d{4})(?![\\w-])(?![- ]\\d)/dg,\n ok: (x) => { const d = digitsOf(x.m[0]); return luhnValid(d) && !allSameDigit(d) },\n mask: [\"g2\", \"g3\"],\n },\n { // AMEX 4-6-5\n re: /(?<![\\w-])(?<g1>3[47]\\d{2})(?<sep>[- ])(?<g2>\\d{6})\\k<sep>(?<g3a>\\d)(?<g3b>\\d{4})(?![\\w-])(?![- ]\\d)/dg,\n ok: (x) => luhnValid(digitsOf(x.m[0])),\n mask: [\"g2\", \"g3a\"],\n },\n { // 온점 구분 4.4.4.4 — 카드 라벨 필요 (버전·좌표 모양과 겹침)\n re: /(?<![\\w.-])(?<g1>\\d{4})\\.(?<g2>\\d{4})\\.(?<g3>\\d{4})\\.(?<g4>\\d{4})(?![\\w-])(?!\\.\\d)/dg,\n needs: \"card\",\n ok: (x) => { const d = digitsOf(x.m[0]); return luhnValid(d) && !allSameDigit(d) },\n mask: [\"g2\", \"g3\"],\n },\n { // 무구분 15~16자리 — 카드 라벨 필요\n re: /(?<![\\w@.-])(?<g1>\\d{4})(?<mid>\\d{7,8})(?<g4>\\d{4})(?![\\w@-])/dg,\n needs: \"card\",\n ok: (x) => { const d = digitsOf(x.m[0]); return luhnValid(d) && !allSameDigit(d) },\n mask: [\"mid\"],\n },\n ],\n // 사업자등록번호 3-2-5 — 체크섬 유효 또는 사업자 라벨. 뒤 5자리(일련·검증번호) 마스크\n brn: [\n {\n re: /(?<![\\w-])(?<a>\\d{3})-(?<b>\\d{2})-(?<c>\\d{5})(?![\\w-])/dg,\n ok: (x) => {\n const d = digitsOf(x.m[0])\n if (allSameDigit(d)) return false\n return has(x, \"brn\") || (brnChecksumValid(d) && !has(x, \"account\") && !otherCtx(x, \"brn\"))\n },\n mask: [\"c\"],\n },\n {\n re: /(?<![\\w@.-])(?<a>\\d{3})(?<b>\\d{2})(?<c>\\d{5})(?![\\w@-])/dg,\n needs: \"brn\",\n ok: (x) => !allSameDigit(digitsOf(x.m[0])),\n mask: [\"c\"],\n },\n ],\n // 전화번호 — 가운데 자리만 마스크 (대표번호는 뒤 4자리).\n // 뒤에 \"-\"가 붙으면 더 긴 코드(예산과목 064-1100-1131-301-210)의 일부라 제외.\n phone: [\n { // 국내 일반: 휴대폰·지역·070/080/060·050x 안심번호. 구분자 -·공백·온점(PHONE_SEP) 또는 무구분\n re: new RegExp(`(?<![\\\\w@+-])(?<pre>${PHONE_PREFIX})(?<s1>${PHONE_SEP}?)(?<mid>\\\\d{3,4})(?<s2>${PHONE_SEP}?)(?<last>\\\\d{4})${PHONE_END}`, \"dg\"),\n ok: (x) => {\n const pre = g(x, \"pre\")\n const bare = g(x, \"s1\") === \"\"\n if (!phoneSepsOk(x, g(x, \"s1\"), g(x, \"s2\"))) return false\n if (!phoneDigitsOk(pre, g(x, \"mid\"), g(x, \"last\"), bare)) return false\n if (serialBefore(x)) return false\n if (!bare) return true // 구분자 있는 번호는 쉼표·온점 목록 안에서도 번호 (\"02-…,010-…\")\n // 무구분 — 소수점 뒤(3.0212345678)는 아님, 휴대폰이 아닌 번호는 쉼표 숫자 목록 안의 코드(…,0507502002,…) 제외\n if (/\\d\\.$/.test(before(x))) return false\n if (pre.startsWith(\"01\")) return true // 휴대폰 무구분 목록 \"01023456789,01098765432\"\n return !/\\d,$/.test(before(x)) && !/^,\\d/.test(after(x, 2))\n },\n mask: [\"mid\"],\n },\n { // 괄호 지역번호: (02) 123-4567, (042)481-6300\n re: new RegExp(`(?<![\\\\w@])\\\\((?<pre>${PHONE_AREA})\\\\) ?(?<mid>\\\\d{3,4})(?<s2>${PHONE_SEP}?)(?<last>\\\\d{4})${PHONE_END}`, \"dg\"),\n ok: (x) => phoneDigitsOk(g(x, \"pre\"), g(x, \"mid\"), g(x, \"last\"), false) && (g(x, \"s2\") !== \".\" || !/^\\.\\d/.test(after(x, 2))),\n mask: [\"mid\"],\n },\n { // 지역번호 뒤 닫는 괄호: 02)450-1234\n re: new RegExp(`(?<![\\\\w@(+-])(?<!\\\\d\\\\.)(?<pre>${PHONE_AREA})\\\\) ?(?<mid>\\\\d{3,4})(?<s2>${PHONE_SEP})(?<last>\\\\d{4})${PHONE_END}`, \"dg\"),\n ok: (x) => phoneDigitsOk(g(x, \"pre\"), g(x, \"mid\"), g(x, \"last\"), false) && (g(x, \"s2\") !== \".\" || !/^\\.\\d/.test(after(x, 2))),\n mask: [\"mid\"],\n },\n { // 국제 표기: +82 10-1234-5678, +82-2-123-4567, +82 (0)10 1234 5678, +821012345678\n re: new RegExp(`(?<![\\\\w@+])(?<cc>\\\\+82)(?<s0>[-. ]?)(?:\\\\(0\\\\) ?)?(?<pre>1[016789]|2|3[1-3]|4[1-4]|5[1-5]|6[1-4]|70|80)(?<s1>${PHONE_SEP}?)(?<mid>\\\\d{3,4})(?<s2>${PHONE_SEP}?)(?<last>\\\\d{4})${PHONE_END}`, \"dg\"),\n ok: (x) => phoneSepsOk(x, g(x, \"s1\"), g(x, \"s2\")) && phoneDigitsOk(\"0\" + g(x, \"pre\"), g(x, \"mid\"), g(x, \"last\"), g(x, \"s1\") === \"\"),\n mask: [\"mid\"],\n },\n { // 대표번호 15xx·16xx·18xx — 구분자 필수. 연도 범위 모양(1592-1598)·온점/공백 구분은 전화 라벨이 있을 때만.\n // 뒤 4자리 0000 은 자리표시자\n re: new RegExp(`(?<![\\\\w@+-])(?<!\\\\d\\\\.)(?<pre>1(?:5[2-9]\\\\d|6\\\\d\\\\d|8\\\\d\\\\d))(?<sep> {0,2}- {0,2}| {1,2}|\\\\.)(?<last>\\\\d{4})${PHONE_END}`, \"dg\"),\n ok: (x) => {\n if (g(x, \"last\") === \"0000\" || serialBefore(x)) return false\n if (g(x, \"sep\") === \".\" && /^\\.\\d/.test(after(x, 2))) return false\n const a = Number(g(x, \"pre\"))\n const b = Number(g(x, \"last\"))\n // 뒤 그룹이 앞 그룹 이상이고 올해 이하면 연도 범위(재위·생몰·전쟁·시대) — 폭과 무관\n const yearRange = b >= a && b <= new Date().getFullYear()\n return (g(x, \"sep\").trim() === \"-\" && !yearRange) || has(x, \"phone\")\n },\n mask: [\"last\"],\n },\n ],\n // 운전면허번호 — 지역코드(11~28)-연도-일련6-검증2 / 구형 지역명 + 2-6-2. 뒤 8자리 마스크\n driver: [\n { // 신형 — 무문맥이지만 비개인 식별번호 문맥(\"(접수)\" 등)이면 아님\n re: /(?<![\\w-])(?<r>1[1-9]|2[0-8])-(?<y>\\d{2})-(?<s>\\d{6})-(?<c>\\d{2})(?![\\w-])/dg,\n ok: (x) => has(x, \"driver\") || !otherCtx(x, \"driver\"),\n mask: [\"s\", \"c\"],\n },\n {\n re: /(?<rn>서울|부산|경기|강원|충북|충남|전북|전남|경북|경남|제주|대구|인천|광주|대전|울산) ?(?<y>\\d{2})-(?<s>\\d{6})-(?<c>\\d{2})(?![\\w-])/dg,\n mask: [\"s\", \"c\"],\n },\n {\n re: /(?<![\\w@.-])(?<r>1[1-9]|2[0-8])(?<y>\\d{2})(?<s>\\d{6})(?<c>\\d{2})(?![\\w@-])/dg,\n needs: \"driver\",\n mask: [\"s\", \"c\"],\n },\n ],\n // 여권번호 — 신형 복수여권(M123A4567)은 무문맥, 그 밖의 종류 글자(S·R·O·D·G)와 구형(M12345678)은 여권 라벨\n // 필요 (\"S123A4567 부품\" 같은 제품 코드). 첫 글자만 남김\n passport: [\n {\n re: /(?<![A-Za-z0-9])(?<l>[MSRODG])(?<num>\\d{3}[A-Z]\\d{4})(?![A-Za-z0-9])/dg,\n ok: (x) => has(x, \"passport\") || (g(x, \"l\") === \"M\" && !otherCtx(x, \"passport\")),\n mask: [\"num\"],\n },\n {\n re: /(?<![A-Za-z0-9])(?<l>[MSRODG])(?<num>\\d{8})(?![A-Za-z0-9])/dg,\n needs: \"passport\",\n mask: [\"num\"],\n },\n { // 소문자로 친 여권번호(m123a4567) — 여권 라벨이 있을 때만\n re: /(?<![A-Za-z0-9])(?<l>[msrodg])(?<num>\\d{3}[a-z]\\d{4}|\\d{8})(?![A-Za-z0-9])/dg,\n needs: \"passport\",\n mask: [\"num\"],\n },\n ],\n // 계좌번호 — 하이픈 3~4그룹, 총 11~16자리 (10자리는 계좌 라벨 필요). 마지막 그룹 빼고 마스크.\n // 무구분 10~14자리는 계좌·은행 라벨이 있을 때만 (뒤 4자리 유지)\n account: [\n {\n // 마지막 그룹 1자리 허용 — 새마을금고 9002-1234-5678-9, 3-2-6-1·3-3-6-1 형식\n re: /(?<![\\w-])(?<!\\d\\.)(?<head>\\d{2,6}(?:-\\d{2,6}){1,2})-(?<last>\\d{1,8})(?![\\w-])(?!\\.\\d)/dg,\n ok: (x) => {\n const s = x.m[0]\n const d = digitsOf(s)\n const acct = has(x, \"account\")\n if (allSameDigit(d) || (!acct && otherCtx(x, \"account\"))) return false\n // 쉼표 숫자 목록 안(1,234-…)은 계좌 라벨이 있을 때만 (\"계좌 110-…,110-…\" 나열)\n if (!acct && (/\\d,$/.test(before(x)) || /^,\\d/.test(after(x, 2)))) return false\n if (/^\\d+(?:-0+)+$/.test(s)) return false // 010-0000-0000·000-00-00000 류 자리표시자\n if (d.length < 10 || d.length > 16 || (d.length === 10 && !acct)) return false\n if (/^\\s*원/.test(after(x, 3))) return false // 금액 표기 뒤 \"원\"\n if (acct) return true\n if (/^(?:10|20|30|40)-(?:19|20)\\d\\d-\\d{6,7}$/.test(s)) return false // 특허·상표·디자인 출원번호\n // 연도로 시작하는 날짜 부호 일련번호(2024-01-15-1234, 2024-0115-123456, 2023-10-123456)\n if (/^(?:19|20)\\d\\d-(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\\d|3[01])?-/.test(s)) return false\n if (s.split(\"-\").filter((p) => /^(?:19|20)\\d\\d$/.test(p)).length >= 2) return false // 연도 나열 2019-2020-2021\n if (/^\\d{3}-\\d{2}-\\d{5}$/.test(s) && brnChecksumValid(d)) return false // 사업자번호 몫\n return true\n },\n mask: [\"head\"],\n },\n {\n re: /(?<![\\w@.-])(?<!\\d,)(?<head>\\d{6,10})(?<last>\\d{4})(?![\\w@-])(?!,\\d)/dg,\n needs: \"account\",\n ok: (x) => !allSameDigit(x.m[0]),\n mask: [\"head\"],\n },\n ],\n // IPv4 (opt-in) — 옥텟 0~255, 첫 옥텟 10 이상(1.2.3.4 절 번호·버전 제외) 또는 IP 라벨. 뒤 두 옥텟 마스크\n ip: [\n {\n re: /(?<![\\w.])(?<a>\\d{1,3})\\.(?<b>\\d{1,3})\\.(?<c>\\d{1,3})\\.(?<d>\\d{1,3})(?![\\w])(?!\\.\\d)/dg,\n ok: (x) => {\n const oct = [g(x, \"a\"), g(x, \"b\"), g(x, \"c\"), g(x, \"d\")]\n if (oct.some((o) => Number(o) > 255 || (o.length > 1 && o[0] === \"0\"))) return false\n return Number(oct[0]) >= 10 || has(x, \"ip\")\n },\n mask: [\"c\", \"d\"],\n },\n ],\n // 주소·인명 (opt-in) — 사전 + 문맥 게이트, redact-name-address.ts\n address: ADDRESS_VARIANTS,\n name: NAME_VARIANTS,\n}\n","/**\n * 한국 공문서 PII(개인정보) 탐지·마스킹 순수 로직.\n *\n * 텍스트 in → 마스킹된 텍스트 + 히트 리포트 out. 문서 파일 단위 마스킹(머리말·각주·미리보기·메타데이터\n * 포함)은 redact-doc.ts 가 이 엔진을 불러 쓴다.\n *\n * 원칙:\n * - 서식 보존 마스킹 — 자릿수·구분자를 유지해 마스킹 전후 길이(UTF-16 단위)가 동일\n * - 히트 리포트에 원본 PII를 절대 담지 않는다 (`masked` 필드만 존재)\n * - 탐지는 같은 길이로 정규화한 그림자 문자열에서 한다 — 전각 숫자·영문·@, 유니코드 대시(‐‑–—−-·─·ㅡ),\n * NBSP·전각 공백을 ASCII 로 1:1 치환하므로 오프셋이 그대로이고, 마스킹은 원문 글자에 적용한다\n * - 번호 모양만으로 모호한 형태(구분자 없는 13자리·10~14자리, 여권 구형 등)는 바로 앞 라벨\n * (\"주민등록번호\", \"계좌\", \"여권\" …) 이나 표 열 머리글이 해당 유형일 때만 잡는다. 가장 가까운\n * 라벨이 이긴다 — \"법인등록번호\" 옆 6-7 번호는 주민번호로 보지 않는다\n * - 체크섬: 카드 Luhn, 사업자·법인등록번호 가중합. 주민·외국인등록번호는 2020-10 이후 뒷자리가\n * 임의 번호라 체크섬으로 거르지 않고 생년월일(세기 포함)·성별 자리만 검증한다\n * - 룰 우선순위 겹침 처리 — 우선순위순으로 매치를 수집하고, 이미 점유된 구간과 겹치는 하위 룰\n * 매치는 스킵 (RULE_PRIORITY 참조)\n * - 정규식은 모듈 로드 시 1회 컴파일, 탐지 루프가 lastIndex 를 0 으로 돌려 그대로 쓴다 (복제 비용 — detect 주석)\n *\n * 룰별 근거·오탐 실측은 bench/redact-bench.mjs (합성 정답 셋 + 실코퍼스) 참조.\n */\n\nimport { RULES, RULE_PRIORITY, labelsBefore, labelsIn, type Ctx, type Match, type Variant } from \"./redact-rules.js\"\n\nexport type RedactRule =\n | \"rrn\" | \"phone\" | \"email\" | \"card\" | \"account\" | \"passport\" | \"driver\" | \"brn\" | \"crn\" | \"ip\" | \"name\" | \"address\"\n\nexport interface RedactHit {\n rule: RedactRule\n /** 마스킹 후 문자열 — 원본 PII는 리포트에 담지 않는다 */\n masked: string\n /** 원문 내 시작 오프셋 (UTF-16 단위) */\n index: number\n /** 매치 길이 (서식 보존이라 마스킹 전후 동일) */\n length: number\n}\n\nexport interface RedactTextResult {\n text: string\n hits: RedactHit[]\n}\n\nexport interface RedactOptions {\n /** 적용할 룰 (기본: DEFAULT_REDACT_RULES — crn·ip는 기본 OFF) */\n rules?: readonly RedactRule[]\n /** 마스크 문자 — 1글자(UTF-16 1단위). 영숫자·공백·제어문자·마크다운/XML 특수문자(|\\<>&\"')는 금지. 기본 \"●\" */\n maskChar?: string\n}\n\n/**\n * 기본 적용 룰. 실코퍼스 2,303문서 오탐 실측 근거(bench/redact-bench.mjs --corpus):\n * - brn(사업자등록번호)은 종전 account 가 잡던 것을 체크섬으로 분리 — 기본 마스킹 범위 그대로\n * - passport 는 신형(M123A4567)만 무문맥, 구형(M12345678)은 \"여권\" 라벨이 있을 때만 → 기본 ON\n * - driver 는 지역코드(11~28)·지역명 검증으로 좁혔고 종전에도 account 로 가려지던 모양 → 기본 ON\n * - crn(법인등록번호)은 개인정보가 아니고, ip 는 \"1.2.3.4\" 절 번호·버전과 겹쳐 opt-in\n * - name(인명)·address(주소)는 사전 + 문맥 게이트라 번호형보다 경계가 흐리다 — opt-in (redact-name-address.ts)\n */\nexport const DEFAULT_REDACT_RULES: readonly RedactRule[] = [\n \"rrn\", \"phone\", \"email\", \"card\", \"account\", \"brn\", \"passport\", \"driver\",\n]\n\n/** 엔진이 아는 전체 룰 (CLI/MCP 입력 검증·벤치용) */\nexport const ALL_REDACT_RULES: readonly RedactRule[] = [...DEFAULT_REDACT_RULES, \"crn\", \"ip\", \"name\", \"address\"]\n\n// ─── 정규화 (같은 길이) ───────────────────────────────\n\n// 대시 모양 — 유니코드 하이픈·대시류 + 한글 문서에서 대시로 쓰는 괘선(─ ━)·한글 모음 ㅡ·장음 ー\nconst DASHES = new Set([0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2212, 0xfe58, 0xfe63, 0xff0d, 0x2500, 0x2501, 0x3161, 0x30fc])\nconst SPACES = new Set([0x00a0, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x202f, 0x3000])\n\n/** 탐지용 그림자 문자열 — 모든 치환이 UTF-16 1단위 → 1단위라 오프셋이 원문과 같다 */\nexport function normalizeForDetect(text: string): string {\n let out = \"\"\n let changed = false\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i)\n let r = c\n if (c >= 0xff01 && c <= 0xff5e) r = c - 0xfee0 // 전각 ASCII(!~~) → 반각\n if (DASHES.has(c)) r = 0x2d\n else if (SPACES.has(c)) r = 0x20\n if (r !== c) changed = true\n out += r === c ? text[i] : String.fromCharCode(r)\n }\n return changed ? out : text\n}\n\n/**\n * 스팬 원문(orig, 정규화 텍스트 start 부터)에 이름 그룹 범위를 maskChar 로 치환한 문자열 (길이 동일).\n * 글자·숫자 판정(한글 인명·주소 포함)은 정규화 텍스트로 해서 전각 숫자도 가린다. maskAll 이면 구분자까지 전부.\n */\nfunction maskMatch(orig: string, norm: string, start: number, x: Match, v: Variant, maskChar: string): string {\n const chars = orig.split(\"\")\n const idx = x.m.indices?.groups\n for (const name of v.mask) {\n const r = idx?.[name]\n if (!r) continue\n for (let i = r[0] - start; i < r[1] - start; i++) {\n if (v.maskAll || /[\\p{L}\\p{N}]/u.test(norm[start + i])) chars[i] = maskChar\n }\n }\n return chars.join(\"\")\n}\n\n/**\n * 마스크 문자 검증 — 1글자(UTF-16 1단위). 금지: 영숫자(전각 포함 — 정규화하면 숫자라 재탐지됨), 공백·제어·\n * 서식·결합 문자·비문자(U+FFFE/FFFF 등 \\\\p{Z}\\\\p{C}\\\\p{M}), 표·태그를 깨는 |\\\\<>&\"', 번호·주소 구분자\n * ._-@+:/,; (가린 결과가 다시 번호·이메일 모양이 되어 재검사 잔존으로 잡힌다)\n */\nfunction assertMaskChar(maskChar: string): void {\n const n = normalizeForDetect(maskChar)\n if (\n maskChar.length !== 1 || /[0-9A-Za-z|\\\\<>&\"'._\\-@+:/,;]/.test(n)\n || /[\\p{Z}\\p{C}\\p{M}]/u.test(maskChar) || /[\\uFDD0-\\uFDEF\\uFFFE\\uFFFF]/.test(maskChar)\n ) {\n throw new Error(`maskChar는 영숫자·공백·제어/결합 문자·구분 기호(|\\\\<>&\"'._-@+:/,;)가 아닌 보이는 1글자여야 함: ${JSON.stringify(maskChar)}`)\n }\n}\n\n/** 나열 구분자 — 앞 값과 이 값 사이가 이것뿐이면 같은 라벨 아래 목록이다 (\"계좌 110-…, 110-…\") */\nconst LIST_SEP_RE = /^\\s{0,2}(?:[,;/·、]|및|또는)?\\s{0,2}$/\n\n/**\n * 탐지 코어 — headerAt 은 오프셋 → 그 위치 표 열 머리글 텍스트 (redactMarkdown 이 표에서 제공).\n * 인라인 라벨이 없으면 (1) 바로 앞에 나열 구분자로 이어진 값이 있을 때 그 값의 라벨과 룰을 잇고,\n * (2) 그래도 없으면 머리글 라벨 집합을 쓴다.\n */\nfunction detect(\n text: string, rules: readonly RedactRule[], maskChar: string, headerAt?: (index: number) => string | undefined,\n): RedactHit[] {\n const norm = normalizeForDetect(text)\n const hits: RedactHit[] = []\n const occupied: Array<{ start: number; end: number; ctx: () => ReadonlySet<Ctx>; rule: RedactRule }> = []\n for (const rule of RULE_PRIORITY) {\n if (!rules.includes(rule)) continue\n for (const v of RULES[rule]) {\n // 모듈 정규식을 그대로 lastIndex 0 부터 exec — 복제(matchAll 내부 복제 포함)는 큰 정규식에서 한 번에 수~15µs 라\n // 24만 줄 문서(hwp5/big_file)에서 변형 하나에 0.2~3.8초였다. detect 는 재진입하지 않아 공유해도 안전.\n // 문맥어를 매치에 넣는 변형(span)은 버린 매치가 뒤 문맥을 삼키지 않게 값 자리부터 다시 찾는다\n // (\"담당 주무관 강민준\" — \"주무관\"을 이름에서 버린 뒤 \"주무관 강민준\"을 본다)\n const re = v.re\n re.lastIndex = 0\n for (let m = re.exec(norm); m !== null; m = re.exec(norm)) {\n const sg = v.span ? m.indices?.groups?.[v.span] : undefined\n const start = sg ? sg[0] : m.index\n const end = sg ? sg[1] : start + m[0].length\n const retry = (): void => { if (sg) re.lastIndex = Math.max(m!.index + 1, start) }\n if (occupied.some((o) => start < o.end && end > o.start)) { retry(); continue }\n let cached: ReadonlySet<Ctx> | undefined\n const ctx = (): ReadonlySet<Ctx> => {\n if (!cached) {\n let c: ReadonlySet<Ctx> = labelsBefore(norm, start)\n if (c.size === 0) {\n const prev = occupied.find((o) => o.end < start && start - o.end <= 6 && LIST_SEP_RE.test(norm.slice(o.end, start)))\n // 앞 값의 라벨 + 앞 값이 무엇이었는지 자체가 문맥 (주민번호 다음 무구분 13자리도 주민번호).\n // 인명·주소는 번호 룰 뒤에 돌아(RULE_PRIORITY) 여기서 앞 값으로 잡히지 않는다\n if (prev) c = prev.rule === \"email\" ? prev.ctx() : new Set<Ctx>([...prev.ctx(), prev.rule as Ctx])\n }\n if (c.size === 0 && headerAt) {\n const h = headerAt(start)\n if (h) c = labelsIn(normalizeForDetect(h))\n }\n cached = c\n }\n return cached\n }\n const x: Match = {\n m, ctx,\n header: () => headerAt?.(start),\n prev: () => {\n let best: (typeof occupied)[number] | undefined\n for (const o of occupied) if (o.end <= start && (!best || o.end > best.end)) best = o\n return best\n },\n }\n if ((v.needs && !ctx().has(v.needs)) || (v.ok && !v.ok(x))) { retry(); continue }\n occupied.push({ start, end, ctx, rule })\n hits.push({ rule, masked: maskMatch(text.slice(start, end), norm, start, x, v, maskChar), index: start, length: end - start })\n }\n }\n }\n return hits.sort((a, b) => a.index - b.index)\n}\n\nfunction applyHits(text: string, hits: readonly RedactHit[]): string {\n let out = \"\"\n let cursor = 0\n for (const h of hits) {\n out += text.slice(cursor, h.index) + h.masked\n cursor = h.index + h.length\n }\n return out + text.slice(cursor)\n}\n\n/**\n * 텍스트에서 PII를 탐지해 서식 보존 마스킹.\n *\n * @param text - 대상 텍스트 (마크다운 포함)\n * @param options - 룰 선택·마스크 문자 (기본: DEFAULT_REDACT_RULES, \"●\")\n * @returns 마스킹된 텍스트 + 히트 리포트 (index 오름차순, 원본 PII 미포함)\n */\nexport function redactText(text: string, options?: RedactOptions): RedactTextResult {\n const maskChar = options?.maskChar ?? \"●\"\n assertMaskChar(maskChar)\n const rules = options?.rules ?? DEFAULT_REDACT_RULES\n if (text === \"\" || rules.length === 0) return { text, hits: [] }\n const hits = detect(text, rules, maskChar)\n return { text: applyHits(text, hits), hits }\n}\n\n// ─── 마크다운 표 머리글 문맥 ──────────────────────────\n\n/** GFM 행의 셀 경계(이스케이프 안 된 파이프) 위치 */\nfunction pipePositions(line: string): number[] {\n const out: number[] = []\n for (let i = 0; i < line.length; i++) {\n if (line[i] === \"|\" && line[i - 1] !== \"\\\\\") out.push(i)\n }\n return out\n}\n\nconst isGfmRow = (l: string): boolean => /^\\s*\\|.*\\|\\s*$/.test(l)\nconst isGfmSep = (l: string): boolean => /^\\s*\\|(?:\\s*:?-{3,}:?\\s*\\|)+\\s*$/.test(l)\n\ninterface HtmlCell { start: number; end: number; col: number; span: number; text: string }\n\n/** HTML 표 행 한 줄의 셀 목록 (colspan 반영 열 번호) */\nfunction htmlCells(line: string): HtmlCell[] {\n const out: HtmlCell[] = []\n let col = 0\n for (const m of line.matchAll(/<t([dh])\\b([^>]*)>([\\s\\S]*?)<\\/t\\1>/gi)) {\n const span = Math.max(1, Number(m[2].match(/colspan=\"?(\\d+)/i)?.[1] ?? 1))\n out.push({ start: m.index as number, end: (m.index as number) + m[0].length, col, span, text: m[3].replace(/<[^>]*>/g, \" \") })\n col += span\n }\n return out\n}\n\n/**\n * 줄 번호 → (줄 내 오프셋 → 열 머리글 텍스트). GFM 표는 첫 행, HTML 표는 첫 <tr> 을 머리글로 본다\n * (병합·중첩 표에서는 근사 — 인라인 라벨이 없을 때만 쓰이는 보조 문맥).\n */\nfunction tableHeaderContext(lines: string[]): Map<number, (offset: number) => string | undefined> {\n const out = new Map<number, (offset: number) => string | undefined>()\n for (let i = 0; i < lines.length; i++) {\n // GFM\n if (isGfmRow(lines[i]) && i + 1 < lines.length && isGfmSep(lines[i + 1])) {\n const head = lines[i]\n const hp = pipePositions(head)\n const headers = hp.slice(0, -1).map((p, k) => head.slice(p + 1, hp[k + 1]))\n let j = i + 2\n for (; j < lines.length && isGfmRow(lines[j]); j++) {\n const rp = pipePositions(lines[j])\n out.set(j, (off) => {\n let k = -1\n for (const p of rp) { if (p < off) k++; else break }\n return k >= 0 ? headers[k] : undefined\n })\n }\n i = j - 1\n continue\n }\n // HTML — 표 시작 줄부터 </table> 까지, 첫 <tr> 이 머리글\n if (/<table\\b/i.test(lines[i])) {\n let headers: HtmlCell[] | null = null\n let depth = 0\n for (let j = i; j < lines.length; j++) {\n depth += (lines[j].match(/<table\\b/gi) ?? []).length\n if (/<tr\\b/i.test(lines[j]) && depth === 1) {\n const cells = htmlCells(lines[j])\n if (!headers) headers = cells\n else if (cells.length > 0) {\n const hdr = headers\n out.set(j, (off) => {\n const c = cells.find((x) => off >= x.start && off < x.end)\n if (!c) return undefined\n const h = hdr.find((x) => c.col >= x.col && c.col < x.col + x.span)\n return h?.text\n })\n }\n }\n depth -= (lines[j].match(/<\\/table>/gi) ?? []).length\n if (depth <= 0) { i = j; break }\n }\n }\n }\n return out\n}\n\n/**\n * 인라인 서식 표지 — 굵게·취소선·밑줄/HTML 강조 태그·백슬래시 이스케이프. 번호 한가운데 끼어도\n * (run 경계에서 굵게가 바뀐 \"010-98**76-5431**\", 이스케이프된 가림표 \"900101-1\\\\*\\\\*…\") 탐지되게\n * 지운 사본에서 찾고, 마스킹은 원문 글자 자리에만 입힌다 (표지는 그대로).\n * 항상 표지: ** __ ~~ (3개 이상 연속은 가림표일 수 있어 제외), 속성 없는 인라인 태그(href 속 값은 탐지\n * 대상으로 남긴다), 백슬래시 이스케이프.\n */\nconst MD_ALWAYS_MARKER_RE = new RegExp([\n \"(?<!\\\\*)\\\\*\\\\*(?!\\\\*)\", \"(?<!_)__(?!_)\", \"(?<!~)~~(?!~)\",\n \"<\\\\/?(?:u|b|i|s|em|strong|sup|sub|del|span|mark|small|big|ins|strike|tt|code|kbd|samp|var|q|cite|abbr|dfn)>\",\n \"\\\\\\\\(?=[\\\\\\\\`*_{}[\\\\]()#+\\\\-.!|~>])\",\n].join(\"|\"), \"g\")\n/**\n * 짝지어야 표지 — 홑 * · 홑 _ · *** 기울임은 단어 경계의 여는 표지와 닫는 표지가 한 줄 안에서 짝을 이룰 때만.\n * 짝 없는 것은 가림표(900101-1694***, 010-****-5678)일 수 있어 둔다.\n */\nconst MD_OPEN_RE = /(?<![\\w*\\\\])(\\*\\*\\*|\\*|_)(?=[0-9A-Za-z0-9])/g\nconst MD_CLOSE_RE = /(?<=[0-9A-Za-z0-9])(\\*\\*\\*|\\*|_)(?![\\w*])/g\n\n/** 마크다운 한 줄의 인라인 서식 표지 범위 [시작, 끝) — 오름차순 */\nexport function mdMarkerRanges(line: string): Array<[number, number]> {\n const out: Array<[number, number]> = []\n for (const m of line.matchAll(MD_ALWAYS_MARKER_RE)) out.push([m.index as number, (m.index as number) + m[0].length])\n const taken = (a: number, b: number): boolean => out.some(([s, e]) => a < e && b > s)\n const closes = [...line.matchAll(MD_CLOSE_RE)].filter((m) => !taken(m.index as number, (m.index as number) + m[0].length))\n let from = 0\n for (const o of line.matchAll(MD_OPEN_RE)) {\n const os = o.index as number\n const oe = os + o[0].length\n if (os < from || taken(os, oe)) continue\n const c = closes.find((m) => (m.index as number) >= oe && m[0] === o[0])\n if (!c) continue\n out.push([os, oe], [c.index as number, (c.index as number) + c[0].length])\n from = (c.index as number) + c[0].length\n }\n return out.sort((a, b) => a[0] - b[0])\n}\n\n/** 마크다운 한 줄 탐지 — 인라인 표지를 지운 사본에서 찾아 원문 오프셋·마스킹으로 되돌린다 */\nfunction detectMarkdownLine(\n line: string, rules: readonly RedactRule[], maskChar: string, headerAt?: (index: number) => string | undefined,\n): RedactHit[] {\n const markers = mdMarkerRanges(line)\n if (markers.length === 0) return detect(line, rules, maskChar, headerAt)\n let clean = \"\"\n const map: number[] = []\n let cursor = 0\n const keep = (to: number): void => {\n for (let i = cursor; i < to; i++) { clean += line[i]; map.push(i) }\n }\n for (const [ms, me] of markers) {\n keep(ms)\n cursor = me\n }\n keep(line.length)\n const hits = detect(clean, rules, maskChar, headerAt ? (i) => headerAt(map[i] ?? i) : undefined)\n return hits.map((h) => {\n const start = map[h.index]\n const end = map[h.index + h.length - 1] + 1\n const chars = line.slice(start, end).split(\"\")\n for (let k = 0; k < h.length; k++) {\n if (h.masked[k] !== clean[h.index + k]) chars[map[h.index + k] - start] = maskChar\n }\n return { rule: h.rule, masked: chars.join(\"\"), index: start, length: end - start }\n })\n}\n\n/**\n * 마크다운 문서 전용 래퍼 — base64 이미지(data URI) 라인은 마스킹에서 제외한다.\n * base64 페이로드의 숫자열이 phone 등에 오탐되면 이미지가 깨지기 때문.\n * 표 셀은 열 머리글(\"주민등록번호\"·\"계좌번호\" …)을 라벨 문맥으로 쓰고, 인라인 서식 표지(**·\\\\ 이스케이프 등)가\n * 번호를 갈라도 잡는다. hits의 index는 문서 전체 기준 절대 오프셋으로 환산된다 (masked 에는 표지가 그대로).\n */\nexport function redactMarkdown(markdown: string, options?: RedactOptions): RedactTextResult {\n const maskChar = options?.maskChar ?? \"●\"\n assertMaskChar(maskChar)\n const rules = options?.rules ?? DEFAULT_REDACT_RULES\n const lines = markdown.split(\"\\n\")\n const headers = tableHeaderContext(lines)\n const hits: RedactHit[] = []\n let offset = 0\n const outLines = lines.map((line, i) => {\n if (line.includes(\"data:image/\") || line === \"\" || rules.length === 0) {\n offset += line.length + 1\n return line\n }\n const lineHits = detectMarkdownLine(line, rules, maskChar, headers.get(i))\n for (const h of lineHits) hits.push({ ...h, index: h.index + offset })\n offset += line.length + 1\n return applyHits(line, lineHits)\n })\n return { text: outLines.join(\"\\n\"), hits }\n}\n"],"mappings":"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AAG;AAAA,EAAK,QAAG;AAAA,EAAM,QAAG;AAAA,EAAM,QAAG;AAAA,EAC7F,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAC3F,QAAG;AAAA,EAAM,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAC5F,QAAG;AAAA,EAAM,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAC5F,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAC3F,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAC3F,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAC3F,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA,EAAK,QAAG;AAAA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EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EACtF;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EACnF;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAClF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA;AAAA,EAErF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EACrF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA;AAAA,EAE3E;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACrF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC/E;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACpF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACrF;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACrF;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;A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","names":["g","span"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  KordocError
4
- } from "./chunk-UET462HB.js";
4
+ } from "./chunk-TITDJWVZ.js";
5
5
 
6
6
  // src/shared/offline.ts
7
7
  import { realpathSync } from "fs";
@@ -49,4 +49,4 @@ export {
49
49
  getAccessRoot,
50
50
  assertWithinRoot
51
51
  };
52
- //# sourceMappingURL=chunk-GDW4YP7K.js.map
52
+ //# sourceMappingURL=chunk-GLSBEEZJ.js.map
@@ -1,5 +1,5 @@
1
1
  // src/utils.ts
2
- var VERSION = true ? "4.14.2" : "0.0.0-dev";
2
+ var VERSION = true ? "4.14.4" : "0.0.0-dev";
3
3
  function toArrayBuffer(buf) {
4
4
  if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
5
5
  return buf.buffer;
@@ -121,4 +121,4 @@ export {
121
121
  safeMax,
122
122
  classifyError
123
123
  };
124
- //# sourceMappingURL=chunk-JQEFP45L.js.map
124
+ //# sourceMappingURL=chunk-GUJ4Q73I.js.map
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  parseRenderStyles
4
- } from "./chunk-ILU76UF3.js";
4
+ } from "./chunk-YAKC46HZ.js";
5
5
  import {
6
6
  KordocError,
7
7
  precheckZipSize
8
- } from "./chunk-UET462HB.js";
8
+ } from "./chunk-TITDJWVZ.js";
9
9
 
10
10
  // src/form/seal.ts
11
11
  import JSZip from "jszip";
@@ -1059,4 +1059,4 @@ export {
1059
1059
  patchZipEntries,
1060
1060
  placeSealHwpx
1061
1061
  };
1062
- //# sourceMappingURL=chunk-ZWOFQRIB.js.map
1062
+ //# sourceMappingURL=chunk-HIJUKZHM.js.map