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
@@ -0,0 +1,258 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>src/js/getCascadePreview.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/getCascadePreview.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>import getPropKey from './getPropKey.mjs'
49
+ import { fmtWarn, getRefWarns } from './getAuthWarn.mjs'
50
+ import { REF_OF, planRefChanges } from './refCascade.mjs'
51
+
52
+
53
+ //清單頁「儲存」前之引用連帶預覽(規則帳本 R-PRINCIPLE-00 原則 6、適用表 m/n; R-REF-CASCADE-01; ADR-036):
54
+ //群組頁/權限頁改名 → 引用端(使用者 cgrups/群組 cpemis)於同一次寫入內連帶改名; 標的頁刪除或改 id → 引用它之權限 crules 項目被連帶清理(標的 id 即名稱, 改 id 等同刪舊增新, 不級聯).
55
+ //預覽與伺服端寫入用同一純函數 planRefChanges(src/js/refCascade.mjs), 前端以 store 之基準列計, 伺服端以實際為準.
56
+ //本檔只判定「要不要彈確認框」與「改名之成功訊息是否附帶筆數」並產生文字(刪除與標的頁之成功訊息不變), 不依賴 Vue/DOM; 元件只做薄接線(確認框、鎖釋放與 loading 仍在元件之 core()).
57
+ //
58
+ //kind: 'Grup'(群組頁, 引用端 users)、'Pemi'(權限頁, 引用端 grups)、'Target'(標的頁, 引用端 pemis)
59
+
60
+
61
+ let KIND = {
62
+ Grup: {
63
+ keyTable: 'grups',
64
+ rename: true,
65
+ confirm: 'confirmSaveRenameRefsGrup',
66
+ confirmExt: 'confirmSaveRenameRefsExtGrup',
67
+ success: 'grupSaveGrupsSuccessCascade',
68
+ },
69
+ Pemi: {
70
+ keyTable: 'pemis',
71
+ rename: true,
72
+ confirm: 'confirmSaveRenameRefsPemi',
73
+ confirmExt: 'confirmSaveRenameRefsExtPemi',
74
+ success: 'pemiSavePemisSuccessCascade',
75
+ },
76
+ Target: {
77
+ keyTable: 'targets',
78
+ rename: false,
79
+ confirm: 'confirmSaveDelRefsTarget',
80
+ confirmExt: '',
81
+ success: '', //標的頁不改成功訊息(刪除之筆數已於確認框告知)
82
+ },
83
+ }
84
+
85
+
86
+ let MAX_NAMES = 5
87
+
88
+
89
+ let hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k)
90
+ let isStr = (v) => typeof v === 'string'
91
+
92
+
93
+ //showName: 名稱之顯示形式, 空字串顯示為 "" (否則清單中看不到, 同 getRefWarns 之 label)
94
+ let showName = (v) => (v === '' ? '""' : String(v))
95
+
96
+
97
+ /**
98
+ * 把項目陣列以「, 」串接, 超過上限者截斷並加「共 N 個」之語系文字(cascadeListMore, {n} 為項目總數)
99
+ *
100
+ * @param {Array} items 輸入項目字串陣列
101
+ * @param {Function} $t 輸入語系函數(key → 文字)
102
+ * @param {Number} [max=5] 輸入最多列出幾項, 預設5
103
+ * @returns {String} 回傳字串, 如 'a, b' 或 'a, b, c, d, e …等 12 個'
104
+ */
105
+ function joinList(items, $t, max = MAX_NAMES) {
106
+ let s = items.slice(0, max).join(', ')
107
+ if (items.length > max) {
108
+ s = `${s} ${fmtWarn($t('cascadeListMore'), { n: items.length })}`
109
+ }
110
+ return s
111
+ }
112
+
113
+
114
+ //getHitKeys: 計畫內「有被改寫之引用端列」之原授權字串中出現之名稱鍵集合(field 為 'nRename' 取有改名者、'nDel' 取有刪除者);
115
+ //解碼經 getRefWarns(getAuthWarn.mjs 之 parseKp+cvKpToArr, 與 rewriteRefs 及伺服端同一套; 不另開 JSON5.parse 讀取點, 見 test/unit-cvKpToObj SITE-AD-001), 被引用表列給空陣列(只取 items 之名稱, 不查實體);
116
+ //名稱依 kind 取比對鍵('ref' 只認字串, 'rule' 取屬性鍵轉型, 同 rewriteRefs), 供名單只列「真的有人引用」者, 不列無人引用之改名/刪除
117
+ let getHitKeys = (plan, field) => {
118
+ let set = new Set()
119
+ let kind = hasOwn(REF_OF, plan.keyTable) ? REF_OF[plan.keyTable].kind : 'ref'
120
+ plan.changes.forEach((c) => {
121
+ if (!(c[field] > 0) || !isStr(c.before)) {
122
+ return
123
+ }
124
+ getRefWarns(c.before, []).items.forEach((it) => {
125
+ if (kind === 'ref' &amp;&amp; !isStr(it.name)) {
126
+ return
127
+ }
128
+ let key = getPropKey(it.name)
129
+ if (key !== null) {
130
+ set.add(key)
131
+ }
132
+ })
133
+ })
134
+ return set
135
+ }
136
+
137
+
138
+ /**
139
+ * 依引用連帶計畫產生儲存前確認框文字與儲存成功訊息(群組頁/權限頁/標的頁共用)
140
+ *
141
+ * 確認框(confirmText, 空字串不彈框):
142
+ * Grup/Pemi: plan.err 為 null、有改名(plan.renames)且有引用端列因改名被連帶更新(plan.nRenameHolders > 0)時, 列出受影響引用端數與「舊→新」(至多 5 個, 只列有人引用者, 其餘「…等 N 個」),
143
+ * 其中來源 from 非空(外部應用)之引用端另加一句警示(下次外部同步時可能被外部資料覆寫); 僅刪除所致之連帶清理已於「刪除」時確認(getDelImpact), 儲存時不再重複.
144
+ * Target: plan.err 為 null 且有權限之規則將被清理(plan.nDelHolders > 0)時, 列出受影響之權限數與被刪除或改 id 之管控對象(同上截斷).
145
+ * plan.err 非 null(伺服端將以固定語系鍵拒存)不彈框, 由呼叫端照常送出, 錯誤訊息由既有 showCheckYes 顯示.
146
+ * 成功訊息(successText, 空字串即使用原成功文字): 只有 Grup/Pemi 且 plan.err 為 null、有改名(plan.renames.length > 0)且有引用端因改名被連帶更新(plan.nRenameHolders > 0)時, 回傳附帶筆數(nRenameHolders)之「引用已更新為新名稱」訊息;
147
+ * 刪除所致之連帶清理(筆數已於刪除確認框告知, 且既有案例刪除被引用列後儲存之成功彈窗須維持原文字)與 Target 頁一律回空字串.
148
+ *
149
+ * @param {Object} plan 輸入 planRefChanges 之結果; null/非物件/keyTable 與 kind 不符視為無影響
150
+ * @param {Function} $t 輸入語系函數(key → 文字)
151
+ * @param {String} kind 輸入頁面種類: 'Grup'、'Pemi' 或 'Target'
152
+ * @returns {Object} 回傳 { confirmText, successText }
153
+ * @example
154
+ *
155
+ * let plan = planRefChanges({ keyTable: 'grups', rowsOld: [{ id: 'g1', name: 'A' }], rowsNew: [{ id: 'g1', name: 'B' }], holdersOld: [{ id: 'u1', cgrups: '{"A":{"mode":"OR","isActive":"y"}}' }] })
156
+ * console.log(getCascadePreview(plan, (k) => k, 'Grup').confirmText)
157
+ * // => confirmSaveRenameRefsGrup(語系函數為恆等時回傳鍵名)
158
+ *
159
+ */
160
+ function getCascadePreview(plan, $t, kind) {
161
+ let res = { confirmText: '', successText: '' }
162
+ if (!hasOwn(KIND, kind)) {
163
+ return res
164
+ }
165
+ let cfg = KIND[kind]
166
+ if (plan === null || typeof plan !== 'object' || plan.err !== null || plan.keyTable !== cfg.keyTable || !Array.isArray(plan.changes) || !Array.isArray(plan.renames)) {
167
+ return res
168
+ }
169
+
170
+ //confirmText, successText(successText 只描述改名, 條件同改名確認框, 筆數為 nRenameHolders 而非 nHolders)
171
+ if (cfg.rename) {
172
+ if (plan.renames.length > 0 &amp;&amp; plan.nRenameHolders > 0) {
173
+ res.successText = fmtWarn($t(cfg.success), { n: plan.nRenameHolders })
174
+ let hit = getHitKeys(plan, 'nRename')
175
+ let rns = plan.renames.filter((rn) => hit.has(rn.from))
176
+ if (rns.length === 0) {
177
+ rns = plan.renames //防呆: 名單判不出時退回全部改名, 不讓確認框無名單
178
+ }
179
+ let list = joinList(rns.map((rn) => `${showName(rn.from)}→${showName(rn.to)}`), $t)
180
+ let nExt = plan.changes.filter((c) => c.nRename > 0 &amp;&amp; isStr(c.from) &amp;&amp; c.from !== '').length
181
+ res.confirmText = fmtWarn($t(nExt > 0 ? cfg.confirmExt : cfg.confirm), { n: plan.nRenameHolders, e: nExt, list })
182
+ }
183
+ }
184
+ else if (plan.nDelHolders > 0) {
185
+ let hit = getHitKeys(plan, 'nDel')
186
+ let names = plan.delNames.filter((v) => hit.has(v))
187
+ if (names.length === 0) {
188
+ names = plan.delNames
189
+ }
190
+ let list = joinList(names.map((v) => showName(v)), $t)
191
+ res.confirmText = fmtWarn($t(cfg.confirm), { n: plan.nDelHolders, list })
192
+ }
193
+
194
+ return res
195
+ }
196
+
197
+
198
+ /**
199
+ * 儲存前預覽: 以與伺服端同一純函數規劃(planRefChanges), 再產生確認框文字與成功訊息; 預覽為輔助資訊, 計畫過程拋例外不得擋住儲存(回傳全空, 伺服端以實際為準)
200
+ *
201
+ * @param {Object} p 輸入參數物件
202
+ * @param {String} p.kind 輸入頁面種類: 'Grup'、'Pemi' 或 'Target'
203
+ * @param {Array} p.rowsOld 輸入基準列(store 之 grups/pemis/targets)
204
+ * @param {Array} p.rowsNew 輸入欲送出之列(清單目前之列)
205
+ * @param {Array} p.holdersOld 輸入引用端列(store 之 users/grups/pemis)
206
+ * @param {Boolean} [p.cascadeRename=true] 輸入伺服端是否做改名級聯(webInfor.cascadeRename), 僅給 false 才關閉, 預設true
207
+ * @param {Function} p.$t 輸入語系函數(key → 文字)
208
+ * @returns {Object} 回傳 { plan, confirmText, successText }; 發生例外時 plan 為 null
209
+ */
210
+ function getSavePreview(p) {
211
+ let kind = p ? p.kind : ''
212
+ if (!hasOwn(KIND, kind)) {
213
+ return { plan: null, confirmText: '', successText: '' }
214
+ }
215
+ try {
216
+ let plan = planRefChanges({
217
+ keyTable: KIND[kind].keyTable,
218
+ rowsOld: p.rowsOld,
219
+ rowsNew: p.rowsNew,
220
+ holdersOld: p.holdersOld,
221
+ opt: { cascadeRename: p.cascadeRename !== false },
222
+ })
223
+ return { plan, ...getCascadePreview(plan, p.$t, kind) }
224
+ }
225
+ catch (err) {
226
+ console.log('getSavePreview', err)
227
+ return { plan: null, confirmText: '', successText: '' }
228
+ }
229
+ }
230
+
231
+
232
+ export { getCascadePreview, getSavePreview, joinList }
233
+ export default getCascadePreview
234
+ </code></pre>
235
+ </article>
236
+ </section>
237
+
238
+
239
+
240
+
241
+
242
+
243
+ </div>
244
+
245
+ <br class="clear">
246
+
247
+ <footer>
248
+ 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.
249
+ </footer>
250
+
251
+ <script>prettyPrint();</script>
252
+ <script src="scripts/polyfill.js"></script>
253
+ <script src="scripts/linenumber.js"></script>
254
+
255
+
256
+
257
+ </body>
258
+ </html>
@@ -0,0 +1,210 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>src/js/getDelRefCount.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/getDelRefCount.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 { countAndRefs, fmtWarn } from './getAuthWarn.mjs'
50
+ import { planRefChanges } from './refCascade.mjs'
51
+
52
+
53
+ //群組頁/權限頁按「刪除」前, 計算刪除對引用端的影響, 並產生確認框文字(規則帳本 R-PRINCIPLE-00 c、d、原則 6、適用表 n; ADR-036):
54
+ // n: 刪除後, 引用被刪名稱之引用端列(任何 mode、含停用項目)將於同一次寫入內被連帶清理(自其授權字串移除該引用), 其數量以 planRefChanges 之 nDelHolders 計(與伺服端同一純函數, R-REF-CASCADE-01);
55
+ // c: 其中以 AND 引用且刪除後該名稱完全消失者 → 引用被移除後其 AND 限制解除, 授權可能變多(run05 前為「AND 項變懸空、授權被收回」, 與連帶清理互斥, 已改);
56
+ // d: 引用的名稱有多筆同名(歷史同名)而只刪其一 → 取各候選之交集, 候選變少則交集變寬, 其授權可能變多(任何 mode; 該名稱仍有實體, 引用不清理, 不計入 n).
57
+ //兩頁共用: 群組頁引用端為 users(欄位 cgrups)、權限頁引用端為 grups(欄位 cpemis). 純函數, 不依賴 Vue/DOM.
58
+
59
+
60
+ //KIND_OF: 被刪者種類 → 被刪表與引用欄位('Grup' 群組頁、'Pemi' 權限頁)
61
+ let KIND_OF = {
62
+ Grup: { keyTable: 'grups', field: 'cgrups' },
63
+ Pemi: { keyTable: 'pemis', field: 'cpemis' },
64
+ }
65
+
66
+
67
+ /**
68
+ * 計算刪除所選列對引用者授權的影響(其中 AND 與歷史同名兩項)
69
+ *
70
+ * 被刪名稱只取「基準(store 之已儲存列)中存在」之列的名稱: 尚未儲存的新增列(id 不在基準)刪除不影響他人;
71
+ * 以基準名稱為準(引用者所寫的是已儲存的名稱, 未儲存的改名不影響其引用). 「刪除後」以刪除後的剩餘列(含未儲存的新增與改名)判斷,
72
+ * 「刪除前」之同名筆數以基準列計.
73
+ *
74
+ * @param {Array} delIds 輸入被刪列的 id 陣列
75
+ * @param {Array} rowsBase 輸入基準列(store 之 grups 或 pemis)
76
+ * @param {Array} holders 輸入引用者列(store 之 users 或 grups)
77
+ * @param {String} field 輸入引用欄位名('cgrups' 或 'cpemis')
78
+ * @param {Array} rowsRemain 輸入刪除後剩餘之列(目前清單剔除被刪列)
79
+ * @returns {Object} 回傳 { n, nWiden }: n 為以 AND 引用且名稱完全消失之引用者數(其 AND 限制將隨引用被清理而解除), nWiden 為授權可能變多之引用者數(歷史同名只刪其一)
80
+ * @example
81
+ *
82
+ * let users = [{ cgrups: '{"G1":{"mode":"AND","isActive":"y"}}' }]
83
+ * console.log(getDelRefCount(['g1'], [{ id: 'g1', name: 'G1' }], users, 'cgrups', []))
84
+ * // => { n: 1, nWiden: 0 }
85
+ *
86
+ */
87
+ function getDelRefCount(delIds, rowsBase, holders, field, rowsRemain) {
88
+ let setIds = new Set(Array.isArray(delIds) ? delIds : [])
89
+ let delNames = []
90
+ eachOwn(rowsBase, (r) => {
91
+ if (r !== null &amp;&amp; typeof r === 'object' &amp;&amp; setIds.has(r.id)) {
92
+ delNames.push(r.name === undefined ? '' : r.name)
93
+ }
94
+ })
95
+ if (delNames.length === 0) {
96
+ return { n: 0, nWiden: 0 }
97
+ }
98
+ let res = countAndRefs(holders, field, delNames, rowsRemain, rowsBase)
99
+ return { n: res.n, nWiden: res.nWiden }
100
+ }
101
+
102
+
103
+ /**
104
+ * 計算刪除所選列對引用端之完整影響: 將被連帶清理(移除引用)之引用端列數、其中以 AND 引用者、歷史同名致授權可能變多者
105
+ *
106
+ * nRefs 以 planRefChanges 計(只算本次刪除: rowsOld 為基準列、rowsNew 為基準列剔除被刪 id 之列, 不含其他未儲存之編輯; holdersOld 為引用端列),
107
+ * 即伺服端寫入時實際會被改寫之引用端列數(任何 mode、含停用項目); nAnd/nWiden 沿用 getDelRefCount.
108
+ * nAnd 以 nRefs 為上限(AND 引用者本即屬被清理者之子集; 若「目前清單之未儲存改名」使兩者判準不一致, 不使文案出現「其中 M 位」大於總數).
109
+ *
110
+ * @param {Array} delIds 輸入被刪列的 id 陣列
111
+ * @param {Array} rowsBase 輸入基準列(store 之 grups 或 pemis)
112
+ * @param {Array} holders 輸入引用端列(store 之 users 或 grups)
113
+ * @param {String} kind 輸入被刪者種類: 'Grup'(群組頁)或 'Pemi'(權限頁); 其他值回傳全 0
114
+ * @param {Array} rowsRemain 輸入刪除後剩餘之列(目前清單剔除被刪列)
115
+ * @returns {Object} 回傳 { nRefs, nAnd, nWiden }
116
+ * @example
117
+ *
118
+ * let users = [{ id: 'u1', cgrups: '{"G1":{"mode":"AND","isActive":"y"}}' }, { id: 'u2', cgrups: '{"G1":{"mode":"OR","isActive":"n"}}' }]
119
+ * console.log(getDelImpact(['g1'], [{ id: 'g1', name: 'G1' }], users, 'Grup', []))
120
+ * // => { nRefs: 2, nAnd: 1, nWiden: 0 }
121
+ *
122
+ */
123
+ function getDelImpact(delIds, rowsBase, holders, kind, rowsRemain) {
124
+ let res = { nRefs: 0, nAnd: 0, nWiden: 0 }
125
+ if (!Object.prototype.hasOwnProperty.call(KIND_OF, kind)) {
126
+ return res
127
+ }
128
+ let ref = KIND_OF[kind]
129
+ let base = Array.isArray(rowsBase) ? rowsBase : []
130
+ let setIds = new Set(Array.isArray(delIds) ? delIds : [])
131
+ let rowsNew = base.filter((r) => !(r !== null &amp;&amp; typeof r === 'object' &amp;&amp; setIds.has(r.id)))
132
+ let plan = planRefChanges({
133
+ keyTable: ref.keyTable,
134
+ rowsOld: base,
135
+ rowsNew,
136
+ holdersOld: Array.isArray(holders) ? holders : [],
137
+ })
138
+ //名稱是否「仍有存續列」依伺服端之身分判準(refCascade.diffRefTargets): 只有「同 id 同名」之列算存續, 刪除後以同名新增之新 id 列或由他列改名而來者不繼承引用, 故不算(否則 nRefs 計其被清理而 nAnd 以為名稱仍在, 漏報 AND 限制解除)
139
+ let nameOf = (r) => (r.name === undefined ? '' : r.name)
140
+ let baseById = new Map()
141
+ base.forEach((r) => {
142
+ if (r !== null &amp;&amp; typeof r === 'object' &amp;&amp; typeof r.id === 'string') {
143
+ baseById.set(r.id, r)
144
+ }
145
+ })
146
+ let remainRetained = (Array.isArray(rowsRemain) ? rowsRemain : []).filter((r) => {
147
+ let b = (r !== null &amp;&amp; typeof r === 'object') ? baseById.get(r.id) : undefined
148
+ return b !== undefined &amp;&amp; nameOf(b) === nameOf(r)
149
+ })
150
+ let c = getDelRefCount(delIds, rowsBase, holders, ref.field, remainRetained)
151
+ res.nRefs = plan.nDelHolders
152
+ res.nAnd = Math.min(c.n, res.nRefs)
153
+ res.nWiden = c.nWiden
154
+ return res
155
+ }
156
+
157
+
158
+ /**
159
+ * 刪除確認框文字: 有引用端將被清理(nRefs)或授權可能變多(nWiden)任一大於 0 才有文字(單一框、單一問句); 皆為 0 回空字串(不彈框)
160
+ *
161
+ * 文案依 nRefs/nAnd/nWiden 組合選鍵(語系鍵尾碼 kind): 僅 nRefs → confirmDelRefs; nRefs+nAnd → confirmDelRefsAnd; nRefs+nWiden → confirmDelRefsDup;
162
+ * 三者皆有 → confirmDelRefsAndDup; 僅 nWiden → confirmDelDupRefs({n} 為 nWiden). nAnd 為 0 或 nRefs 為 0 時不提 AND(nAnd 以 nRefs 為上限).
163
+ *
164
+ * @param {Object} impact 輸入 getDelImpact 之結果 { nRefs, nAnd, nWiden }
165
+ * @param {Function} $t 輸入語系函數(key → 文字)
166
+ * @param {String} kind 輸入被刪者種類: 'Grup'(群組頁) 或 'Pemi'(權限頁), 組成語系鍵 confirmDelRefs/confirmDelRefsAnd/confirmDelRefsDup/confirmDelRefsAndDup/confirmDelDupRefs 之尾碼
167
+ * @returns {String} 回傳確認框文字, 無影響為空字串
168
+ */
169
+ function getDelConfirmText(impact, $t, kind) {
170
+ let n = impact &amp;&amp; impact.nRefs > 0 ? impact.nRefs : 0
171
+ let m = n > 0 &amp;&amp; impact &amp;&amp; impact.nAnd > 0 ? Math.min(impact.nAnd, n) : 0
172
+ let w = impact &amp;&amp; impact.nWiden > 0 ? impact.nWiden : 0
173
+ if (n > 0) {
174
+ let key = `confirmDelRefs${m > 0 ? 'And' : ''}${w > 0 ? 'Dup' : ''}${kind}`
175
+ return fmtWarn($t(key), { n, m, w })
176
+ }
177
+ if (w > 0) {
178
+ return fmtWarn($t(`confirmDelDupRefs${kind}`), { n: w })
179
+ }
180
+ return ''
181
+ }
182
+
183
+
184
+ export { getDelRefCount, getDelImpact, getDelConfirmText }
185
+ export default getDelRefCount
186
+ </code></pre>
187
+ </article>
188
+ </section>
189
+
190
+
191
+
192
+
193
+
194
+
195
+ </div>
196
+
197
+ <br class="clear">
198
+
199
+ <footer>
200
+ 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.
201
+ </footer>
202
+
203
+ <script>prettyPrint();</script>
204
+ <script src="scripts/polyfill.js"></script>
205
+ <script src="scripts/linenumber.js"></script>
206
+
207
+
208
+
209
+ </body>
210
+ </html>
@@ -0,0 +1,92 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>src/js/getEffMode.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/getEffMode.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>/**
49
+ * 取得合併模式之有效值: 'OR' 為 'OR', 其餘(缺、非字串、'or'、'XOR' 等無法判讀者)一律視為 'AND'
50
+ *
51
+ * 規則帳本 R-AUTH-MERGE-00 第 5 條、R-PRINCIPLE-00 e: 無法判讀時取最嚴之解讀. 略過該項會使 AND 限制消失而放寬, 故改視為 AND(mergeRules 中為「限制項」:只當交集項、不單獨當基底,該層無合法 mode 之啟用項時全 n,否則唯一項成基底而比修正前多給).
52
+ * 伺服端 mergeRules(src/plugins/mShare.mjs)、編輯器解碼(cvKpToObj)、無損表示判定(getKpLossy)與頁面提示(getAuthWarn)共用, 單一擁有者.
53
+ *
54
+ * @param {*} m 輸入 mode
55
+ * @returns {String} 回傳 'OR' 或 'AND'
56
+ * @example
57
+ *
58
+ * console.log(getEffMode('OR'), getEffMode('AND'), getEffMode('or'), getEffMode(undefined))
59
+ * // => OR AND AND AND
60
+ *
61
+ */
62
+ function getEffMode(m) {
63
+ return m === 'OR' ? 'OR' : 'AND'
64
+ }
65
+
66
+
67
+ export default getEffMode
68
+ </code></pre>
69
+ </article>
70
+ </section>
71
+
72
+
73
+
74
+
75
+
76
+
77
+ </div>
78
+
79
+ <br class="clear">
80
+
81
+ <footer>
82
+ 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.
83
+ </footer>
84
+
85
+ <script>prettyPrint();</script>
86
+ <script src="scripts/polyfill.js"></script>
87
+ <script src="scripts/linenumber.js"></script>
88
+
89
+
90
+
91
+ </body>
92
+ </html>