w-web-perm 1.1.5 → 1.1.7

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 (42) hide show
  1. package/dist/css/{chunk-vendors.80aa3ae8.css → chunk-vendors.3c580e72.css} +1 -1
  2. package/dist/index.tmp +1 -1
  3. package/dist/js/app-legacy.6f82d8bd.js +1 -0
  4. package/dist/js/app.6f82d8bd.js +1 -0
  5. package/dist/js/{chunk-vendors.fbf771bd.js → chunk-vendors-legacy.00f159e8.js} +5 -5
  6. package/dist/js/{chunk-vendors-legacy.fbf771bd.js → chunk-vendors.00f159e8.js} +5 -5
  7. package/dist/w-web-perm.umd.js +2 -2
  8. package/dist/w-web-perm.umd.js.map +1 -1
  9. package/docs/WWebPerm.html +3 -3
  10. package/docs/global.html +474 -2
  11. package/docs/index.html +2 -2
  12. package/docs/server_WWebPerm.mjs.html +12 -3
  13. package/docs/server_lockSave.mjs.html +115 -0
  14. package/docs/server_maskLog.mjs.html +2 -2
  15. package/docs/server_procCore.mjs.html +31 -5
  16. package/docs/server_wrapInjected.mjs.html +2 -2
  17. package/docs/src_fetchJson.mjs.html +2 -2
  18. package/docs/src_js_legendFit.mjs.html +2 -2
  19. package/docs/src_plugins_reuseDeletedIds.mjs.html +136 -0
  20. package/docs/src_plugins_submitGuard.mjs.html +163 -0
  21. package/package.json +13 -13
  22. package/server/WWebPerm.mjs +10 -1
  23. package/server/lockSave.mjs +43 -0
  24. package/server/procCore.mjs +29 -3
  25. package/server/procLang.mjs +8 -0
  26. package/src/components/Common/CheckYes.vue +6 -0
  27. package/src/components/Common/CheckYesNo.vue +6 -0
  28. package/src/components/LayoutContentGrups.vue +30 -21
  29. package/src/components/LayoutContentPemis.vue +30 -21
  30. package/src/components/LayoutContentTargets.vue +36 -22
  31. package/src/components/LayoutContentUsers.vue +31 -21
  32. package/src/components/VeCgrups.vue +8 -1
  33. package/src/components/VeCpemis.vue +8 -1
  34. package/src/components/VeCrules.vue +8 -1
  35. package/src/components/VeGrupBlngUsers.vue +37 -31
  36. package/src/components/VePemiBlngGrups.vue +37 -31
  37. package/src/plugins/mUI.mjs +20 -0
  38. package/src/plugins/reuseDeletedIds.mjs +64 -0
  39. package/src/plugins/submitGuard.mjs +91 -0
  40. package/dist/js/app-legacy.833f9566.js +0 -1
  41. package/dist/js/app.833f9566.js +0 -1
  42. package/tableTags-web-perm.json +0 -1
@@ -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#createLegendMeasurer">createLegendMeasurer</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#wrapInjected">wrapInjected</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#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -80,7 +80,7 @@
80
80
 
81
81
  <dt class="tag-source">Source:</dt>
82
82
  <dd class="tag-source"><ul class="dummy"><li>
83
- <a href="server_WWebPerm.mjs.html">server/WWebPerm.mjs</a>, <a href="server_WWebPerm.mjs.html#line142">line 142</a>
83
+ <a href="server_WWebPerm.mjs.html">server/WWebPerm.mjs</a>, <a href="server_WWebPerm.mjs.html#line144">line 144</a>
84
84
  </li></ul></dd>
85
85
 
86
86
 
@@ -1045,7 +1045,7 @@
1045
1045
  <br class="clear">
1046
1046
 
1047
1047
  <footer>
1048
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep 27 2026 16:30:14 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1048
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 01:45:43 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1049
1049
  </footer>
1050
1050
 
1051
1051
  <script>prettyPrint();</script>
package/docs/global.html CHANGED
@@ -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#createLegendMeasurer">createLegendMeasurer</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#wrapInjected">wrapInjected</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#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -289,6 +289,170 @@
289
289
 
290
290
 
291
291
 
