w-web-perm 1.1.12 → 1.1.13

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 (124) hide show
  1. package/README.md +158 -123
  2. package/dist/css/app.f09c7433.css +1 -0
  3. package/dist/index.tmp +1 -1
  4. package/dist/js/app-legacy.8af87052.js +1 -0
  5. package/dist/js/app.8af87052.js +1 -0
  6. package/dist/js/{chunk-vendors.3e9e3ef1.js → chunk-vendors-legacy.ece5a361.js} +1 -1
  7. package/dist/js/{chunk-vendors-legacy.3e9e3ef1.js → chunk-vendors.ece5a361.js} +1 -1
  8. package/dist/w-web-perm.umd.js +2 -2
  9. package/dist/w-web-perm.umd.js.map +1 -1
  10. package/docs/WWebPerm.html +84 -4
  11. package/docs/global.html +23023 -1125
  12. package/docs/index.html +2 -2
  13. package/docs/server_WWebPerm.mjs.html +232 -32
  14. package/docs/server_cascadeWrite.mjs.html +225 -0
  15. package/docs/server_dataChannel.mjs.html +2 -2
  16. package/docs/server_keyCheck.mjs.html +353 -0
  17. package/docs/server_lockSave.mjs.html +2 -2
  18. package/docs/server_maskLog.mjs.html +2 -2
  19. package/docs/server_procCore.mjs.html +180 -41
  20. package/docs/server_staLogs_resolveShellWorker.mjs.html +2 -2
  21. package/docs/server_syncKeep.mjs.html +311 -0
  22. package/docs/server_tools_authRefsAudit.mjs.html +247 -0
  23. package/docs/server_tools_authRefsClean.mjs.html +335 -0
  24. package/docs/server_tools_fromTools.mjs.html +797 -0
  25. package/docs/server_tools_isMain.mjs.html +103 -0
  26. package/docs/server_tools_nameDupReport.mjs.html +280 -0
  27. package/docs/server_validateRows.mjs.html +2 -2
  28. package/docs/server_wrapInjected.mjs.html +3 -3
  29. package/docs/src_fetchJson.mjs.html +2 -2
  30. package/docs/src_js_cvKpToArr.mjs.html +2 -2
  31. package/docs/src_js_cvKpToObj.mjs.html +44 -33
  32. package/docs/src_js_draftSync.mjs.html +142 -0
  33. package/docs/src_js_eachOwn.mjs.html +3 -3
  34. package/docs/src_js_editorRefRows.mjs.html +356 -0
  35. package/docs/src_js_externalRows.mjs.html +338 -0
  36. package/docs/src_js_externalUi.mjs.html +230 -0
  37. package/docs/src_js_findRefRows.mjs.html +107 -0
  38. package/docs/src_js_findTouchedDup.mjs.html +150 -0
  39. package/docs/src_js_fmtLossyMsg.mjs.html +2 -2
  40. package/docs/src_js_fromAudit.mjs.html +489 -0
  41. package/docs/src_js_getAddImpact.mjs.html +269 -0
  42. package/docs/src_js_getAuthWarn.mjs.html +463 -0
  43. package/docs/src_js_getCascadePreview.mjs.html +258 -0
  44. package/docs/src_js_getDelRefCount.mjs.html +210 -0
  45. package/docs/src_js_getEffMode.mjs.html +92 -0
  46. package/docs/src_js_getKpLossy.mjs.html +47 -52
  47. package/docs/src_js_getNameErrs.mjs.html +266 -0
  48. package/docs/src_js_getOwn.mjs.html +3 -3
  49. package/docs/src_js_getPropKey.mjs.html +109 -0
  50. package/docs/src_js_getRuleEnable.mjs.html +2 -2
  51. package/docs/src_js_isAuthMode.mjs.html +4 -4
  52. package/docs/src_js_joinNames.mjs.html +2 -2
  53. package/docs/src_js_legendFit.mjs.html +2 -2
  54. package/docs/src_js_pairRowsToSnapshot.mjs.html +156 -0
  55. package/docs/src_js_refAudit.mjs.html +359 -0
  56. package/docs/src_js_refCascade.mjs.html +572 -0
  57. package/docs/src_js_relDialogSend.mjs.html +199 -0
  58. package/docs/src_js_rowDigest.mjs.html +265 -0
  59. package/docs/src_plugins_dataReloadGuard.mjs.html +109 -0
  60. package/docs/src_plugins_reuseDeletedIds.mjs.html +2 -2
  61. package/docs/src_plugins_submitGuard.mjs.html +2 -2
  62. package/g_authRefsClean.mjs +79 -0
  63. package/g_fixFromType.mjs +92 -0
  64. package/g_initialData.mjs +3 -3
  65. package/g_purgeFrom.mjs +127 -0
  66. package/package.json +1 -1
  67. package/server/WWebPerm.mjs +230 -30
  68. package/server/cascadeWrite.mjs +153 -0
  69. package/server/keyCheck.mjs +281 -0
  70. package/server/procCore.mjs +178 -39
  71. package/server/procLang.mjs +270 -3
  72. package/server/syncKeep.mjs +239 -0
  73. package/server/tools/authRefsAudit.mjs +175 -0
  74. package/server/tools/authRefsClean.mjs +263 -0
  75. package/server/tools/fromTools.mjs +725 -0
  76. package/server/tools/isMain.mjs +31 -0
  77. package/server/tools/nameDupReport.mjs +208 -0
  78. package/server/wrapInjected.mjs +1 -1
  79. package/src/components/LayoutContentGrups.vue +396 -75
  80. package/src/components/LayoutContentPemis.vue +395 -75
  81. package/src/components/LayoutContentTargets.vue +261 -25
  82. package/src/components/LayoutContentUsers.vue +334 -97
  83. package/src/components/VeCgrups.vue +88 -129
  84. package/src/components/VeCpemis.vue +86 -129
  85. package/src/components/VeCrules.vue +15 -5
  86. package/src/components/VeGrupBlngUsers.vue +202 -89
  87. package/src/components/VePemiBlngGrups.vue +209 -99
  88. package/src/js/cvKpToObj.mjs +42 -31
  89. package/src/js/draftSync.mjs +70 -0
  90. package/src/js/eachOwn.mjs +1 -1
  91. package/src/js/editorRefRows.mjs +284 -0
  92. package/src/js/externalRows.mjs +266 -0
  93. package/src/js/externalUi.mjs +158 -0
  94. package/src/js/findRefRows.mjs +35 -0
  95. package/src/js/findTouchedDup.mjs +78 -0
  96. package/src/js/fromAudit.mjs +417 -0
  97. package/src/js/getAddImpact.mjs +197 -0
  98. package/src/js/getAuthWarn.mjs +391 -0
  99. package/src/js/getCascadePreview.mjs +186 -0
  100. package/src/js/getDelRefCount.mjs +138 -0
  101. package/src/js/getEffMode.mjs +20 -0
  102. package/src/js/getKpLossy.mjs +44 -49
  103. package/src/js/getNameErrs.mjs +194 -0
  104. package/src/js/getOwn.mjs +1 -1
  105. package/src/js/getPropKey.mjs +37 -0
  106. package/src/js/isAuthMode.mjs +2 -2
  107. package/src/js/pairRowsToSnapshot.mjs +84 -0
  108. package/src/js/refAudit.mjs +287 -0
  109. package/src/js/refCascade.mjs +500 -0
  110. package/src/js/relDialogSend.mjs +127 -0
  111. package/src/js/rowDigest.mjs +193 -0
  112. package/src/plugins/dataReloadGuard.mjs +37 -0
  113. package/src/plugins/mShare.mjs +144 -140
  114. package/src/plugins/mUI.mjs +3 -8
  115. package/src/schema/tables/users.mjs +152 -152
  116. package/dist/css/app.6918ec45.css +0 -1
  117. package/dist/js/app-legacy.7cb7e394.js +0 -1
  118. package/dist/js/app.7cb7e394.js +0 -1
  119. package/docs/src_js_getKeyOrderLost.mjs.html +0 -121
  120. package/docs/src_js_getKpWriteLost.mjs.html +0 -100
  121. package/docs/src_js_keepKeyOrder.mjs.html +0 -139
  122. package/src/js/getKeyOrderLost.mjs +0 -49
  123. package/src/js/getKpWriteLost.mjs +0 -28
  124. package/src/js/keepKeyOrder.mjs +0 -67
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getKeyOrderLost">getKeyOrderLost</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getKpWriteLost">getKpWriteLost</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepKeyOrder">keepKeyOrder</a></li><li><a href="global.html#keyOrderOf">keyOrderOf</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#applyPurgePlan">applyPurgePlan</a></li><li><a href="global.html#auditFrom">auditFrom</a></li><li><a href="global.html#auditRefs">auditRefs</a></li><li><a href="global.html#buildReport">buildReport</a></li><li><a href="global.html#buildRowsSend">buildRowsSend</a></li><li><a href="global.html#buildSyncRowsNew">buildSyncRowsNew</a></li><li><a href="global.html#checkBaseline">checkBaseline</a></li><li><a href="global.html#checkCascadePlan">checkCascadePlan</a></li><li><a href="global.html#checkDbNotInUse">checkDbNotInUse</a></li><li><a href="global.html#checkExternalWrite">checkExternalWrite</a></li><li><a href="global.html#checkMapKeyBatch">checkMapKeyBatch</a></li><li><a href="global.html#checkUsersWrite">checkUsersWrite</a></li><li><a href="global.html#cleanOutOf">cleanOutOf</a></li><li><a href="global.html#compareBaseline">compareBaseline</a></li><li><a href="global.html#countAndRefs">countAndRefs</a></li><li><a href="global.html#countExternalRows">countExternalRows</a></li><li><a href="global.html#countIdRenumber">countIdRenumber</a></li><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#diffRefTargets">diffRefTargets</a></li><li><a href="global.html#dropServerOnlyDiff">dropServerOnlyDiff</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fillIdlessIds">fillIdlessIds</a></li><li><a href="global.html#findDupIndex">findDupIndex</a></li><li><a href="global.html#findKeyOtherFrom">findKeyOtherFrom</a></li><li><a href="global.html#findRefRows">findRefRows</a></li><li><a href="global.html#findTouchedDup">findTouchedDup</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#fmtVars">fmtVars</a></li><li><a href="global.html#fmtWarn">fmtWarn</a></li><li><a href="global.html#formatFixReport">formatFixReport</a></li><li><a href="global.html#formatFromAudit">formatFromAudit</a></li><li><a href="global.html#formatPurgeReport">formatPurgeReport</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getAddConfirmText">getAddConfirmText</a></li><li><a href="global.html#getAddImpact">getAddImpact</a></li><li><a href="global.html#getBannerLines">getBannerLines</a></li><li><a href="global.html#getBaselineDigest">getBaselineDigest</a></li><li><a href="global.html#getCascadePreview">getCascadePreview</a></li><li><a href="global.html#getChangedIdx">getChangedIdx</a></li><li><a href="global.html#getDelConfirmText">getDelConfirmText</a></li><li><a href="global.html#getDelImpact">getDelImpact</a></li><li><a href="global.html#getDelRefCount">getDelRefCount</a></li><li><a href="global.html#getDigestFields">getDigestFields</a></li><li><a href="global.html#getDupRowsInconsistent">getDupRowsInconsistent</a></li><li><a href="global.html#getEffMode">getEffMode</a></li><li><a href="global.html#getExternalEdits">getExternalEdits</a></li><li><a href="global.html#getItemWarnText">getItemWarnText</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getLockFields">getLockFields</a></li><li><a href="global.html#getNameErrs">getNameErrs</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getPropKey">getPropKey</a></li><li><a href="global.html#getRawModeMap">getRawModeMap</a></li><li><a href="global.html#getRefRowsExtra">getRefRowsExtra</a></li><li><a href="global.html#getRefState">getRefState</a></li><li><a href="global.html#getRefWarnText">getRefWarnText</a></li><li><a href="global.html#getRefWarns">getRefWarns</a></li><li><a href="global.html#getRowIdxs">getRowIdxs</a></li><li><a href="global.html#getRowState">getRowState</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#getRuleWarnText">getRuleWarnText</a></li><li><a href="global.html#getRuleWarns">getRuleWarns</a></li><li><a href="global.html#getSavePreview">getSavePreview</a></li><li><a href="global.html#getSyncWarnLines">getSyncWarnLines</a></li><li><a href="global.html#hasDupKey">hasDupKey</a></li><li><a href="global.html#hasExternalDiff">hasExternalDiff</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#isBaselineStale">isBaselineStale</a></li><li><a href="global.html#isBaselineValid">isBaselineValid</a></li><li><a href="global.html#isDraftStale">isDraftStale</a></li><li><a href="global.html#isExternalRow">isExternalRow</a></li><li><a href="global.html#isJsonLossy">isJsonLossy</a></li><li><a href="global.html#isMain">isMain</a></li><li><a href="global.html#joinList">joinList</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepAssignByEntity">keepAssignByEntity</a></li><li><a href="global.html#keepAssignOnMissing">keepAssignOnMissing</a></li><li><a href="global.html#makeBaseline">makeBaseline</a></li><li><a href="global.html#makeRowStates">makeRowStates</a></li><li><a href="global.html#mapKeyOf">mapKeyOf</a></li><li><a href="global.html#mapNameKp">mapNameKp</a></li><li><a href="global.html#markDataReload">markDataReload</a></li><li><a href="global.html#markHasKey">markHasKey</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#pairRowsToSnapshot">pairRowsToSnapshot</a></li><li><a href="global.html#pickUniqueUser">pickUniqueUser</a></li><li><a href="global.html#planDraftSync">planDraftSync</a></li><li><a href="global.html#planFixFromType">planFixFromType</a></li><li><a href="global.html#planPurgeChain">planPurgeChain</a></li><li><a href="global.html#planPurgeFrom">planPurgeFrom</a></li><li><a href="global.html#planRefChanges">planRefChanges</a></li><li><a href="global.html#putRefEntry">putRefEntry</a></li><li><a href="global.html#putRowsToKp">putRowsToKp</a></li><li><a href="global.html#refsChanged">refsChanged</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#restoreExternalOrder">restoreExternalOrder</a></li><li><a href="global.html#restoreServerFields">restoreServerFields</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#rewriteRefs">rewriteRefs</a></li><li><a href="global.html#rowDigest">rowDigest</a></li><li><a href="global.html#runAuditFrom">runAuditFrom</a></li><li><a href="global.html#runClean">runClean</a></li><li><a href="global.html#runFixFromType">runFixFromType</a></li><li><a href="global.html#runPurgeFrom">runPurgeFrom</a></li><li><a href="global.html#splitCheckedRows">splitCheckedRows</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#tabWriteKey">tabWriteKey</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#withTabLocks">withTabLocks</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li><li><a href="global.html#writeWithCascade">writeWithCascade</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -45,24 +45,31 @@
45
45
 
