w-web-perm 1.1.11 → 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.
- package/README.md +158 -117
- package/dist/css/app.f09c7433.css +1 -0
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.8af87052.js +1 -0
- package/dist/js/app.8af87052.js +1 -0
- package/dist/js/{chunk-vendors.3e9e3ef1.js → chunk-vendors-legacy.ece5a361.js} +1 -1
- package/dist/js/{chunk-vendors-legacy.3e9e3ef1.js → chunk-vendors.ece5a361.js} +1 -1
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +84 -4
- package/docs/global.html +24747 -832
- package/docs/index.html +2 -2
- package/docs/server_WWebPerm.mjs.html +245 -32
- package/docs/server_cascadeWrite.mjs.html +225 -0
- package/docs/server_dataChannel.mjs.html +2 -2
- package/docs/server_keyCheck.mjs.html +353 -0
- package/docs/server_lockSave.mjs.html +2 -2
- package/docs/server_maskLog.mjs.html +2 -2
- package/docs/server_procCore.mjs.html +180 -41
- package/docs/server_staLogs_resolveShellWorker.mjs.html +2 -2
- package/docs/server_syncKeep.mjs.html +311 -0
- package/docs/server_tools_authRefsAudit.mjs.html +247 -0
- package/docs/server_tools_authRefsClean.mjs.html +335 -0
- package/docs/server_tools_fromTools.mjs.html +797 -0
- package/docs/server_tools_isMain.mjs.html +103 -0
- package/docs/server_tools_nameDupReport.mjs.html +280 -0
- package/docs/server_validateRows.mjs.html +15 -4
- package/docs/server_wrapInjected.mjs.html +3 -3
- package/docs/src_fetchJson.mjs.html +2 -2
- package/docs/src_js_cvKpToArr.mjs.html +125 -0
- package/docs/src_js_cvKpToObj.mjs.html +210 -0
- package/docs/src_js_draftSync.mjs.html +142 -0
- package/docs/src_js_eachOwn.mjs.html +3 -3
- package/docs/src_js_editorRefRows.mjs.html +356 -0
- package/docs/src_js_externalRows.mjs.html +338 -0
- package/docs/src_js_externalUi.mjs.html +230 -0
- package/docs/src_js_findRefRows.mjs.html +107 -0
- package/docs/src_js_findTouchedDup.mjs.html +150 -0
- package/docs/src_js_fmtLossyMsg.mjs.html +95 -0
- package/docs/src_js_fromAudit.mjs.html +489 -0
- package/docs/src_js_getAddImpact.mjs.html +269 -0
- package/docs/src_js_getAuthWarn.mjs.html +463 -0
- package/docs/src_js_getCascadePreview.mjs.html +258 -0
- package/docs/src_js_getDelRefCount.mjs.html +210 -0
- package/docs/src_js_getEffMode.mjs.html +92 -0
- package/docs/src_js_getKpLossy.mjs.html +155 -0
- package/docs/src_js_getNameErrs.mjs.html +266 -0
- package/docs/src_js_getOwn.mjs.html +3 -3
- package/docs/src_js_getPropKey.mjs.html +109 -0
- package/docs/src_js_getRuleEnable.mjs.html +2 -2
- package/docs/src_js_isAuthMode.mjs.html +92 -0
- package/docs/src_js_joinNames.mjs.html +96 -0
- package/docs/src_js_legendFit.mjs.html +2 -2
- package/docs/src_js_pairRowsToSnapshot.mjs.html +156 -0
- package/docs/src_js_refAudit.mjs.html +359 -0
- package/docs/src_js_refCascade.mjs.html +572 -0
- package/docs/src_js_relDialogSend.mjs.html +199 -0
- package/docs/src_js_rowDigest.mjs.html +265 -0
- package/docs/src_plugins_dataReloadGuard.mjs.html +109 -0
- package/docs/src_plugins_reuseDeletedIds.mjs.html +2 -2
- package/docs/src_plugins_submitGuard.mjs.html +2 -2
- package/g_authRefsClean.mjs +79 -0
- package/g_fixFromType.mjs +92 -0
- package/g_initialData.mjs +3 -3
- package/g_purgeFrom.mjs +127 -0
- package/package.json +1 -1
- package/server/WWebPerm.mjs +243 -30
- package/server/cascadeWrite.mjs +153 -0
- package/server/keyCheck.mjs +281 -0
- package/server/procCore.mjs +178 -39
- package/server/procLang.mjs +271 -0
- package/server/syncKeep.mjs +239 -0
- package/server/tools/authRefsAudit.mjs +175 -0
- package/server/tools/authRefsClean.mjs +263 -0
- package/server/tools/fromTools.mjs +725 -0
- package/server/tools/isMain.mjs +31 -0
- package/server/tools/nameDupReport.mjs +208 -0
- package/server/validateRows.mjs +13 -2
- package/server/wrapInjected.mjs +1 -1
- package/src/components/LayoutContentGrups.vue +399 -77
- package/src/components/LayoutContentPemis.vue +400 -78
- package/src/components/LayoutContentTargets.vue +261 -25
- package/src/components/LayoutContentUsers.vue +336 -98
- package/src/components/VeCgrups.vue +119 -115
- package/src/components/VeCpemis.vue +117 -115
- package/src/components/VeCrules.vue +18 -2
- package/src/components/VeGrupBlngUsers.vue +240 -73
- package/src/components/VePemiBlngGrups.vue +247 -83
- package/src/js/cvKpToArr.mjs +53 -0
- package/src/js/cvKpToObj.mjs +138 -0
- package/src/js/draftSync.mjs +70 -0
- package/src/js/eachOwn.mjs +1 -1
- package/src/js/editorRefRows.mjs +284 -0
- package/src/js/externalRows.mjs +266 -0
- package/src/js/externalUi.mjs +158 -0
- package/src/js/findRefRows.mjs +35 -0
- package/src/js/findTouchedDup.mjs +78 -0
- package/src/js/fmtLossyMsg.mjs +23 -0
- package/src/js/fromAudit.mjs +417 -0
- package/src/js/getAddImpact.mjs +197 -0
- package/src/js/getAuthWarn.mjs +391 -0
- package/src/js/getCascadePreview.mjs +186 -0
- package/src/js/getDelRefCount.mjs +138 -0
- package/src/js/getEffMode.mjs +20 -0
- package/src/js/getKpLossy.mjs +83 -0
- package/src/js/getNameErrs.mjs +194 -0
- package/src/js/getOwn.mjs +1 -1
- package/src/js/getPropKey.mjs +37 -0
- package/src/js/isAuthMode.mjs +20 -0
- package/src/js/joinNames.mjs +24 -0
- package/src/js/pairRowsToSnapshot.mjs +84 -0
- package/src/js/refAudit.mjs +287 -0
- package/src/js/refCascade.mjs +500 -0
- package/src/js/relDialogSend.mjs +127 -0
- package/src/js/rowDigest.mjs +193 -0
- package/src/plugins/dataReloadGuard.mjs +37 -0
- package/src/plugins/mShare.mjs +147 -174
- package/src/plugins/mUI.mjs +3 -8
- package/src/schema/tables/users.mjs +152 -152
- package/dist/css/app.6918ec45.css +0 -1
- package/dist/js/app-legacy.963e800c.js +0 -1
- package/dist/js/app.963e800c.js +0 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>src/js/relDialogSend.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/relDialogSend.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>//反向關聯對話框(VeGrupBlngUsers/VePemiBlngGrups)存檔時「送出之整表」之組裝(F-26 對話框部分、S4; R-PRINCIPLE-00 原則 3; 規則帳本 R-BASELINE-01): 純函數(不依賴 Vue/DOM), 兩個對話框共用.
|
|
49
|
+
//
|
|
50
|
+
//背景: 對話框之列是開框時 store 快照之投影(只含「歸屬」一欄可改). 修前存檔以「對話框之列」映射成整表送出, 開框期間他人/外部同步新增之列不在對話框內, 被伺服端整表 diff 刪除(S4);
|
|
51
|
+
//且對話框之列為開框時之草稿值, 若某列之歸屬於開框後被他人改過而使用者並未碰它, 以草稿值改寫即以舊值覆蓋他人之變更.
|
|
52
|
+
//修後: 送出之整表=存檔當下 store 快照之全部列; 使用者於本次編輯有變更之列(相對開框時之歸屬狀態)才以 putRefEntry 只改本群組/權限之一項(套用於存檔當下快照之字串);
|
|
53
|
+
//其餘一律原樣帶回(同一個物件、逐字不動). 注意(run11 起): 伺服端載入基準比對為表級(整表, R-BASELINE-01), 與「帶回哪個版本之列」無關——開框後他人若改了任何一列或增刪列, 本次存檔仍整次拒收 saveStale(請重新開啟); 帶回存檔當下 store 版本之列只是避免誤刪/誤改他人之列.
|
|
54
|
+
//本檔不解析授權字串(解析與 putRefEntry 留在 .vue 之 doSave, 站點由 unit-cvKpToObj SITE-AD-001 盤點), 只處理狀態比對與整表組裝.
|
|
55
|
+
|
|
56
|
+
import getEffMode from './getEffMode.mjs'
|
|
57
|
+
import { toId } from './pairRowsToSnapshot.mjs'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
let isObj = (v) => v !== null && typeof v === 'object'
|
|
61
|
+
let hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 取得對話框列之歸屬有效狀態(與 putRefEntry 之相等判定同一口徑: 不歸屬 → 'n'; 歸屬 → 'y:' 加有效 mode)
|
|
66
|
+
*
|
|
67
|
+
* 不歸屬時 mode 無授權效果(putRefEntry 對停用目標不看 mode), 故不納入; 歸屬時 mode 取 getEffMode(非 'OR' 者為 'AND').
|
|
68
|
+
*
|
|
69
|
+
* @param {*} enable 輸入列之 enable('y' 為歸屬, 其他為不歸屬)
|
|
70
|
+
* @param {*} mode 輸入列之 mode
|
|
71
|
+
* @returns {String} 回傳 'n'、'y:OR' 或 'y:AND'
|
|
72
|
+
* @example
|
|
73
|
+
*
|
|
74
|
+
* console.log(getRowState('n', 'AND'), getRowState('y', 'OR'), getRowState('y', 'or'))
|
|
75
|
+
* // => n y:OR y:AND
|
|
76
|
+
*
|
|
77
|
+
*/
|
|
78
|
+
function getRowState(enable, mode) {
|
|
79
|
+
return enable === 'y' ? `y:${getEffMode(mode)}` : 'n'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 由開框時生成之列(items)記錄各列之歸屬有效狀態(以列身分為鍵), 供存檔時判定「使用者於本次編輯有無變更該列」
|
|
85
|
+
*
|
|
86
|
+
* 須於 show() 生成 items 之同一處、尚未有任何切換操作前呼叫(opt.rows 與 items 為同一份陣列, 切換會就地改 enable/mode).
|
|
87
|
+
*
|
|
88
|
+
* @param {Array} rows 輸入開框時生成之列(各列帶 id、enable、mode); 非陣列視為無列
|
|
89
|
+
* @returns {Object} 回傳無原型物件 id(字串形式; 數字身分以其字串形式) → 'n'|'y:OR'|'y:AND'; 缺身分之列不記, 同身分重複者取第一筆
|
|
90
|
+
*/
|
|
91
|
+
function makeRowStates(rows) {
|
|
92
|
+
let kp = Object.create(null) //無原型: 鍵為使用者資料(R-KEY-01)
|
|
93
|
+
;(Array.isArray(rows) ? rows : []).forEach((r) => {
|
|
94
|
+
let id = isObj(r) ? toId(r.id) : null
|
|
95
|
+
if (id !== null && !(id in kp)) {
|
|
96
|
+
kp[id] = getRowState(r.enable, r.mode)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
return kp
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 找出對話框之列中「使用者於本次編輯相對開框時有變更」者之列索引
|
|
105
|
+
*
|
|
106
|
+
* 比對列目前之歸屬有效狀態與開框時記錄者(makeRowStates); 相同(含切換後又切回)視為未變更.
|
|
107
|
+
* 無法證明未變更者一律視為有變更(fail toward 既有流程: 對存檔當下快照字串套用 putRefEntry, 再由伺服端基準比對把關): 列缺身分、開框時無該列之記錄、未給記錄(null/非物件).
|
|
108
|
+
*
|
|
109
|
+
* @param {Array} rows 輸入對話框目前之列(opt.rows); 非陣列回傳空物件
|
|
110
|
+
* @param {Object} states 輸入 makeRowStates 之結果
|
|
111
|
+
* @returns {Object} 回傳無原型物件 列索引 → true(只含有變更者)
|
|
112
|
+
* @example
|
|
113
|
+
*
|
|
114
|
+
* let s = makeRowStates([{ id: 'u1', enable: 'n', mode: 'OR' }, { id: 'u2', enable: 'y', mode: 'OR' }])
|
|
115
|
+
* let ch = getChangedIdx([{ id: 'u1', enable: 'y', mode: 'OR' }, { id: 'u2', enable: 'y', mode: 'OR' }], s)
|
|
116
|
+
* console.log(Object.keys(ch))
|
|
117
|
+
* // => ['0']
|
|
118
|
+
*
|
|
119
|
+
*/
|
|
120
|
+
function getChangedIdx(rows, states) {
|
|
121
|
+
let kp = Object.create(null)
|
|
122
|
+
if (!Array.isArray(rows)) {
|
|
123
|
+
return kp
|
|
124
|
+
}
|
|
125
|
+
for (let kr = 0; kr < rows.length; kr++) { //索引迴圈: 稀疏陣列之空洞視為無法證明未變更
|
|
126
|
+
let r = rows[kr]
|
|
127
|
+
let id = isObj(r) ? toId(r.id) : null
|
|
128
|
+
let st0 = (id !== null && isObj(states) && hasOwn(states, id)) ? states[id] : undefined
|
|
129
|
+
if (st0 === undefined || !isObj(r) || st0 !== getRowState(r.enable, r.mode)) {
|
|
130
|
+
kp[kr] = true
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return kp
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 組裝送出之整表: 存檔當下 store 快照之全部列(依快照列序), 僅「有新值」之列改其引用欄位, 其餘原樣帶回
|
|
139
|
+
*
|
|
140
|
+
* 不改輸入; 未被改之列即快照中同一個物件(逐字不動, 含開框後他人新增而不在對話框內之列、使用者未變更之列).
|
|
141
|
+
* 被改之列為快照列之淺拷貝加新值(只換 refKey 一欄, 其餘欄位逐字保留).
|
|
142
|
+
*
|
|
143
|
+
* @param {Array} snapshot 輸入存檔當下之 store 快照(依 order 排序之全部列); 非陣列回傳空陣列
|
|
144
|
+
* @param {Array} pairs 輸入 pairRowsToSnapshot 之 pairs(對話框第 kr 列配到之快照列, 同一個物件參考; 配不到者為 null)
|
|
145
|
+
* @param {Object} newVals 輸入列索引 → 該列新字串(只放使用者有變更且確需改寫者)
|
|
146
|
+
* @param {String} refKey 輸入引用欄位名('cgrups'|'cpemis')
|
|
147
|
+
* @returns {Array} 回傳送出之整表(與 snapshot 等長同序)
|
|
148
|
+
* @example
|
|
149
|
+
*
|
|
150
|
+
* let snapshot = [{ id: 'u1', cgrups: '{}' }, { id: 'u2', cgrups: '{"G":{}}' }, { id: 'u3', cgrups: '{}' }]
|
|
151
|
+
* let rows = buildRowsSend(snapshot, [snapshot[0], snapshot[1]], { 0: '{"G1":{"mode":"OR","isActive":"y"}}' }, 'cgrups')
|
|
152
|
+
* console.log(rows.map((r) => r.cgrups), rows[1] === snapshot[1], rows[2] === snapshot[2])
|
|
153
|
+
* // => [ '{"G1":{"mode":"OR","isActive":"y"}}', '{"G":{}}', '{}' ] true true
|
|
154
|
+
*
|
|
155
|
+
*/
|
|
156
|
+
function buildRowsSend(snapshot, pairs, newVals, refKey) {
|
|
157
|
+
if (!Array.isArray(snapshot)) {
|
|
158
|
+
return []
|
|
159
|
+
}
|
|
160
|
+
let kpNew = new Map() //快照列(物件參考) → 新字串; 同一快照列被多個對話框列配到時取第一個(對話框每個身分只有一列, 防禦用)
|
|
161
|
+
if (Array.isArray(pairs) && isObj(newVals)) {
|
|
162
|
+
for (let kr = 0; kr < pairs.length; kr++) {
|
|
163
|
+
let u = pairs[kr]
|
|
164
|
+
if (isObj(u) && hasOwn(newVals, kr) && typeof newVals[kr] === 'string' && !kpNew.has(u)) {
|
|
165
|
+
kpNew.set(u, newVals[kr])
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return snapshot.map((row) => (kpNew.has(row) ? { ...row, [refKey]: kpNew.get(row) } : row))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
export { getRowState, makeRowStates, getChangedIdx, buildRowsSend }
|
|
174
|
+
export default buildRowsSend
|
|
175
|
+
</code></pre>
|
|
176
|
+
</article>
|
|
177
|
+
</section>
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<br class="clear">
|
|
187
|
+
|
|
188
|
+
<footer>
|
|
189
|
+
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.
|
|
190
|
+
</footer>
|
|
191
|
+
|
|
192
|
+
<script>prettyPrint();</script>
|
|
193
|
+
<script src="scripts/polyfill.js"></script>
|
|
194
|
+
<script src="scripts/linenumber.js"></script>
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
</body>
|
|
199
|
+
</html>
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>src/js/rowDigest.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/rowDigest.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>//列內容指紋與載入基準(F-26 過期快照保護; R-PRINCIPLE-00 原則 3「遇衝突或歧義:收嚴+提示」; 規則帳本 R-BASELINE-01; ADR-037): 純函數(不依賴 Vue/DOM/ORM), 前端與伺服端同一份.
|
|
49
|
+
//
|
|
50
|
+
//基準(baseline)=「編輯草稿所依據之那一版資料」之逐列內容指紋 [[id, digest], ...]; 於**開啟編輯(建立草稿)時**由草稿所依據之那份整表計算並綁定,
|
|
51
|
+
//之後即使資料推播使 store 變新, 舊草稿仍帶舊基準(外審 B.1: 存檔當下才取基準, 證明的是「store 新」而不是「待存之編輯內容新」).
|
|
52
|
+
//伺服端於整表寫入之互斥鎖內以庫內整表現況比對(表級, R-PRINCIPLE-00 原則 7, 使用者 2026-10-22 裁示): 表內任何一列之內容與載入時不同, 或列被增刪, 即整次拒收 saveStale、零寫入, 提示重新載入;
|
|
53
|
+
//不部分變更、不上鎖, 先動先贏(run07 版只比對「本次將寫入之列」, run11 改為整表).
|
|
54
|
+
//指紋只含內容欄位(排除 order 與伺服器管理欄 userId/timeCreate/userIdUpdate/timeUpdate): 外部同步整來源重寫使每列之建立/更新時間皆變, 內容未變者不應使草稿作廢; order 由儲存時依列序重給
|
|
55
|
+
//(外部列之 order 由外部擁有), 僅 order 不同不是內容變更.
|
|
56
|
+
//
|
|
57
|
+
//未帶基準之舊客戶端: 後台列維持舊行為(最後寫入者勝, ADR-013); 但**修改外部列之指派欄**無從證明快照新舊(Lv1 放行之指派欄不得因舊快照還原外部已撤銷之授權, 外審 B.2), 依原則 3 收嚴拒收(saveBaselineRequired).
|
|
58
|
+
|
|
59
|
+
import { SERVER_FIELDS } from './externalRows.mjs'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
let isStr = (v) => typeof v === 'string'
|
|
63
|
+
let isObj = (v) => v !== null && typeof v === 'object'
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
//cyrb53: 53 位元非密碼學雜湊(純 JS, 前後端皆可用; 用途為一致性比對, 非安全邊界)
|
|
67
|
+
function cyrb53(str, seed = 0) {
|
|
68
|
+
let h1 = 0xdeadbeef ^ seed
|
|
69
|
+
let h2 = 0x41c6ce57 ^ seed
|
|
70
|
+
for (let i = 0; i < str.length; i++) {
|
|
71
|
+
let ch = str.charCodeAt(i)
|
|
72
|
+
h1 = Math.imul(h1 ^ ch, 2654435761)
|
|
73
|
+
h2 = Math.imul(h2 ^ ch, 1597334677)
|
|
74
|
+
}
|
|
75
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909)
|
|
76
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909)
|
|
77
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 取得列內容指紋所含欄位: schema 全部欄位扣除 order 與伺服器管理欄
|
|
83
|
+
*
|
|
84
|
+
* @param {Array} keys 輸入該表 schema 欄位名陣列(ds[keyTable].keys)
|
|
85
|
+
* @returns {Array} 回傳欄位名陣列
|
|
86
|
+
*/
|
|
87
|
+
function getDigestFields(keys) {
|
|
88
|
+
return (Array.isArray(keys) ? keys : []).filter((k) => k !== 'order' && SERVER_FIELDS.indexOf(k) < 0)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 計算單列內容指紋(欄位依 schema 次序; 缺欄視為 null; 與列內其他鍵、鍵序無關)
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} row 輸入列物件
|
|
96
|
+
* @param {Array} keys 輸入該表 schema 欄位名陣列
|
|
97
|
+
* @returns {String} 回傳指紋字串
|
|
98
|
+
*/
|
|
99
|
+
function rowDigest(row, keys) {
|
|
100
|
+
let fields = getDigestFields(keys)
|
|
101
|
+
let vals = fields.map((k) => {
|
|
102
|
+
let v = isObj(row) ? row[k] : undefined
|
|
103
|
+
return v === undefined ? null : v
|
|
104
|
+
})
|
|
105
|
+
return cyrb53(JSON.stringify(vals)).toString(36)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 由草稿所依據之列計算載入基準(只含主鍵為字串之列)
|
|
111
|
+
*
|
|
112
|
+
* @param {Array} rows 輸入草稿所依據之那份列(開啟編輯時之 store 快照, 非存檔當下)
|
|
113
|
+
* @param {Array} keys 輸入該表 schema 欄位名陣列
|
|
114
|
+
* @returns {Array} 回傳 [[id, digest], ...]
|
|
115
|
+
*/
|
|
116
|
+
function makeBaseline(rows, keys) {
|
|
117
|
+
let rs = []
|
|
118
|
+
;(Array.isArray(rows) ? rows : []).forEach((r) => {
|
|
119
|
+
if (isObj(r) && isStr(r.id)) {
|
|
120
|
+
rs.push([r.id, rowDigest(r, keys)])
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
return rs
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 判斷客戶端送入之基準格式是否合法(陣列, 每項為 [字串, 字串])
|
|
129
|
+
*
|
|
130
|
+
* @param {*} baseline 輸入客戶端送入之基準
|
|
131
|
+
* @returns {Boolean} 回傳是否合法
|
|
132
|
+
*/
|
|
133
|
+
function isBaselineValid(baseline) {
|
|
134
|
+
if (!Array.isArray(baseline) || !baseline.every((e) => Array.isArray(e) && e.length === 2 && isStr(e[0]) && isStr(e[1]))) {
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//同 id 不得出現兩次(外審 sol R8): 同 id 帶不同版本為矛盾之基準, 由列序決定採哪版屬靜默擇一(R-PRINCIPLE-00 原則 3), 格式驗證即拒收
|
|
139
|
+
let ids = new Set()
|
|
140
|
+
for (let e of baseline) {
|
|
141
|
+
if (ids.has(e[0])) {
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
ids.add(e[0])
|
|
145
|
+
}
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 取得基準中某列之指紋(供前端確認草稿所依據之某列版本於期間是否變動)
|
|
152
|
+
*
|
|
153
|
+
* @param {Array} baseline 輸入載入基準 [[id, digest], ...]
|
|
154
|
+
* @param {String} id 輸入列 id
|
|
155
|
+
* @returns {String|undefined} 回傳該列指紋; 基準內無該列為 undefined
|
|
156
|
+
*/
|
|
157
|
+
function getBaselineDigest(baseline, id) {
|
|
158
|
+
let hit = (Array.isArray(baseline) ? baseline : []).find((e) => Array.isArray(e) && e[0] === id)
|
|
159
|
+
return hit === undefined ? undefined : hit[1]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 表級比對: 目前整表與載入基準(草稿所依據之整表)逐列內容指紋與列集合之差異數
|
|
165
|
+
*
|
|
166
|
+
* 比對對象是整表而非「本次將寫入之列」(R-PRINCIPLE-00 原則 7, 使用者 2026-10-22 裁示「以表雜湊比對, 先動先贏」): 表內任何一列之內容(指紋口徑見 getDigestFields: 排除 order 與伺服器管理欄)與基準不同,
|
|
167
|
+
* 或列被增刪(id 集合不同), 即視為資料已被他人/外部同步更新. 只含主鍵為字串之列(同 makeBaseline; 主鍵非字串之列 ORM 不允許入庫, 兩側一致忽略).
|
|
168
|
+
* 前端(判定草稿是否過期、顯示提示)與伺服端(checkBaseline)共用本函數, 口徑單一.
|
|
169
|
+
*
|
|
170
|
+
* @param {Object} p 輸入參數物件
|
|
171
|
+
* @param {Array} p.keys 輸入該表 schema 欄位名陣列
|
|
172
|
+
* @param {Array} p.rows 輸入目前整表之列(伺服端為庫內全部列, 前端為 store 之該表列)
|
|
173
|
+
* @param {Array} p.baseline 輸入載入基準 [[id, digest], ...](須已通過 isBaselineValid; 非陣列視為空基準)
|
|
174
|
+
* @returns {Object} 回傳 { nChanged, nRemoved, nAdded }: 內容指紋不同之列數/基準有而現已不在之列數/基準無而現在才有之列數; 三者皆 0 為一致
|
|
175
|
+
*/
|
|
176
|
+
function compareBaseline({ keys, rows, baseline }) {
|
|
177
|
+
let kp = new Map(Array.isArray(baseline) ? baseline : [])
|
|
178
|
+
let nChanged = 0
|
|
179
|
+
let nAdded = 0
|
|
180
|
+
let seen = new Set()
|
|
181
|
+
;(Array.isArray(rows) ? rows : []).forEach((o) => {
|
|
182
|
+
if (!isObj(o) || !isStr(o.id) || seen.has(o.id)) {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
seen.add(o.id)
|
|
186
|
+
if (!kp.has(o.id)) {
|
|
187
|
+
nAdded++
|
|
188
|
+
}
|
|
189
|
+
else if (kp.get(o.id) !== rowDigest(o, keys)) {
|
|
190
|
+
nChanged++
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
let nRemoved = 0
|
|
194
|
+
kp.forEach((v, id) => {
|
|
195
|
+
if (!seen.has(id)) {
|
|
196
|
+
nRemoved++
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
return { nChanged, nRemoved, nAdded }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 判斷載入基準相對於目前整表是否已過期(compareBaseline 之布林版, 供前端提示用)
|
|
205
|
+
*
|
|
206
|
+
* @param {Object} p 輸入參數物件(同 compareBaseline)
|
|
207
|
+
* @returns {Boolean} 回傳 true 為過期(有任何一列內容不同或列被增刪)
|
|
208
|
+
*/
|
|
209
|
+
function isBaselineStale(p) {
|
|
210
|
+
let c = compareBaseline(p)
|
|
211
|
+
return c.nChanged + c.nRemoved + c.nAdded > 0
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 以載入基準做表級過期檢查(在表鎖內, 以庫內整表現況比對; 不符整次拒收、零寫入; R-PRINCIPLE-00 原則 7)
|
|
217
|
+
*
|
|
218
|
+
* 規則: 載入時之整表與目前整表有任何差異(compareBaseline: 任一列內容指紋不同, 或列被增刪)即過期, 不論差異是否落在「本次將寫入之列」——
|
|
219
|
+
* 不部分變更(不逐項判斷哪些可蓋)、不上鎖, 先動先贏; 指紋排除 order 與伺服器管理欄, 故外部同步整批重寫但內容未變(含時間/建立者改變、order 重編)、純粹重排 order 皆不使草稿作廢.
|
|
220
|
+
* 修改前之 run07 版只檢查「本次將寫入之列」(他人改別列不擋), 使用者 2026-10-22 裁示改為表級, 見 ADR-037 Update(run11).
|
|
221
|
+
*
|
|
222
|
+
* @param {Object} p 輸入參數物件
|
|
223
|
+
* @param {Array} p.keys 輸入該表 schema 欄位名陣列
|
|
224
|
+
* @param {Array} p.ltdtOld 輸入該表庫內全部列
|
|
225
|
+
* @param {Array} p.baseline 輸入客戶端載入基準(須已通過 isBaselineValid)
|
|
226
|
+
* @returns {Object|null} 回傳 null 為通過; 否則 { key: 'saveStale', reason, n }: reason 為 rowChanged(有列內容不同)/rowRemoved(基準之列已不在)/rowAdded(多出基準沒有的列), 依序取第一個成立者; n 為差異總列數
|
|
227
|
+
*/
|
|
228
|
+
function checkBaseline({ keys, ltdtOld, baseline }) {
|
|
229
|
+
let c = compareBaseline({ keys, rows: ltdtOld, baseline })
|
|
230
|
+
let n = c.nChanged + c.nRemoved + c.nAdded
|
|
231
|
+
if (n === 0) {
|
|
232
|
+
return null
|
|
233
|
+
}
|
|
234
|
+
let reason = c.nChanged > 0 ? 'rowChanged' : (c.nRemoved > 0 ? 'rowRemoved' : 'rowAdded')
|
|
235
|
+
return { key: 'saveStale', reason, n }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
export { cyrb53, getDigestFields, rowDigest, makeBaseline, isBaselineValid, getBaselineDigest, compareBaseline, isBaselineStale, checkBaseline }
|
|
240
|
+
export default makeBaseline
|
|
241
|
+
</code></pre>
|
|
242
|
+
</article>
|
|
243
|
+
</section>
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<br class="clear">
|
|
253
|
+
|
|
254
|
+
<footer>
|
|
255
|
+
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.
|
|
256
|
+
</footer>
|
|
257
|
+
|
|
258
|
+
<script>prettyPrint();</script>
|
|
259
|
+
<script src="scripts/polyfill.js"></script>
|
|
260
|
+
<script src="scripts/linenumber.js"></script>
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
</body>
|
|
265
|
+
</html>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>src/plugins/dataReloadGuard.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/plugins/dataReloadGuard.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>//程式端資料載入/重載旗標(systemProcing)之守門核心(純邏輯, 不依賴 Vue/DOM, 可單元測試; mUI.markDataReload 即此; 規則帳本 R-RELOAD-GUARD-01)
|
|
49
|
+
//
|
|
50
|
+
//背景: 清單頁以旗標 systemProcing 標記「程式端剛重建 opt」, 使其後由 WTable/ag-grid 非同步觸發之 rowsChange 不被視為使用者編輯(isModified 不被誤設為真).
|
|
51
|
+
//旗標於「Vue 更新完成之 nextTick 後再過 1ms」解除(ag-grid 之 rowDataUpdated 事件於同一輪更新中以 setTimeout(0) 排程, 先於該 1ms 計時器觸發).
|
|
52
|
+
//
|
|
53
|
+
//缺陷(run12, AF-037 不穩定失敗之根因): 重載可重疊——一次儲存引起兩張表(如權限改名連帶更新群組之引用端)先後推播, 兩次重建相隔約 20ms, 而一次重建之渲染可超過此數.
|
|
54
|
+
//舊實作每次呼叫各自排一個「解除旗標」計時器, 第 1 次重建之計時器在第 2 次重建之 rowsChange 事件之前觸發而把旗標解除, 第 2 次之 rowsChange 因此被當成使用者編輯,
|
|
55
|
+
//isModified 變真、儲存鈕在儲存成功後殘留(並使其後之推播被當成「有未存編輯」而顯示過期橫幅).
|
|
56
|
+
//
|
|
57
|
+
//規則: 旗標只由「最後一次」呼叫所排之計時器解除; 較早呼叫之計時器於觸發時若已有較新之呼叫則不動旗標.
|
|
58
|
+
|
|
59
|
+
let seqs = new WeakMap() //vm → 最近一次呼叫之序號
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 標記接下來由程式端資料載入/重載(初始載入、store 推播、isEditable/語系切換等 genOpt 重建 opt)所觸發之 rowsChange 非使用者變更
|
|
64
|
+
*
|
|
65
|
+
* @param {Object} vm 輸入元件實例(須有 systemProcing 屬性與 $nextTick 函數)
|
|
66
|
+
* @param {Function} [setTimeoutFn=setTimeout] 輸入計時器函數(供單元測試注入, 預設全域 setTimeout)
|
|
67
|
+
*/
|
|
68
|
+
function markDataReload(vm, setTimeoutFn = setTimeout) {
|
|
69
|
+
let seq = (seqs.get(vm) || 0) + 1
|
|
70
|
+
seqs.set(vm, seq)
|
|
71
|
+
vm.systemProcing = true
|
|
72
|
+
vm.$nextTick(() => {
|
|
73
|
+
setTimeoutFn(() => {
|
|
74
|
+
//已有較新之重載(其渲染與事件尚未完成)則不解除: 旗標交由最後一次呼叫所排之計時器
|
|
75
|
+
if (seqs.get(vm) === seq) {
|
|
76
|
+
vm.systemProcing = false
|
|
77
|
+
}
|
|
78
|
+
}, 1)
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
export { markDataReload }
|
|
84
|
+
export default markDataReload
|
|
85
|
+
</code></pre>
|
|
86
|
+
</article>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<br class="clear">
|
|
97
|
+
|
|
98
|
+
<footer>
|
|
99
|
+
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.
|
|
100
|
+
</footer>
|
|
101
|
+
|
|
102
|
+
<script>prettyPrint();</script>
|
|
103
|
+
<script src="scripts/polyfill.js"></script>
|
|
104
|
+
<script src="scripts/linenumber.js"></script>
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|