292
+ <h4 class="name" id="createLockSave"><span class="type-signature"></span>createLockSave<span class="signature">(cst)</span><span class="type-signature"> &rarr; {function}</span></h4>
293
+
294
+
295
+
296
+
297
+
298
+
299
+ <dl class="details">
300
+
301
+ <dt class="tag-description">Description:</dt>
302
+ <dd class="tag-description"><ul class="dummy"><li><p>有副作用之寫入路徑(如清單儲存 updateUsersList / updateTokensList / updateIpsList)之雙擊防護(後端, ADR-025)</p>
303
+ <p>以「操作:操作者 id」於 wsemi cacheSt 原子占位執行 fn, 同一操作者之同一操作處理中再送出即 reject 錯誤 key
304
+ (預設 'saveInProgress', 可以 opt.errKey 指定; 皆為 i18n key, 見 procLang); 不同操作者、不同操作互不影響;
305
+ fn 之 reject 原樣 bubble; fn 結束(含失敗)即釋放.
306
+ 第 2 次不排隊執行: 其內容與第 1 次相同, 排隊後再寫一次會把第 1 次剛建立之列當修改覆寫
307
+ (依序重送另由各專案之資料層檢查擋, 如 SSO procCore.updateTabItems 之新增列檢查回 'saveNewRowExists').
308
+ operatorId 可為操作者 id 或「操作者 id + 列識別」(只擋同一列之連發, 同批其他列不受影響).</p></li></ul></dd>
309
+
310
+
311
+
312
+ <dt class="tag-source">Source:</dt>
313
+ <dd class="tag-source"><ul class="dummy"><li>
314
+ <a href="server_lockSave.mjs.html">server/lockSave.mjs</a>, <a href="server_lockSave.mjs.html#line18">line 18</a>
315
+ </li></ul></dd>
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+ </dl>
348
+
349
+
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+
359
+
360
+
361
+ <h5 class="h5-parameters">Parameters:</h5>
362
+
363
+
364
+ <table class="params">
365
+ <thead>
366
+ <tr>
367
+
368
+ <th>Name</th>
369
+
370
+
371
+ <th>Type</th>
372
+
373
+
374
+
375
+
376
+
377
+ <th class="last">Description</th>
378
+ </tr>
379
+ </thead>
380
+
381
+ <tbody>
382
+
383
+
384
+ <tr>
385
+
386
+ <td class="name"><code>cst</code></td>
387
+
388
+
389
+ <td class="type">
390
+
391
+
392
+ <span class="param-type">Object</span>
393
+
394
+
395
+
396
+
397
+ </td>
398
+
399
+
400
+
401
+
402
+
403
+ <td class="description last"><p>輸入 wsemi cacheSt 實例</p></td>
404
+ </tr>
405
+
406
+
407
+ </tbody>
408
+ </table>
409
+
410
+
411
+
412
+
413
+
414
+
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+ <h5 class="h5-returns">Returns:</h5>
426
+
427
+
428
+ <div class="param-desc">
429
+ <p>回傳 async (op, operatorId, fn, opt = {}) =&gt; fn 之結果, opt.errKey 為占位衝突時 reject 之 key</p>
430
+ </div>
431
+
432
+
433
+
434
+ <dl class="param-type">
435
+ <dt>
436
+ Type
437
+ </dt>
438
+ <dd>
439
+
440
+ <span class="param-type">function</span>
441
+
442
+
443
+
444
+ </dd>
445
+ </dl>
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
292
456
  <h4 class="name" id="decideLegendType"><span class="type-signature"></span>decideLegendType<span class="signature">(m)</span><span class="type-signature"> &rarr; {String}</span></h4>
293
457
 
294
458
 