46
46
  <section>
47
47
  <article>
48
- <pre class="prettyprint source linenums"><code>import isestr from 'wsemi/src/isestr.mjs'
49
- import eachOwn from './eachOwn.mjs'
48
+ <pre class="prettyprint source linenums"><code>import eachOwn from './eachOwn.mjs'
50
49
  import cvKpToArr from './cvKpToArr.mjs'
51
- import isAuthMode from './isAuthMode.mjs'
50
+ import getEffMode from './getEffMode.mjs'
52
51
 
53
52
 
54
53
  /**
55
54
  * 判定帶 mode 之授權資料(JSON5.parse(cgrups/cpemis) 之結果)是否含「編輯器無法以物件形式無損表示」之格, 回傳受影響之名稱
56
55
  *
57
- * 編輯器存檔一律寫物件形式 { 名稱: { mode, isActive } }(每個名稱只有一個 mode、鍵序為 JS 屬性序); 外部應用可直接寫入陣列形式, 其下列兩格物件形式無法無損表示,
56
+ * 編輯器存檔一律寫物件形式 { 名稱: { mode, isActive } }(每個名稱只有一個 mode); 外部應用可直接寫入陣列形式, 其下列兩格物件形式無法無損表示,
58
57
  * 硬寫即靜默改變授權(規則帳本 R-AUTH-LOSSY-01; 複審 R4), 編輯器須拒存並顯示固定訊息(authLossyBlocked), 庫內字串不動:
59
- * 1. 同名有效啟用元素同時為 OR 與 AND: 物件形式只能留一個 mode. 保留 AND 支者, 另有其他 AND 名稱時 AND 合併之起始值由 OR 聯集改為第一個 AND 項而增權(外審 sol 第三輪#2);
60
- * 保留 OR 支或停用者亦會減權或增權, 一律視為有損(此格若整組只有該名稱則碰巧相等, 仍從嚴判定).
61
- * 2. 有效 AND 名稱 ≥ 2 且其相對順序於伺服端序(陣列元素首次有效出現之序)與物件鍵序(整數式鍵先、遞增)不同:
62
- * mergeRules 在沒有有效 OR 項時以第一個 AND 項為 AND 合併之起始, 結果因之而異; 整數式名稱(如群組名 '1')在物件形式中恆排在前面, 陣列形式之順序無法保留.
63
- * 須比整個相對順序而非只比第一個: 查無實體(群組/權限表內沒有)之名稱會被伺服端 arrFinder 丟棄、編輯器存檔時亦不寫出, 丟棄後實際的第一個 AND 項可能是其後任一名稱(外審 sol#1/minimax#1).
64
- * 有效啟用元素: 名稱為非空字串、isActive 為 'y'、mode 為 OR/AND(同伺服端 arrFinder 以 === 比對名稱、mergeRules 跳過非 OR/AND 者).
65
- * 物件形式(編輯器自己寫出之形式、鍵唯一、鍵序即伺服端序)與其他輸入恆可無損表示, 回傳空陣列. 以原函式 getUserRules 為真理之窮舉驗證見 unit-cvKpToObj PR-001/PR-001b/PR-003
58
+ * - 同名啟用元素之有效模式同時為 OR 與 AND: 物件形式只能留一個 mode. 合併規則為「(所有 OR 聯集) ∩ (所有 AND)」(R-AUTH-MERGE-00), 同名同時為 OR 與 AND 時
59
+ * 該項既擴張聯集又收斂交集, 保留 AND 支會使聯集少了該項(減權), 保留 OR 支則少了一個交集條件(增權), 一律視為有損(此格若整組只有該名稱則碰巧相等, 仍從嚴判定).
60
+ * - 啟用元素之名稱不是字串(陣列元素無 name 等): 物件形式沒有對應的鍵. 該元素仍以懸空引用參與合併(內容全 n; AND 使結果為空, OR 使「無 OR 時以第一個 AND 為基底」失效,
61
+ * R-PRINCIPLE-00 c), 靜默丟棄即放寬, 故拒存(R-PRINCIPLE-00 f 之「無法原樣保留則拒存」; 空字串名稱則可原樣保留為物件鍵 '', 不在此格). 名稱以 `#&lt;序號>`(1 起算)表示.
62
+ *
63
+ * 不判定「該層啟用項皆 mode 缺或非法」(原第三格, ADR-035 決定 e; run06 使用者裁示放寬, ADR-035 Update 2026-10-22): mode 缺或非法之啟用項伺服端只當限制項
64
+ * (R-PRINCIPLE-00 e, 該層無任何 mode 合法之啟用項時結果全 n), 編輯器雖顯示為「AND、啟用」, 但存檔時對未被管理者改動者原樣寫回其原始 mode(editorRefRows 之
65
+ * getRawModeMap/putRowsToKp/putRefEntry, 方案 A; R-EDIT-KEEP-01), 故未改動時存檔前後授權相等、不會成為唯一 AND 基底而多給; 放寬前此格拒存只會使管理者無法存任何變更.
66
+ * 該項之 mode 缺漏提示仍由 getAuthWarn 之 modeInvalid 警告圖示承擔(不變). 管理者在對話框內明確改動(改為 OR/AND、啟用其他項)之結果, 等於伺服端對「改動後之狀態」之解讀,
67
+ * 未被改動之項不放寬(以真 getUserRules 窮舉證明見 unit-editorRows RT-LOSSY3-*).
68
+ *
69
+ * 不再判定「AND 名稱之相對順序」: 合併規則下結果與項目之儲存順序、物件鍵序、陣列元素序無關(R-AUTH-MERGE-00 第 4 條; ADR-033 取代 ADR-001 非對稱條),
70
+ * 整數式名稱被 JS 屬性序排前不再改變授權(原 run02 之排序保護 keepKeyOrder/getKeyOrderLost/getKpWriteLost 隨之移除).
71
+ * 啟用元素: isActive 為 'y' 者(mode 缺或非法者視為 AND 限制項, 見 getEffMode; 同伺服端 mergeRules).
72
+ * 編輯器自己寫出之物件形式(每個名稱有合法 mode)與其他輸入恆可無損表示, 回傳空陣列. 以原函式 getUserRules 為真理之窮舉驗證見 unit-cvKpToObj PR-001/PR-001b/PR-003
66
73
  * (凡存檔前後授權不相等者皆被本函數判定, 漏判 0; 本函數為保守側, 某些整體授權碰巧相等之格仍判有損).
67
74
  *
68
75
  * @param {Object|Array} kp 輸入 JSON5.parse(cgrups/cpemis) 之結果
@@ -71,64 +78,52 @@ import isAuthMode from './isAuthMode.mjs'
71
78
  *
72
79
  * console.log(getKpLossy([{ name: 'G1', mode: 'OR', isActive: 'y' }, { name: 'G1', mode: 'AND', isActive: 'y' }]))
73
80
  * // => ['G1']
81
+ * console.log(getKpLossy([{ name: 'G1', mode: 'OR', isActive: 'y' }, { name: 'G1', isActive: 'y' }]))
82
+ * // => ['G1'] (缺 mode 視為 AND)
83
+ * console.log(getKpLossy([{ mode: 'AND', isActive: 'y' }]))
84
+ * // => ['#1']
74
85
  * console.log(getKpLossy({ G1: { mode: 'OR', isActive: 'y' } }))
75
86
  * // => []
87
+ * console.log(getKpLossy({ G1: { isActive: 'y' } }))
88
+ * // => [] (該層啟用項皆 mode 缺漏: 原第三格, 已放寬)
76
89
  *
77
90
  */