@@ -1742,6 +1906,314 @@ reject 之 message 只含例外名稱與代碼(如 'TypeError ECONNREFUSED'、'S
1742
1906
 
1743
1907
 
1744
1908
 
1909
+ <h4 class="name" id="reuseDeletedIds"><span class="type-signature"></span>reuseDeletedIds<span class="signature">(rows, rowsDb)</span><span class="type-signature"> &rarr; {Array}</span></h4>
1910
+
1911
+
1912
+
1913
+
1914
+
1915
+
1916
+ <dl class="details">
1917
+
1918
+ <dt class="tag-description">Description:</dt>
1919
+ <dd class="tag-description"><ul class="dummy"><li><p>新增列沿用資料庫既有 id 時之正規化 (標的清單專用, 其 id 為可就地改寫之標的路徑; ADR-025)</p>
1920
+ <p>同一次儲存中刪除(或把 id 改掉)某既有列, 又新增一列並把 id 改成該既有列之 id, 淨效果為「修改該列」:
1921
+ 該新增列改為一般列送出(_isNew 設 false), 建立者 / 建立時間沿用資料庫之值(修改者 / 修改時間由後端寫入).
1922
+ 否則後端之「新增列已存在即拒絕」(saveNewRowExists, 擋同一包重送) 會把它當重送擋下, 且重新整理後重做仍被擋;
1923
+ 亦避免以前端佔位字({待自動給予})覆寫建立者 / 建立時間.
1924
+ 只處理 id 於 rows 內唯一者; 重複 id 不處理, 由後端 ckKey 回 saveRowFieldDuplicate.
1925
+ 使用者 / 群組 / 權限清單之 id 為系統產生且不可編輯, 新增列不會與既有列同 id, 不需此處理.</p></li></ul></dd>
1926
+
1927
+
1928
+
1929
+ <dt class="tag-source">Source:</dt>
1930
+ <dd class="tag-source"><ul class="dummy"><li>
1931
+ <a href="src_plugins_reuseDeletedIds.mjs.html">src/plugins/reuseDeletedIds.mjs</a>, <a href="src_plugins_reuseDeletedIds.mjs.html#line20">line 20</a>
1932
+ </li></ul></dd>
1933
+
1934
+
1935
+
1936
+
1937
+
1938
+
1939
+
1940
+
1941
+
1942
+
1943
+
1944
+
1945
+
1946
+
1947
+
1948
+
1949
+
1950
+
1951
+
1952
+
1953
+
1954
+
1955
+
1956
+
1957
+
1958
+
1959
+
1960
+
1961
+
1962
+
1963
+
1964
+ </dl>
1965
+
1966
+
1967
+
1968
+
1969
+
1970
+
1971
+
1972
+
1973
+
1974
+
1975
+
1976
+
1977
+
1978
+ <h5 class="h5-parameters">Parameters:</h5>
1979
+
1980
+
1981
+ <table class="params">
1982
+ <thead>
1983
+ <tr>
1984
+
1985
+ <th>Name</th>
1986
+
1987
+
1988
+ <th>Type</th>
1989
+
1990
+
1991
+
1992
+
1993
+
1994
+ <th class="last">Description</th>
1995
+ </tr>
1996
+ </thead>
1997
+
1998
+ <tbody>
1999
+
2000
+
2001
+ <tr>
2002
+
2003
+ <td class="name"><code>rows</code></td>
2004
+
2005
+
2006
+ <td class="type">
2007
+
2008
+
2009
+ <span class="param-type">Array</span>
2010
+
2011
+
2012
+
2013
+
2014
+ </td>
2015
+
2016
+
2017
+
2018
+
2019
+
2020
+ <td class="description last"><p>輸入欲送出之列陣列(清單之 opt.rows), 不修改</p></td>
2021
+ </tr>
2022
+
2023
+
2024
+
2025
+ <tr>
2026
+
2027
+ <td class="name"><code>rowsDb</code></td>
2028
+
2029
+
2030
+ <td class="type">
2031
+
2032
+
2033
+ <span class="param-type">Array</span>
2034
+
2035
+
2036
+
2037
+
2038
+ </td>
2039
+
2040
+
2041
+
2042
+
2043
+
2044
+ <td class="description last"><p>輸入資料庫之列陣列(前端 store 之同表資料)</p></td>
2045
+ </tr>
2046
+
2047
+
2048
+ </tbody>
2049
+ </table>
2050
+
2051
+
2052
+
2053
+
2054
+
2055
+
2056
+
2057
+
2058
+
2059
+
2060
+
2061
+
2062
+
2063
+
2064
+
2065
+
2066
+ <h5 class="h5-returns">Returns:</h5>
2067
+
2068
+
2069
+ <div class="param-desc">
2070
+ <p>回傳正規化後之新陣列</p>
2071
+ </div>
2072
+
2073
+
2074
+
2075
+ <dl class="param-type">
2076
+ <dt>
2077
+ Type
2078
+ </dt>
2079
+ <dd>
2080
+
2081
+ <span class="param-type">Array</span>
2082
+
2083
+
2084
+
2085
+ </dd>
2086
+ </dl>
2087
+
2088
+
2089
+
2090
+
2091
+
2092
+
2093
+
2094
+
2095
+
2096
+
2097
+ <h4 class="name" id="submitGuard"><span class="type-signature"></span>submitGuard<span class="signature">()</span><span class="type-signature"> &rarr; {Object}</span></h4>
2098
+
2099
+
2100
+
2101
+
2102
+
2103
+
2104
+ <dl class="details">
2105
+
2106
+ <dt class="tag-description">Description:</dt>
2107
+ <dd class="tag-description"><ul class="dummy"><li><p>送出類操作之前端雙擊防護核心 (與 Vue / store 無關, 供單元測試; 由 mUI 之 runSubmit 與 updateLoading 使用, ADR-025)</p>
2108
+ <ul>
2109
+ <li>run(key, fn, opt): 同 key 之送出流程進行中(自觸發起至流程結束, 含結果訊息框開啟期間)再觸發即略過.
2110
+ 按鈕之滑鼠與鍵盤 Enter、輸入框之 Enter 等入口共用同一狀態.
2111
+ opt.pm 為 WButtonChip / WButtonCircle 之 promiseUnlock 鎖(click 事件之 msg.pm), 於 releaseBtnLocks()(全頁 loading 關閉, 即請求結束)
2112
+ 或流程結束時釋放; 被略過之觸發亦立即釋放其 pm, 否則該按鈕永久鎖住.
2113
+ fn 收到 unlock 函數: 流程需先開確認框者於開框前呼叫, 按鈕鎖只涵蓋請求期間(確認框背後之按鈕不顯示載入圖示), 重入仍由流程狀態擋.
2114
+ opt.hold 為函數, 輸入流程結果, 回傳 true 時保持占位與按鈕鎖(如登入成功轉址, 頁面即將離開).</li>
2115
+ <li>releaseBtnLocks(): 釋放進行中流程所登記之按鈕鎖, 流程狀態不變(結果訊息框開啟期間之重入仍擋).</li>
2116
+ <li>isRunning(key): 查詢 key 是否進行中.</li>
2117
+ </ul>
2118
+ <p>為何需要: 全頁 loading(WDialog)不搶焦點, 只擋滑鼠, 鍵盤事件仍送達背後之按鈕與輸入框;
2119
+ 按鈕鎖若於 handler 第一行即解除, 鍵盤連按照樣送出(2026-09-29 實測).</p></li></ul></dd>
2120
+
2121
+
2122
+
2123
+ <dt class="tag-source">Source:</dt>
2124
+ <dd class="tag-source"><ul class="dummy"><li>
2125
+ <a href="src_plugins_submitGuard.mjs.html">src/plugins/submitGuard.mjs</a>, <a href="src_plugins_submitGuard.mjs.html#line24">line 24</a>
2126
+ </li></ul></dd>
2127
+
2128
+
2129
+
2130
+
2131
+
2132
+
2133
+
2134
+
2135
+
2136
+
2137
+
2138
+
2139
+
2140
+
2141
+
2142
+
2143
+
2144
+
2145
+
2146
+
2147
+
2148
+
2149
+
2150
+
2151
+
2152
+
2153
+
2154
+
2155
+
2156
+
2157
+
2158
+ </dl>
2159
+
2160
+
2161
+
2162
+
2163
+
2164
+
2165
+
2166
+
2167
+
2168
+
2169
+
2170
+
2171
+
2172
+
2173
+
2174
+
2175
+
2176
+
2177
+
2178
+
2179
+
2180
+
2181
+
2182
+
2183
+
2184
+
2185
+
2186
+ <h5 class="h5-returns">Returns:</h5>
2187
+
2188
+
2189
+ <div class="param-desc">
2190
+ <p>回傳 { run, releaseBtnLocks, isRunning }</p>
2191
+ </div>
2192
+
2193
+
2194
+
2195
+ <dl class="param-type">
2196
+ <dt>
2197
+ Type
2198
+ </dt>
2199
+ <dd>
2200
+
2201
+ <span class="param-type">Object</span>
2202
+
2203
+
2204
+
2205
+ </dd>
2206
+ </dl>
2207
+
2208
+
2209
+
2210
+
2211
+
2212
+
2213
+
2214
+
2215
+
2216
+
1745
2217
  <h4 class="name" id="wrapInjected"><span class="type-signature"></span>wrapInjected<span class="signature">(fn, opt<span class="signature-attributes">opt</span>)</span><span class="type-signature"> &rarr; {function}</span></h4>
1746
2218
 
1747
2219
 
@@ -2082,7 +2554,7 @@ reject 之 message 只含例外名稱與代碼(如 'TypeError ECONNREFUSED'、'S
2082
2554
  <br class="clear">
2083
2555
 
2084
2556
  <footer>
2085
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep 27 2026 16:30:14 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
2557
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 01:45:43 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
2086
2558
  </footer>
2087
2559
 
2088
2560
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -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#createLegendMeasurer">createLegendMeasurer</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#wrapInjected">wrapInjected</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#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep 27 2026 16:30:14 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
74
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 01:45:43 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>
@@ -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#createLegendMeasurer">createLegendMeasurer</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#wrapInjected">wrapInjected</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#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</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#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</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#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -70,11 +70,13 @@ import replace from 'wsemi/src/replace.mjs'
70
70
  import strdelleft from 'wsemi/src/strdelleft.mjs'
71
71
  import ltdtmapping from 'wsemi/src/ltdtmapping.mjs'
72
72
  import pmKeyMutex from 'wsemi/src/pmKeyMutex.mjs'
73
+ import cacheSt from 'wsemi/src/cacheSt.mjs'
73
74
  import WServHapiServer from 'w-serv-hapi/src/WServHapiServer.mjs'
74
75
  import WServOrm from 'w-serv-orm/src/WServOrm.mjs'
75
76
  import ds from '../src/schema/index.mjs'
76
77
  import { getUserRules } from '../src/plugins/mShare.mjs'
77
78
  import procCore from './procCore.mjs'
79
+ import createLockSave from './lockSave.mjs'
78
80
  import procLang from './procLang.mjs'
79
81
  import srLogInit from './srLog.mjs'
80
82
  import procStaInfor from './procStaInfor.mjs'
@@ -367,8 +369,14 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
367
369
  let kmx = pmKeyMutex()
368
370
 
369
371
 
372
+ //lockSave: 後台整表寫入(updateTabItems)之雙擊防護(後端, 見 lockSave.mjs, ADR-025): 以「updateTabItems:{表名}:{操作者 id}」於 cacheSt 原子占位,
373
+ //同一操作者之同表寫入處理中再送出即 reject 'saveInProgress'(不排隊); 包在 kmx 之外層, 不同操作者之並行仍由 kmx 序列化(ADR-012)
374
+ let cst = cacheSt()
375
+ let lockSave = createLockSave(cst)
376
+
377
+
370
378
  //procCore: 整表批次 diff 寫入邏輯 (updateTabItems + updateTargets/Pemis/Grups/Users)
371
- let pc = procCore(woItems, procOrm, { srLog, kmx })
379
+ let pc = procCore(woItems, procOrm, { srLog, kmx, lockSave })
372
380
 
373
381
 
374
382
  //procStaInfor: 統計資訊, fdLog 對齊 srLog 使用同一目錄(讀 settings.logFd, fallback 邏輯同 srLog.mjs); mock 由 settings.staEventMock 注入(e2e 統計圖穩定用)
@@ -1124,6 +1132,7 @@ function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser
1124
1132
  console.log('apiType', apiType)
1125
1133
  console.log('authorization', maskTok(authorization))
1126
1134
  console.log('invalid token')
1135
+ srLog.error({ event: 'verifyConn-error', ok: false, apiType, msg: 'tokenNoPermission' }) //認證失敗須入 log 供稽核/統計(CLAUDE.md「log 記錄要點」; 同 w-web-api、w-web-task); 只記 key, 不記 authorization 原值
1127
1136
  return false
1128
1137
  }
1129
1138
 
@@ -1244,7 +1253,7 @@ export default WWebPerm
1244
1253
  <br class="clear">
1245
1254
 
1246
1255
  <footer>
1247
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Sep 27 2026 16:30:14 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1256
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 01:45:43 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1248
1257
  </footer>
1249
1258
 
1250
1259
  <script>prettyPrint();</script>