78
91
  function getKpLossy(kp) {
79
- if (!Array.isArray(kp)) {
92
+ if (kp === null || typeof kp !== 'object') {
80
93
  return []
81
94
  }
82
95
 
83
- //seq: 有效啟用元素(依伺服端序)
84
- let seq = []
96
+ //kpModes: 啟用元素之名稱 → 出現過的有效 mode; unnamed: 啟用而名稱非字串之元素
97
+ let kpModes = Object.create(null)
98
+ let unnamed = []
99
+ let k = 0
85
100
  eachOwn(cvKpToArr(kp), (r) => {
86
- if (isestr(r.name) &amp;&amp; r.isActive === 'y' &amp;&amp; isAuthMode(r.mode)) {
87
- seq.push({ name: r.name, mode: r.mode })
101
+ k++
102
+ if (r.isActive !== 'y') {
103
+ return true //跳出換下一個
88
104
  }
89
- })
90
-
91
- let out = []
92
- let add = (n) => {
93
- if (!out.includes(n)) {
94
- out.push(n)
105
+ if (typeof r.name !== 'string') {
106
+ unnamed.push(`#${k}`)
107
+ return true //跳出換下一個
95
108
  }
96
- }
97
-
98
- //1) 同名同時有效 OR 與 AND
99
- let kpModes = Object.create(null)
100
- eachOwn(seq, (e) => {
101
- if (!(e.name in kpModes)) {
102
- kpModes[e.name] = []
109
+ let m = getEffMode(r.mode)
110
+ if (!(r.name in kpModes)) {
111
+ kpModes[r.name] = []
103
112
  }
104
- if (!kpModes[e.name].includes(e.mode)) {
105
- kpModes[e.name].push(e.mode)
113
+ if (!kpModes[r.name].includes(m)) {
114
+ kpModes[r.name].push(m)
106
115
  }
107
116
  })
117
+
118
+ //同名同時有效 OR 與 AND(僅陣列形式可能), 及啟用而無字串名稱者
119
+ let out = []
108
120
  eachOwn(kpModes, (ms, n) => {
109
121
  if (ms.length > 1) {
110
- add(n)
111
- }
112
- })
113
-
114
- //2) AND 名稱之相對順序於伺服端序與物件鍵序不同
115
- let andOrder = []
116
- eachOwn(seq, (e) => {
117
- if (e.mode === 'AND' &amp;&amp; !andOrder.includes(e.name)) {
118
- andOrder.push(e.name)
122
+ out.push(n)
119
123
  }
120
124
  })
121
- if (andOrder.length >= 2) {
122
- let probe = Object.create(null)
123
- eachOwn(andOrder, (n) => {
124
- probe[n] = true
125
- })
126
- if (Object.keys(probe).some((n, i) => n !== andOrder[i])) {
127
- eachOwn(andOrder, add)
128
- }
129
- }
130
125
 
131
- return out
126
+ return [...out, ...unnamed]
132
127
  }
133
128
 
134
129
 
@@ -147,7 +142,7 @@ export default getKpLossy
147
142
  <br class="clear">
148
143
 
149
144
  <footer>
150
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 21 2026 01:27:46 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
145
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri Oct 23 2026 10:56:39 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
151
146
  </footer>
152
147
 
153
148
  <script>prettyPrint();</script>
@@ -0,0 +1,266 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>src/js/getNameErrs.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#applyPurgePlan">applyPurgePlan</a></li><li><a href="global.html#auditFrom">auditFrom</a></li><li><a href="global.html#auditRefs">auditRefs</a></li><li><a href="global.html#buildReport">buildReport</a></li><li><a href="global.html#buildRowsSend">buildRowsSend</a></li><li><a href="global.html#buildSyncRowsNew">buildSyncRowsNew</a></li><li><a href="global.html#checkBaseline">checkBaseline</a></li><li><a href="global.html#checkCascadePlan">checkCascadePlan</a></li><li><a href="global.html#checkDbNotInUse">checkDbNotInUse</a></li><li><a href="global.html#checkExternalWrite">checkExternalWrite</a></li><li><a href="global.html#checkMapKeyBatch">checkMapKeyBatch</a></li><li><a href="global.html#checkUsersWrite">checkUsersWrite</a></li><li><a href="global.html#cleanOutOf">cleanOutOf</a></li><li><a href="global.html#compareBaseline">compareBaseline</a></li><li><a href="global.html#countAndRefs">countAndRefs</a></li><li><a href="global.html#countExternalRows">countExternalRows</a></li><li><a href="global.html#countIdRenumber">countIdRenumber</a></li><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#diffRefTargets">diffRefTargets</a></li><li><a href="global.html#dropServerOnlyDiff">dropServerOnlyDiff</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fillIdlessIds">fillIdlessIds</a></li><li><a href="global.html#findDupIndex">findDupIndex</a></li><li><a href="global.html#findKeyOtherFrom">findKeyOtherFrom</a></li><li><a href="global.html#findRefRows">findRefRows</a></li><li><a href="global.html#findTouchedDup">findTouchedDup</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#fmtVars">fmtVars</a></li><li><a href="global.html#fmtWarn">fmtWarn</a></li><li><a href="global.html#formatFixReport">formatFixReport</a></li><li><a href="global.html#formatFromAudit">formatFromAudit</a></li><li><a href="global.html#formatPurgeReport">formatPurgeReport</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getAddConfirmText">getAddConfirmText</a></li><li><a href="global.html#getAddImpact">getAddImpact</a></li><li><a href="global.html#getBannerLines">getBannerLines</a></li><li><a href="global.html#getBaselineDigest">getBaselineDigest</a></li><li><a href="global.html#getCascadePreview">getCascadePreview</a></li><li><a href="global.html#getChangedIdx">getChangedIdx</a></li><li><a href="global.html#getDelConfirmText">getDelConfirmText</a></li><li><a href="global.html#getDelImpact">getDelImpact</a></li><li><a href="global.html#getDelRefCount">getDelRefCount</a></li><li><a href="global.html#getDigestFields">getDigestFields</a></li><li><a href="global.html#getDupRowsInconsistent">getDupRowsInconsistent</a></li><li><a href="global.html#getEffMode">getEffMode</a></li><li><a href="global.html#getExternalEdits">getExternalEdits</a></li><li><a href="global.html#getItemWarnText">getItemWarnText</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getLockFields">getLockFields</a></li><li><a href="global.html#getNameErrs">getNameErrs</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getPropKey">getPropKey</a></li><li><a href="global.html#getRawModeMap">getRawModeMap</a></li><li><a href="global.html#getRefRowsExtra">getRefRowsExtra</a></li><li><a href="global.html#getRefState">getRefState</a></li><li><a href="global.html#getRefWarnText">getRefWarnText</a></li><li><a href="global.html#getRefWarns">getRefWarns</a></li><li><a href="global.html#getRowIdxs">getRowIdxs</a></li><li><a href="global.html#getRowState">getRowState</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#getRuleWarnText">getRuleWarnText</a></li><li><a href="global.html#getRuleWarns">getRuleWarns</a></li><li><a href="global.html#getSavePreview">getSavePreview</a></li><li><a href="global.html#getSyncWarnLines">getSyncWarnLines</a></li><li><a href="global.html#hasDupKey">hasDupKey</a></li><li><a href="global.html#hasExternalDiff">hasExternalDiff</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#isBaselineStale">isBaselineStale</a></li><li><a href="global.html#isBaselineValid">isBaselineValid</a></li><li><a href="global.html#isDraftStale">isDraftStale</a></li><li><a href="global.html#isExternalRow">isExternalRow</a></li><li><a href="global.html#isJsonLossy">isJsonLossy</a></li><li><a href="global.html#isMain">isMain</a></li><li><a href="global.html#joinList">joinList</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepAssignByEntity">keepAssignByEntity</a></li><li><a href="global.html#keepAssignOnMissing">keepAssignOnMissing</a></li><li><a href="global.html#makeBaseline">makeBaseline</a></li><li><a href="global.html#makeRowStates">makeRowStates</a></li><li><a href="global.html#mapKeyOf">mapKeyOf</a></li><li><a href="global.html#mapNameKp">mapNameKp</a></li><li><a href="global.html#markDataReload">markDataReload</a></li><li><a href="global.html#markHasKey">markHasKey</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#pairRowsToSnapshot">pairRowsToSnapshot</a></li><li><a href="global.html#pickUniqueUser">pickUniqueUser</a></li><li><a href="global.html#planDraftSync">planDraftSync</a></li><li><a href="global.html#planFixFromType">planFixFromType</a></li><li><a href="global.html#planPurgeChain">planPurgeChain</a></li><li><a href="global.html#planPurgeFrom">planPurgeFrom</a></li><li><a href="global.html#planRefChanges">planRefChanges</a></li><li><a href="global.html#putRefEntry">putRefEntry</a></li><li><a href="global.html#putRowsToKp">putRowsToKp</a></li><li><a href="global.html#refsChanged">refsChanged</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#restoreExternalOrder">restoreExternalOrder</a></li><li><a href="global.html#restoreServerFields">restoreServerFields</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#rewriteRefs">rewriteRefs</a></li><li><a href="global.html#rowDigest">rowDigest</a></li><li><a href="global.html#runAuditFrom">runAuditFrom</a></li><li><a href="global.html#runClean">runClean</a></li><li><a href="global.html#runFixFromType">runFixFromType</a></li><li><a href="global.html#runPurgeFrom">runPurgeFrom</a></li><li><a href="global.html#splitCheckedRows">splitCheckedRows</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#tabWriteKey">tabWriteKey</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#withTabLocks">withTabLocks</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li><li><a href="global.html#writeWithCascade">writeWithCascade</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">src/js/getNameErrs.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>import eachOwn from './eachOwn.mjs'
49
+ import findTouchedDup from './findTouchedDup.mjs'
50
+ import getPropKey from './getPropKey.mjs'
51
+ import { isExternalRow } from './externalRows.mjs'
52
+
53
+
54
+ //後台清單頁(群組/權限/使用者)「名稱(或 email)擋存」之判準, 單一擁有者(規則帳本 R-PRINCIPLE-00 g、h, R-NAME-UNIQ-02):
55
+ // 只擋「本次造成或觸及」者, 庫內既有的歷史問題(僅外部同步可造成)改為警告圖示而不擋存——否則已有歷史同名之庫在後台永遠存不了、無從修復.
56
+ // 重複之判準與伺服端同一份(findTouchedDup, server/keyCheck.mjs 亦轉出之), 故「前端判擋存 ⇔ 伺服端回 nameDuplicate」.
57
+ // 純函數, 不依賴 Vue/DOM/語系; 文字由呼叫端依 errBlock/warnHist/warnHistBad 之分類代入($t).
58
+ //
59
+ // 比對一律精確 ===; 以 Map 與無原型物件計, 免原型鍵名(constructor、__proto__)誤判(規則帳本 R-KEY-01).
60
+ // 基準 rowsOld = 最近一次由伺服端載入的列(store 之 grups/pemis/users 原始列); 新增但尚未儲存的列(id 不在基準內)視為本次造成.
61
+
62
+
63
+ /**
64
+ * 取得清單頁某欄(name/email)之擋存錯誤與歷史警告
65
+ *
66
+ * 每一列之該欄值分三類:
67
+ * - 壞值(empty): 不是字串或為空字串; 另 opt.isValid 給予時, 非空字串但不合格式者為 invalid(如 email 格式不符);
68
+ * - 好值之重複: 好值在 rowsNew 內出現 ≥ 2 次; 用 findTouchedDup 判斷是「本次造成或觸及」(dup, 擋存)還是「歷史既有原樣」(warnHist, 不擋存);
69
+ * - 其餘無事.
70
+ * 壞值預設一律擋存(opt.badScope 'all', 群組/權限之名稱: 伺服端 ckKey 對任一列之空/非字串名稱皆拒);
71
+ * opt.badScope 為 'touched' 時只擋「本次新增、或由其他值改成該壞值」之列, 基準內原樣就是壞值之列(歷史空值)不擋、改列入 warnHistBad(使用者之 name/email).
72
+ *
73
+ * 判定前先把兩邊列的有限數字 id 轉為字串(同伺服端), 非陣列視為空.
74
+ * 回傳之物件鍵為「儲存格之值」(非字串者依屬性鍵轉型, 空字串為 ''; 與清單頁以 props.value 查表一致), 皆為無原型物件.
75
+ *
76
+ * @param {Array} rowsNew 輸入目前清單之列(vo.opt.rows, 含未儲存之編修)
77
+ * @param {Array} rowsOld 輸入基準列(store 之原始列; 非陣列視為空)
78
+ * @param {String} [key='name'] 輸入欄位名
79
+ * @param {Object} [opt={}] 輸入設定物件
80
+ * @param {String} [opt.badScope='all'] 輸入壞值之擋存範圍, 'all' 或 'touched'
81
+ * @param {Function} [opt.isValid] 輸入好值格式檢查函數(值 → Boolean), 不給則任一非空字串皆合格
82
+ * @returns {Object} 回傳 { errBlock, warnHist, warnHistBad }:
83
+ * errBlock 值 → 'empty' | 'invalid' | 'dup'(會擋存)
84
+ * warnHist 值 → 目前共用該值之列數(歷史同名, 不擋存; 與 errBlock 互斥, 以擋存者為準)
85
+ * warnHistBad 值 → 'empty' | 'invalid'(歷史壞值, 不擋存; 僅 badScope 為 'touched' 時有)
86
+ * dupExternal 值 → 外部列之來源 from(僅 errBlock 為 'dup' 者, 且共用該值之列中有外部列〔from 為非空字串, externalRows.isExternalRow〕; 供提示改為「外部來源 X 已使用此名稱」, 否則無此鍵)
87
+ * @example
88
+ *
89
+ * let rowsOld = [{ id: '1', name: 'A' }, { id: '2', name: 'A' }]
90
+ * let r1 = getNameErrs(rowsOld, rowsOld, 'name')
91
+ * console.log(r1.errBlock.A, r1.warnHist.A)
92
+ * // => undefined 2
93
+ * let r2 = getNameErrs([...rowsOld, { id: '3', name: 'A' }], rowsOld, 'name')
94
+ * console.log(r2.errBlock.A, r2.warnHist.A)
95
+ * // => 'dup' undefined
96
+ *
97
+ */
98
+ function getNameErrs(rowsNew0, rowsOld0, key = 'name', opt = {}) {
99
+ let o = (opt !== null &amp;&amp; typeof opt === 'object') ? opt : {}
100
+ let touchedOnly = o.badScope === 'touched'
101
+ let isValid = typeof o.isValid === 'function' ? o.isValid : null
102
+
103
+ let errBlock = Object.create(null)
104
+ let warnHist = Object.create(null)
105
+ let warnHistBad = Object.create(null)
106
+ let dupExternal = Object.create(null)
107
+
108
+ let isRow = (r) => r !== null &amp;&amp; typeof r === 'object'
109
+
110
+ //id 正規化: 有限數字 id 轉為字串(同伺服端 procCore.updateTabItems 於 findTouchedDup 之前所做, R-ORM-04), 其餘不變; 非陣列視為空.
111
+ //why: 前端若直接以數字 id 比對庫內之字串 id 會認不得同一列, 把歷史同名誤判為本次造成而擋存(兩端不一致)
112
+ let normIds = (rows) => {
113
+ if (!Array.isArray(rows)) {
114
+ return []
115
+ }
116
+ return rows.map((r) => {
117
+ return (isRow(r) &amp;&amp; typeof r.id === 'number' &amp;&amp; Number.isFinite(r.id)) ? { ...r, id: String(r.id) } : r
118
+ })
119
+ }
120
+ let rowsNew = normIds(rowsNew0)
121
+ let rowsOld = normIds(rowsOld0)
122
+
123
+ //基準: id → 基準列該欄之原始值
124
+ let kpOldVal = new Map()
125
+ eachOwn(rowsOld, (r) => {
126
+ if (isRow(r) &amp;&amp; typeof r.id === 'string') {
127
+ kpOldVal.set(r.id, r[key])
128
+ }
129
+ })
130
+
131
+ //rowsNew: 壞值分類; 好值計次(Map: 值 → 列數)
132
+ let cnt = new Map()
133
+ eachOwn(rowsNew, (r) => {
134
+ if (!isRow(r)) {
135
+ return true //跳出換下一個
136
+ }
137
+ let raw = r[key]
138
+ let v = (raw === undefined) ? '' : raw //缺欄位視為空字串(同清單頁既有 get(v, 'name', ''))
139
+ let kind = null
140
+ if (typeof v !== 'string' || v.length === 0) {
141
+ kind = 'empty'
142
+ }
143
+ else if (isValid !== null &amp;&amp; !isValid(v)) {
144
+ kind = 'invalid'
145
+ }
146
+ if (kind === null) {
147
+ cnt.set(v, (cnt.get(v) || 0) + 1)
148
+ return true //跳出換下一個
149
+ }
150
+ let kc = getPropKey(v) //儲存格值轉屬性鍵, 轉不了者(如 { toString: null })無從在畫面標示, 略過
151
+ if (kc === null) {
152
+ return true //跳出換下一個
153
+ }
154
+ //本次造成或觸及: 新列(id 不在基準)、或該欄值與基準不同
155
+ let touched = !(typeof r.id === 'string' &amp;&amp; kpOldVal.has(r.id) &amp;&amp; kpOldVal.get(r.id) === raw)
156
+ if (!touchedOnly || touched) {
157
+ errBlock[kc] = kind
158
+ }
159
+ else {
160
+ warnHistBad[kc] = kind
161
+ }
162
+ })
163
+
164
+ //好值之重複: 同值各列與基準中同值各列交給 findTouchedDup(兩端同一份判準)
165
+ let dupVals = []
166
+ cnt.forEach((n, v) => {
167
+ if (n >= 2) {
168
+ dupVals.push(v)
169
+ }
170
+ })
171
+ if (dupVals.length > 0) {
172
+ let setDup = new Set(dupVals)
173
+ let kpNew = new Map()
174
+ let kpOld = new Map()
175
+ eachOwn(rowsNew, (r) => {
176
+ if (isRow(r) &amp;&amp; setDup.has(r[key])) {
177
+ if (!kpNew.has(r[key])) {
178
+ kpNew.set(r[key], [])
179
+ }
180
+ kpNew.get(r[key]).push(r)
181
+ }
182
+ })
183
+ eachOwn(rowsOld, (r) => {
184
+ if (isRow(r) &amp;&amp; setDup.has(r[key])) {
185
+ if (!kpOld.has(r[key])) {
186
+ kpOld.set(r[key], [])
187
+ }
188
+ kpOld.get(r[key]).push(r)
189
+ }
190
+ })
191
+ eachOwn(dupVals, (v) => {
192
+ let kc = getPropKey(v)
193
+ if (kc === null) {
194
+ return true //跳出換下一個
195
+ }
196
+ let k = findTouchedDup(kpNew.get(v), kpOld.get(v) || [], key)
197
+ if (k >= 0) {
198
+ errBlock[kc] = 'dup'
199
+ let rExt = kpNew.get(v).find((r) => isExternalRow(r)) //另一方(或其中一方)為外部列: 提示改為「外部來源 X 已使用此名稱」
200
+ if (rExt !== undefined) {
201
+ dupExternal[kc] = rExt.from
202
+ }
203
+ }
204
+ else {
205
+ warnHist[kc] = cnt.get(v)
206
+ }
207
+ })
208
+ }
209
+
210
+ //以擋存者為準
211
+ eachOwn(Object.keys(errBlock), (kc) => {
212
+ delete warnHist[kc]
213
+ delete warnHistBad[kc]
214
+ })
215
+
216
+ return {
217
+ errBlock,
218
+ warnHist,
219
+ warnHistBad,
220
+ dupExternal,
221
+ }
222
+ }
223
+
224
+
225
+ /**
226
+ * 把「值 → 分類(或筆數)」之無原型物件逐項轉為「值 → 文字」之無原型物件(清單頁以 props.value 查表), 供 computed 組 tooltip 文字用
227
+ *
228
+ * @param {Object} kp 輸入 getNameErrs 回傳之 errBlock/warnHist/warnHistBad
229
+ * @param {Function} fn 輸入轉換函數(分類或筆數, 值) → 文字
230
+ * @returns {Object} 回傳無原型物件
231
+ */
232
+ function mapNameKp(kp, fn) {
233
+ let out = Object.create(null)
234
+ eachOwn(kp, (v, k) => {
235
+ out[k] = fn(v, k)
236
+ })
237
+ return out
238
+ }
239
+
240
+
241
+ export { getNameErrs, mapNameKp }
242
+ </code></pre>
243
+ </article>
244
+ </section>
245
+
246
+
247
+
248
+
249
+
250
+
251
+ </div>
252
+
253
+ <br class="clear">
254
+
255
+ <footer>
256
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri Oct 23 2026 10:56:39 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
257
+ </footer>
258
+
259
+ <script>prettyPrint();</script>
260
+ <script src="scripts/polyfill.js"></script>
261
+ <script src="scripts/linenumber.js"></script>
262
+
263
+
264
+
265
+ </body>
266
+ </html>
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getKeyOrderLost">getKeyOrderLost</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getKpWriteLost">getKpWriteLost</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepKeyOrder">keepKeyOrder</a></li><li><a href="global.html#keyOrderOf">keyOrderOf</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#applyPurgePlan">applyPurgePlan</a></li><li><a href="global.html#auditFrom">auditFrom</a></li><li><a href="global.html#auditRefs">auditRefs</a></li><li><a href="global.html#buildReport">buildReport</a></li><li><a href="global.html#buildRowsSend">buildRowsSend</a></li><li><a href="global.html#buildSyncRowsNew">buildSyncRowsNew</a></li><li><a href="global.html#checkBaseline">checkBaseline</a></li><li><a href="global.html#checkCascadePlan">checkCascadePlan</a></li><li><a href="global.html#checkDbNotInUse">checkDbNotInUse</a></li><li><a href="global.html#checkExternalWrite">checkExternalWrite</a></li><li><a href="global.html#checkMapKeyBatch">checkMapKeyBatch</a></li><li><a href="global.html#checkUsersWrite">checkUsersWrite</a></li><li><a href="global.html#cleanOutOf">cleanOutOf</a></li><li><a href="global.html#compareBaseline">compareBaseline</a></li><li><a href="global.html#countAndRefs">countAndRefs</a></li><li><a href="global.html#countExternalRows">countExternalRows</a></li><li><a href="global.html#countIdRenumber">countIdRenumber</a></li><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#diffRefTargets">diffRefTargets</a></li><li><a href="global.html#dropServerOnlyDiff">dropServerOnlyDiff</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fillIdlessIds">fillIdlessIds</a></li><li><a href="global.html#findDupIndex">findDupIndex</a></li><li><a href="global.html#findKeyOtherFrom">findKeyOtherFrom</a></li><li><a href="global.html#findRefRows">findRefRows</a></li><li><a href="global.html#findTouchedDup">findTouchedDup</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#fmtVars">fmtVars</a></li><li><a href="global.html#fmtWarn">fmtWarn</a></li><li><a href="global.html#formatFixReport">formatFixReport</a></li><li><a href="global.html#formatFromAudit">formatFromAudit</a></li><li><a href="global.html#formatPurgeReport">formatPurgeReport</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getAddConfirmText">getAddConfirmText</a></li><li><a href="global.html#getAddImpact">getAddImpact</a></li><li><a href="global.html#getBannerLines">getBannerLines</a></li><li><a href="global.html#getBaselineDigest">getBaselineDigest</a></li><li><a href="global.html#getCascadePreview">getCascadePreview</a></li><li><a href="global.html#getChangedIdx">getChangedIdx</a></li><li><a href="global.html#getDelConfirmText">getDelConfirmText</a></li><li><a href="global.html#getDelImpact">getDelImpact</a></li><li><a href="global.html#getDelRefCount">getDelRefCount</a></li><li><a href="global.html#getDigestFields">getDigestFields</a></li><li><a href="global.html#getDupRowsInconsistent">getDupRowsInconsistent</a></li><li><a href="global.html#getEffMode">getEffMode</a></li><li><a href="global.html#getExternalEdits">getExternalEdits</a></li><li><a href="global.html#getItemWarnText">getItemWarnText</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getLockFields">getLockFields</a></li><li><a href="global.html#getNameErrs">getNameErrs</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getPropKey">getPropKey</a></li><li><a href="global.html#getRawModeMap">getRawModeMap</a></li><li><a href="global.html#getRefRowsExtra">getRefRowsExtra</a></li><li><a href="global.html#getRefState">getRefState</a></li><li><a href="global.html#getRefWarnText">getRefWarnText</a></li><li><a href="global.html#getRefWarns">getRefWarns</a></li><li><a href="global.html#getRowIdxs">getRowIdxs</a></li><li><a href="global.html#getRowState">getRowState</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#getRuleWarnText">getRuleWarnText</a></li><li><a href="global.html#getRuleWarns">getRuleWarns</a></li><li><a href="global.html#getSavePreview">getSavePreview</a></li><li><a href="global.html#getSyncWarnLines">getSyncWarnLines</a></li><li><a href="global.html#hasDupKey">hasDupKey</a></li><li><a href="global.html#hasExternalDiff">hasExternalDiff</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#isBaselineStale">isBaselineStale</a></li><li><a href="global.html#isBaselineValid">isBaselineValid</a></li><li><a href="global.html#isDraftStale">isDraftStale</a></li><li><a href="global.html#isExternalRow">isExternalRow</a></li><li><a href="global.html#isJsonLossy">isJsonLossy</a></li><li><a href="global.html#isMain">isMain</a></li><li><a href="global.html#joinList">joinList</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepAssignByEntity">keepAssignByEntity</a></li><li><a href="global.html#keepAssignOnMissing">keepAssignOnMissing</a></li><li><a href="global.html#makeBaseline">makeBaseline</a></li><li><a href="global.html#makeRowStates">makeRowStates</a></li><li><a href="global.html#mapKeyOf">mapKeyOf</a></li><li><a href="global.html#mapNameKp">mapNameKp</a></li><li><a href="global.html#markDataReload">markDataReload</a></li><li><a href="global.html#markHasKey">markHasKey</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#pairRowsToSnapshot">pairRowsToSnapshot</a></li><li><a href="global.html#pickUniqueUser">pickUniqueUser</a></li><li><a href="global.html#planDraftSync">planDraftSync</a></li><li><a href="global.html#planFixFromType">planFixFromType</a></li><li><a href="global.html#planPurgeChain">planPurgeChain</a></li><li><a href="global.html#planPurgeFrom">planPurgeFrom</a></li><li><a href="global.html#planRefChanges">planRefChanges</a></li><li><a href="global.html#putRefEntry">putRefEntry</a></li><li><a href="global.html#putRowsToKp">putRowsToKp</a></li><li><a href="global.html#refsChanged">refsChanged</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#restoreExternalOrder">restoreExternalOrder</a></li><li><a href="global.html#restoreServerFields">restoreServerFields</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#rewriteRefs">rewriteRefs</a></li><li><a href="global.html#rowDigest">rowDigest</a></li><li><a href="global.html#runAuditFrom">runAuditFrom</a></li><li><a href="global.html#runClean">runClean</a></li><li><a href="global.html#runFixFromType">runFixFromType</a></li><li><a href="global.html#runPurgeFrom">runPurgeFrom</a></li><li><a href="global.html#splitCheckedRows">splitCheckedRows</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#tabWriteKey">tabWriteKey</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#withTabLocks">withTabLocks</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li><li><a href="global.html#writeWithCascade">writeWithCascade</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -46,7 +46,7 @@
46
46
  <section>
47
47
  <article>
48
48
  <pre class="prettyprint source linenums"><code>//授權與資料歸屬之查值: 供權限評估(src/plugins/mShare.mjs)、外部應用 SDK(src/perm.mjs)、後台整表寫入(server/procCore.mjs)與編輯對話框(VeCgrups/VeCpemis/VeCrules)共用
49
- //規則見 CLAUDE_rulebook.md「迭代使用端傳入之物件」之 R-AUTH-PATH-01
49
+ //規則見 sgud/rule/rulebook.md「迭代使用端傳入之物件」之 R-AUTH-PATH-01
50
50
  //本檔無外部相依
51
51
 
52
52
 
@@ -97,7 +97,7 @@ export default getOwn
97
97
  <br class="clear">
98
98
 
99
99
  <footer>
100
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 21 2026 01:27:46 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
100
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri Oct 23 2026 10:56:39 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
101
101
  </footer>
102
102
 
103
103
  <script>prettyPrint();</script>