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
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#applyPurgePlan">applyPurgePlan</a></li><li><a href="global.html#auditFrom">auditFrom</a></li><li><a href="global.html#auditRefs">auditRefs</a></li><li><a href="global.html#buildReport">buildReport</a></li><li><a href="global.html#buildRowsSend">buildRowsSend</a></li><li><a href="global.html#buildSyncRowsNew">buildSyncRowsNew</a></li><li><a href="global.html#checkBaseline">checkBaseline</a></li><li><a href="global.html#checkCascadePlan">checkCascadePlan</a></li><li><a href="global.html#checkDbNotInUse">checkDbNotInUse</a></li><li><a href="global.html#checkExternalWrite">checkExternalWrite</a></li><li><a href="global.html#checkMapKeyBatch">checkMapKeyBatch</a></li><li><a href="global.html#checkUsersWrite">checkUsersWrite</a></li><li><a href="global.html#cleanOutOf">cleanOutOf</a></li><li><a href="global.html#compareBaseline">compareBaseline</a></li><li><a href="global.html#countAndRefs">countAndRefs</a></li><li><a href="global.html#countExternalRows">countExternalRows</a></li><li><a href="global.html#countIdRenumber">countIdRenumber</a></li><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#diffRefTargets">diffRefTargets</a></li><li><a href="global.html#dropServerOnlyDiff">dropServerOnlyDiff</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fillIdlessIds">fillIdlessIds</a></li><li><a href="global.html#findDupIndex">findDupIndex</a></li><li><a href="global.html#findKeyOtherFrom">findKeyOtherFrom</a></li><li><a href="global.html#findRefRows">findRefRows</a></li><li><a href="global.html#findTouchedDup">findTouchedDup</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#fmtVars">fmtVars</a></li><li><a href="global.html#fmtWarn">fmtWarn</a></li><li><a href="global.html#formatFixReport">formatFixReport</a></li><li><a href="global.html#formatFromAudit">formatFromAudit</a></li><li><a href="global.html#formatPurgeReport">formatPurgeReport</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getAddConfirmText">getAddConfirmText</a></li><li><a href="global.html#getAddImpact">getAddImpact</a></li><li><a href="global.html#getBannerLines">getBannerLines</a></li><li><a href="global.html#getBaselineDigest">getBaselineDigest</a></li><li><a href="global.html#getCascadePreview">getCascadePreview</a></li><li><a href="global.html#getChangedIdx">getChangedIdx</a></li><li><a href="global.html#getDelConfirmText">getDelConfirmText</a></li><li><a href="global.html#getDelImpact">getDelImpact</a></li><li><a href="global.html#getDelRefCount">getDelRefCount</a></li><li><a href="global.html#getDigestFields">getDigestFields</a></li><li><a href="global.html#getDupRowsInconsistent">getDupRowsInconsistent</a></li><li><a href="global.html#getEffMode">getEffMode</a></li><li><a href="global.html#getExternalEdits">getExternalEdits</a></li><li><a href="global.html#getItemWarnText">getItemWarnText</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getLockFields">getLockFields</a></li><li><a href="global.html#getNameErrs">getNameErrs</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getPropKey">getPropKey</a></li><li><a href="global.html#getRawModeMap">getRawModeMap</a></li><li><a href="global.html#getRefRowsExtra">getRefRowsExtra</a></li><li><a href="global.html#getRefState">getRefState</a></li><li><a href="global.html#getRefWarnText">getRefWarnText</a></li><li><a href="global.html#getRefWarns">getRefWarns</a></li><li><a href="global.html#getRowIdxs">getRowIdxs</a></li><li><a href="global.html#getRowState">getRowState</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#getRuleWarnText">getRuleWarnText</a></li><li><a href="global.html#getRuleWarns">getRuleWarns</a></li><li><a href="global.html#getSavePreview">getSavePreview</a></li><li><a href="global.html#getSyncWarnLines">getSyncWarnLines</a></li><li><a href="global.html#hasDupKey">hasDupKey</a></li><li><a href="global.html#hasExternalDiff">hasExternalDiff</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#isBaselineStale">isBaselineStale</a></li><li><a href="global.html#isBaselineValid">isBaselineValid</a></li><li><a href="global.html#isDraftStale">isDraftStale</a></li><li><a href="global.html#isExternalRow">isExternalRow</a></li><li><a href="global.html#isJsonLossy">isJsonLossy</a></li><li><a href="global.html#isMain">isMain</a></li><li><a href="global.html#joinList">joinList</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepAssignByEntity">keepAssignByEntity</a></li><li><a href="global.html#keepAssignOnMissing">keepAssignOnMissing</a></li><li><a href="global.html#makeBaseline">makeBaseline</a></li><li><a href="global.html#makeRowStates">makeRowStates</a></li><li><a href="global.html#mapKeyOf">mapKeyOf</a></li><li><a href="global.html#mapNameKp">mapNameKp</a></li><li><a href="global.html#markDataReload">markDataReload</a></li><li><a href="global.html#markHasKey">markHasKey</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#pairRowsToSnapshot">pairRowsToSnapshot</a></li><li><a href="global.html#pickUniqueUser">pickUniqueUser</a></li><li><a href="global.html#planDraftSync">planDraftSync</a></li><li><a href="global.html#planFixFromType">planFixFromType</a></li><li><a href="global.html#planPurgeChain">planPurgeChain</a></li><li><a href="global.html#planPurgeFrom">planPurgeFrom</a></li><li><a href="global.html#planRefChanges">planRefChanges</a></li><li><a href="global.html#putRefEntry">putRefEntry</a></li><li><a href="global.html#putRowsToKp">putRowsToKp</a></li><li><a href="global.html#refsChanged">refsChanged</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#restoreExternalOrder">restoreExternalOrder</a></li><li><a href="global.html#restoreServerFields">restoreServerFields</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#rewriteRefs">rewriteRefs</a></li><li><a href="global.html#rowDigest">rowDigest</a></li><li><a href="global.html#runAuditFrom">runAuditFrom</a></li><li><a href="global.html#runClean">runClean</a></li><li><a href="global.html#runFixFromType">runFixFromType</a></li><li><a href="global.html#runPurgeFrom">runPurgeFrom</a></li><li><a href="global.html#splitCheckedRows">splitCheckedRows</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#tabWriteKey">tabWriteKey</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#withTabLocks">withTabLocks</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li><li><a href="global.html#writeWithCascade">writeWithCascade</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -60,6 +60,11 @@ import getOwn from '../src/js/getOwn.mjs'
|
|
|
60
60
|
import ds from '../src/schema/index.mjs'
|
|
61
61
|
import procLang from './procLang.mjs'
|
|
62
62
|
import validateRowsBeforeWrite from './validateRows.mjs'
|
|
63
|
+
import planRefChanges, { REF_OF, diffRefTargets, fillIdlessIds } from '../src/js/refCascade.mjs'
|
|
64
|
+
import { restoreExternalOrder, dropServerOnlyDiff, restoreServerFields, checkExternalWrite, hasExternalDiff } from '../src/js/externalRows.mjs'
|
|
65
|
+
import { isBaselineValid, checkBaseline } from '../src/js/rowDigest.mjs'
|
|
66
|
+
import { findTouchedDup, findDupIndex, withTabLocks, checkUsersWrite } from './keyCheck.mjs'
|
|
67
|
+
import { checkCascadePlan, writeWithCascade } from './cascadeWrite.mjs'
|
|
63
68
|
|
|
64
69
|
|
|
65
70
|
//isRowObj: 待寫入之列須為至少有一個自有可列舉鍵之物件(非 null、非陣列; 不用依 toString 判型之 iseobj, 免被 Symbol.toStringTag 偽稱)
|
|
@@ -105,7 +110,17 @@ function mergePickKeysOnly(ltdtDb, rowsIn, keyDetect, pickKeys) {
|
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
|
|
108
|
-
|
|
113
|
+
//mappingBy: 使用者對應鍵('id'|'email'|'name', WWebPerm 已正規化; 未給或其他值視同 'id'), 供 users 整表寫入的對應鍵檢查(R-PRINCIPLE-00 適用表 h, R-MAPKEY-03)
|
|
114
|
+
//cascadeRename: 群組/權限改名時是否連帶更新引用(預設 true; 只管改名, 刪除連帶清理不受影響, R-REF-CASCADE-01)
|
|
115
|
+
//lockExternalRows: 後台是否不得改外部列(from 為非空字串者)之身分/結構欄(預設 true, R-EXT-OWN-01; 'n' 時回到舊行為, 僅保留 from 型別檢查)
|
|
116
|
+
function proc(woItems, procOrm, { srLog, kmx, lockSave, mappingBy = 'id', cascadeRename = true, lockExternalRows = true }) {
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
//pickClientOpt: 用戶端(kpFunExt 第 3 參)只採納 baseline, 不接受 pickKeysOnly 等伺服端設定; 未給(undefined/null)視為舊客戶端
|
|
120
|
+
let pickClientOpt = (opt) => {
|
|
121
|
+
let b = (opt !== null && typeof opt === 'object') ? opt.baseline : undefined
|
|
122
|
+
return (b === undefined || b === null) ? {} : { baseline: b }
|
|
123
|
+
}
|
|
109
124
|
|
|
110
125
|
|
|
111
126
|
//updateTabItems
|
|
@@ -121,6 +136,13 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
121
136
|
return Promise.reject('saveRowFieldInvalid')
|
|
122
137
|
}
|
|
123
138
|
|
|
139
|
+
//baseline: 草稿所依據之載入基準 [[id, digest], ...](F-26, R-BASELINE-01; 前端於開啟編輯時綁定); 未給(舊客戶端)為 null; 給了但格式不合視為無效請求, 於任何讀寫之前拒絕
|
|
140
|
+
let baseline = get(opt, 'baseline', null)
|
|
141
|
+
if (baseline !== null && !isBaselineValid(baseline)) {
|
|
142
|
+
srLog.error({ event: 'updateTabItems-validate-error', keyTable, msg: 'saveRowFieldInvalid', reason: 'baselineInvalid', row: -1, field: '' })
|
|
143
|
+
return Promise.reject('saveRowFieldInvalid')
|
|
144
|
+
}
|
|
145
|
+
|
|
124
146
|
//pickKeysOnly, 僅採納既有列之指定欄位變更(見 mergePickKeysOnly), 未給則整表 diff 寫入
|
|
125
147
|
let pickKeysOnly = get(opt, 'pickKeysOnly', null)
|
|
126
148
|
|
|
@@ -138,10 +160,16 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
138
160
|
//雙擊防護(後端): 同一操作者之同表寫入處理中再送出即拒絕 'saveInProgress'(不排隊, 見 lockSave.mjs, ADR-025);
|
|
139
161
|
//占位包在 kmx 之外層, 不同操作者之並行寫入仍由 kmx 序列化、各自成功(ADR-012)
|
|
140
162
|
//序列化同表之並行整表批次寫入: 防 lost update (同 keyTable 序列化, 不同表並行)
|
|
141
|
-
|
|
163
|
+
//持鎖: 先取本表之鎖; 本表有改名或刪除而須改寫引用端時, 才於內層依階層再取引用端表之鎖(見下方「引用連帶」段). 皆經單一取鎖函數(R-WRITE-LOCK-02); 與外部同步(WWebPerm.syncAndReplaceTabs)共用同表互斥鍵(外審 sol R10)
|
|
164
|
+
return await lockSave(`updateTabItems:${keyTable}`, userId, () => withTabLocks(kmx, [keyTable], async () => {
|
|
142
165
|
|
|
143
166
|
//pickKeysOnly, 須於 mutex 內讀取資料庫為基底, 避免與並行寫入 race
|
|
144
167
|
if (isearr(pickKeysOnly)) {
|
|
168
|
+
//有限數字 id 先轉為字串形式再與庫內對應(外審 sol R7): mergePickKeysOnly 只認非空字串 id, 數字 id(如 1)對不上庫內之 '1' 而該列指派被靜默略過卻回成功(R-ORM-04 之同一正規化, 此處原晚於合併)
|
|
169
|
+
rows = map(rows, (r) => {
|
|
170
|
+
let id = get(r, 'id')
|
|
171
|
+
return (typeof id === 'number' && Number.isFinite(id)) ? { ...r, id: String(id) } : r
|
|
172
|
+
})
|
|
145
173
|
let ltdtDb = await woItems[keyTable].select()
|
|
146
174
|
rows = mergePickKeysOnly(ltdtDb, rows, keyDetect, pickKeysOnly)
|
|
147
175
|
srLog.info({ event: 'updateTabItems-pickKeysOnly', keyTable, userId, pickKeysOnly })
|
|
@@ -158,7 +186,8 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
158
186
|
})
|
|
159
187
|
|
|
160
188
|
//ckKey, 回傳 err key (saveRowFieldInvalid / saveRowFieldDuplicate) 或 null; 動態細節 (row index/field/value) 改記 srLog, 不進使用者訊息
|
|
161
|
-
|
|
189
|
+
//opt.skipDup 為 true 時只檢查空值/非字串, 不檢查重複: pemis/grups 之名稱唯一改由下方 findTouchedDup 判定(只拒本次造成或觸及者, F-02, ADR-034)
|
|
190
|
+
let ckKey = (rows, key, opt = {}) => {
|
|
162
191
|
let err = null
|
|
163
192
|
|
|
164
193
|
//check
|
|
@@ -176,7 +205,7 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
176
205
|
}
|
|
177
206
|
|
|
178
207
|
//check
|
|
179
|
-
if (haskey(kp, value)) {
|
|
208
|
+
if (opt.skipDup !== true && haskey(kp, value)) {
|
|
180
209
|
srLog.error({ event: 'updateTabItems-ckKey-error', keyTable, key, msg: 'saveRowFieldDuplicate', row: k, value })
|
|
181
210
|
err = `saveRowFieldDuplicate`
|
|
182
211
|
return false //跳出
|
|
@@ -199,18 +228,13 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
199
228
|
return Promise.reject(err)
|
|
200
229
|
}
|
|
201
230
|
}
|
|
202
|
-
if (arrHas(keyTable, ['pemis', 'grups'])) { //users可重複name
|
|
203
|
-
err = ckKey(rows, 'name')
|
|
204
|
-
if (err !== null) {
|
|
205
|
-
return Promise.reject(err)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
if (arrHas(keyTable, ['users'])) {
|
|
209
|
-
err = ckKey(rows, 'email')
|
|
231
|
+
if (arrHas(keyTable, ['pemis', 'grups'])) { //users可重複name故不列入; 此處只驗空值, 名稱重複見下方 findTouchedDup
|
|
232
|
+
err = ckKey(rows, 'name', { skipDup: true })
|
|
210
233
|
if (err !== null) {
|
|
211
234
|
return Promise.reject(err)
|
|
212
235
|
}
|
|
213
236
|
}
|
|
237
|
+
//users 之 email/對應鍵檢查改至下方 select 之後(需庫內現況判定「本次造成或觸及」, R-PRINCIPLE-00 適用表 h), 不再於此以整表 ckKey('email') 擋(歷史空值/重複會使所有存檔失敗)
|
|
214
238
|
}
|
|
215
239
|
|
|
216
240
|
//主鍵之有限數字轉為其字串形式(規則帳本 R-ORM-04; w-serv-orm 1.1.11 起庫內即如此存放): ltdtDiffByKey 以嚴格比對, 未轉時 123 對不上庫內之 '123',
|
|
@@ -220,9 +244,59 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
220
244
|
return (typeof id === 'number' && Number.isFinite(id)) ? { ...r, id: String(id) } : r
|
|
221
245
|
})
|
|
222
246
|
|
|
247
|
+
//pemis/grups 以 id 對應既有列(見下方 isNameKeyTable 分支), 送入之整表內主鍵 id(字串)不得重複, 否則 Map(id → 列) 靜默併成一列而遺失其一(unit-procCore-writeValidate WV-008);
|
|
248
|
+
//缺 id 之列(API 直打, 視為新增)不參與; 錯誤鍵同 validateRowsBeforeWrite 之 idDuplicate: saveRowFieldDuplicate
|
|
249
|
+
if (arrHas(keyTable, ['pemis', 'grups'])) {
|
|
250
|
+
let kIdDup = findDupIndex(rows, 'id', (r) => isestr(get(r, 'id'))) //空字串 id 視為未給(待自動產號, validateRows 同), 不參與重複判定(外審 sol R6)
|
|
251
|
+
if (kIdDup >= 0) {
|
|
252
|
+
srLog.error({ event: 'updateTabItems-ckKey-error', keyTable, key: 'id', msg: 'saveRowFieldDuplicate', row: kIdDup, value: get(rows, [kIdDup, 'id']) })
|
|
253
|
+
return Promise.reject('saveRowFieldDuplicate')
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
223
257
|
//ltdtDiffByKey
|
|
224
258
|
let ltdtOld = await woItems[keyTable].select()
|
|
225
259
|
|
|
260
|
+
//表級載入基準比對(R-PRINCIPLE-00 原則 7, 規則帳本 R-BASELINE-01, ADR-037 Update(run11); 使用者 2026-10-22 裁示「多人同時編輯以表雜湊比對, 先動先贏」):
|
|
261
|
+
//有基準者, 草稿載入時之整表須與目前庫內整表相同(任一列內容指紋不同或列被增刪即過期), 否則整次拒收 saveStale、零寫入, 不部分變更、不上鎖; 指紋排除 order 與伺服器管理欄(見 rowDigest.getDigestFields).
|
|
262
|
+
//放在 select 之後、所有檢查與還原之前: 基準過期時下列各檢查之結果(撞名、新增列已存在、外部列守門…)皆以過期之畫面為前提, 應先回「請重新載入」而非誤導之其他訊息.
|
|
263
|
+
//無基準者(舊客戶端)不在此檢查: 後台列維持最後寫入者勝(ADR-013), 外部列之指派欄改動另於下方拒收(saveBaselineRequired). srLog 只記錯誤鍵、原因與差異筆數, 不記列 id/個資值
|
|
264
|
+
if (baseline !== null) {
|
|
265
|
+
let ck = checkBaseline({ keys: ds[keyTable].keys, ltdtOld, baseline })
|
|
266
|
+
if (ck !== null) {
|
|
267
|
+
srLog.error({ event: 'updateTabItems-stale-error', keyTable, userId, msg: ck.key, reason: ck.reason, n: ck.n })
|
|
268
|
+
return Promise.reject(ck.key)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//外部列之 order 由外部擁有(R-EXT-OWN-01): 上方 order=k+1 重編不得改外部列之順序, 還原為庫內值(使其不因後台存檔而成為 diff)
|
|
273
|
+
if (lockExternalRows) {
|
|
274
|
+
rows = restoreExternalOrder(rows, ltdtOld)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
//pemis/grups 名稱唯一(F-02, 規則帳本 R-NAME-UNIQ-02, ADR-034): 寫入後之名稱集合即送入之整表(含他 from 之外部同步列), 其中出現兩列(不同 id)同名且為「本次造成或觸及」者整次拒收於任何寫入之前;
|
|
278
|
+
//庫內已有之歷史重複(僅外部同步可造成)原樣送回不算, 否則已有重複之庫將無法在後台修復. 比對為精確 ===(同 arrFinder). 放在 select 之後、saveNewRowExists 之前, 使撞名先於「新增列已存在」被回報(與修正前 ckKey 之次序相同)
|
|
279
|
+
let isNameKeyTable = arrHas(keyTable, ['pemis', 'grups'])
|
|
280
|
+
if (isNameKeyTable) {
|
|
281
|
+
let kDup = findTouchedDup(rows, ltdtOld, 'name')
|
|
282
|
+
if (kDup >= 0) {
|
|
283
|
+
srLog.error({ event: 'updateTabItems-ckKey-error', keyTable, key: 'name', msg: 'nameDuplicate', row: kDup, value: get(rows, [kDup, 'name']) })
|
|
284
|
+
return Promise.reject('nameDuplicate')
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
//users 之 email/對應鍵(R-PRINCIPLE-00 適用表 h, 規則帳本 R-MAPKEY-03): 遭遇衝突就收嚴, 但只擋「本次造成或觸及」者, 歷史既有之空值/重複原樣送回不拒(否則已有歷史資料之庫無法在後台修復, 連群組指派 pickKeysOnly 也會失敗);
|
|
289
|
+
//①全表 email 須為非空字串且不重複; ②mappingBy 為 email/name 時另對 isActive==='y' 之列檢查該欄位(含停用改啟用); 判準單一擁有者 keyCheck.checkUsersWrite(與外部同步 F-14 同口徑).
|
|
290
|
+
//放在 select 之後、saveNewRowExists 之前(次序同 pemis/grups 之 findTouchedDup), 於任何破壞性寫入之前(本函式在 kmx(tabWriteKey) 內, R-WRITE-LOCK-01); 被拒時資料表零變更.
|
|
291
|
+
//srLog 只記錯誤鍵、原因、欄位與列索引, 不記 email/name 之值(個資; 修正前重複時記 value)
|
|
292
|
+
if (keyTable === 'users') {
|
|
293
|
+
let ck = checkUsersWrite(rows, ltdtOld, mappingBy)
|
|
294
|
+
if (ck !== null) {
|
|
295
|
+
srLog.error({ event: 'updateTabItems-ckKey-error', keyTable, key: ck.field, msg: ck.key, reason: ck.reason, row: ck.k })
|
|
296
|
+
return Promise.reject(ck.key)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
226
300
|
//新增列已存在即拒絕 (ADR-025): 同一包重送且第 2 次於第 1 次完成後才處理時(占位已釋放), 第 1 次已建立之列會被當「修改」(diff),
|
|
227
301
|
//以前端之佔位字覆寫伺服器於新增時填入之建立者 / 建立時間(procOrm save 只覆寫 userIdUpdate / timeUpdate); 故整包拒絕、不寫入
|
|
228
302
|
if (size(idsNew) > 0) {
|
|
@@ -236,9 +310,44 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
236
310
|
}
|
|
237
311
|
|
|
238
312
|
let ltdtNew = rows
|
|
239
|
-
let r =
|
|
313
|
+
let r = null
|
|
314
|
+
if (isNameKeyTable || keyTable === 'users') { //users 亦拆出缺 id 之列(外審 sol R5): 同批兩列缺 id 時 ltdtmapping 皆補 id:'', ltdtDiffByKey 以 Map(id → 列) 使前列被後列覆蓋而只建立一列卻回成功
|
|
315
|
+
//pemis/grups 一律以主鍵 id 對應(F-02 完整化, run04 主責補強, ADR-034 Update): 寫入後之名稱集合須等於送入之整表, findTouchedDup 對「送入整表」驗唯一才成立,
|
|
316
|
+
//而以 name 為 diff 鍵時 ltdtDiffByKey 以 Map(name → 列) 對應, 下列單次存檔雖通過檢查卻使庫內與送入不同、留下同名兩列且仍回成功(D1 設計稿 §1.5 以真 procCore+ORM 替身實測,
|
|
317
|
+
//unit-procCore-nameUniq NU-021~024 守護): ①改名 A→A2 並以舊名 A 新增另一列(新 id)——改名靜默遺失; ②刪除 A 並以同名新增(新 id, F-02② 原案)——舊列未刪;
|
|
318
|
+
//③鏈式改名 A→B 且 B→C——id-2 應為 C 卻停在 B; ④庫內已有同名列(歷史重複)時改名其中先出現之一列——新名稱之列走 add 而其 id 已存在, insert 對既有主鍵略過(NU-011 修前實測).
|
|
319
|
+
//改以 id 對應後: 改名成為 diff 而非 del + add(建立者與建立時間沿用庫內, 同名之歷史重複各自獨立); 缺 id 之送入列(API 直打)無從對應既有列, 視為新增.
|
|
320
|
+
let withId = ltdtNew.filter((v) => isestr(get(v, 'id')))
|
|
321
|
+
let noId = ltdtNew.filter((v) => !isestr(get(v, 'id')))
|
|
322
|
+
r = ltdtDiffByKey(ltdtOld, withId, 'id')
|
|
323
|
+
r.add = [...r.add, ...noId]
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
r = ltdtDiffByKey(ltdtOld, ltdtNew, keyDetect)
|
|
327
|
+
}
|
|
240
328
|
// console.log('ltdtDiffByKey r', r)
|
|
241
329
|
|
|
330
|
+
//外部列保護(F-23; R-PRINCIPLE-00 原則 3、5; 規則帳本 R-EXT-OWN-01, ADR-037): 於任何寫入之前(含引用端), 違規整次拒收、資料表零變更, 不靜默忽略;
|
|
331
|
+
//(載入基準之表級比對 checkBaseline 已在上方 select 之後先行, run11: 基準過期時應先回「請重新載入」, 而非誤導之「外部列不可修改」, run07 U1 觀察 1)
|
|
332
|
+
//①外部列僅伺服器管理欄不同者(外部同步重寫使其時間/建立者較新, 持舊快照送回)視為無變更, 免把建立者與建立時間回退為舊值(S2); 內容確有改動(指派欄)之外部列改帶庫內之伺服器管理欄寫入(restoreServerFields);
|
|
333
|
+
//②from 型別與外部列守門(checkExternalWrite): 新增列 from 須為 ''、外部列不可刪/不可改 from 與身分結構欄, 指派欄可寫(草稿新鮮而仍違規者在此被拒);
|
|
334
|
+
//③無基準而修改外部列之指派欄: 無從證明快照新舊, Lv1 放行之指派欄不得因舊快照還原外部已撤銷之授權(外審 B.2), 依原則 3 拒收(saveBaselineRequired); 後台列無基準維持舊行為(最後寫入者勝, ADR-013).
|
|
335
|
+
//srLog 只記錯誤鍵、原因、筆數與來源名稱, 不記列 id/個資值
|
|
336
|
+
let keysTab = ds[keyTable].keys
|
|
337
|
+
if (lockExternalRows) {
|
|
338
|
+
r = dropServerOnlyDiff({ keys: keysTab, r, ltdtOld })
|
|
339
|
+
r = restoreServerFields({ r, ltdtOld }) //改動之外部列帶庫內之伺服器管理欄: 持舊草稿(保留未存編輯期間外部同步重寫了該列)不得把較新之建立者/建立時間回退(外審 sol 第 3 點)
|
|
340
|
+
}
|
|
341
|
+
let ex =checkExternalWrite({ keyTable, keys: keysTab, r, ltdtOld, lockExternalRows })
|
|
342
|
+
if (ex !== null) {
|
|
343
|
+
srLog.error({ event: 'updateTabItems-external-error', keyTable, userId, msg: ex.key, reason: ex.reason, n: ex.n, froms: ex.froms })
|
|
344
|
+
return Promise.reject(ex.key)
|
|
345
|
+
}
|
|
346
|
+
if (baseline === null && lockExternalRows && hasExternalDiff({ r, ltdtOld })) {
|
|
347
|
+
srLog.error({ event: 'updateTabItems-stale-error', keyTable, userId, msg: 'saveBaselineRequired', reason: 'externalAssignNoBaseline' })
|
|
348
|
+
return Promise.reject('saveBaselineRequired')
|
|
349
|
+
}
|
|
350
|
+
|
|
242
351
|
//寫入前驗證(規則帳本 R-ORM-01, ADR-026): 於第一個破壞步驟(del)之前擋下可預判之寫入失敗, 被拒時資料表零變更.
|
|
243
352
|
//why: 寫入順序為 del → insert → save, 而 w-serv-orm 1.1.8 起對「會被 clearXSS 改寫之主鍵」整批拒絕、LMDB 對 > 1978 B 之主鍵整批拒絕,
|
|
244
353
|
//修正前遇之即「舊列已刪、新列被拒」(改標的 id 時該列遺失, 2026-10-14 V3/V7 實測); 非主鍵欄位之標記則被靜默改寫(存值≠送入值).
|
|
@@ -256,30 +365,59 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
256
365
|
return Promise.reject(key)
|
|
257
366
|
}
|
|
258
367
|
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
368
|
+
//writeMain
|
|
369
|
+
let writeMain = async () => {
|
|
370
|
+
|
|
371
|
+
//del
|
|
372
|
+
if (size(r.del) > 0) {
|
|
373
|
+
await procOrm(userId, keyTable, 'del', r.del) //須使用procOrm才有辦法自動給予相關欄位
|
|
374
|
+
// .catch((err) => {
|
|
375
|
+
// console.log('woItems[keyTable].del err', err)
|
|
376
|
+
// })
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
//add
|
|
380
|
+
if (size(r.add) > 0) {
|
|
381
|
+
await procOrm(userId, keyTable, 'insert', r.add) //須使用procOrm才有辦法自動給予相關欄位
|
|
382
|
+
// .catch((err) => {
|
|
383
|
+
// console.log('woItems[keyTable].insert err', err)
|
|
384
|
+
// })
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
//diff
|
|
388
|
+
if (size(r.diff) > 0) {
|
|
389
|
+
await procOrm(userId, keyTable, 'save', r.diff) //須使用procOrm才有辦法自動給予相關欄位
|
|
390
|
+
// .catch((err) => {
|
|
391
|
+
// console.log('woItems[keyTable].save err', err)
|
|
392
|
+
// })
|
|
393
|
+
}
|
|
266
394
|
|
|
267
|
-
//add
|
|
268
|
-
if (size(r.add) > 0) {
|
|
269
|
-
await procOrm(userId, keyTable, 'insert', r.add) //須使用procOrm才有辦法自動給予相關欄位
|
|
270
|
-
// .catch((err) => {
|
|
271
|
-
// console.log('woItems[keyTable].insert err', err)
|
|
272
|
-
// })
|
|
273
395
|
}
|
|
274
396
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
397
|
+
//引用連帶(R-PRINCIPLE-00 原則 6、適用表 m/n, 規則帳本 R-REF-CASCADE-01, ADR-036): grups/pemis 改名(同 id 異名)與 grups/pemis/targets 刪除(舊 id 不在送入整表),
|
|
398
|
+
//須於同一次寫入內連帶更新/清理引用它之授權字串(users.cgrups、grups.cpemis、pemis.crules). 是否須改寫引用端只依本表新舊列判定(diffRefTargets), 故於持有本表鎖之下即可決定是否再取引用端表之鎖;
|
|
399
|
+
//取齊鎖之後才讀取引用端、規劃與預檢(新名撞懸空名、歷史同名組改名、容器內相撞、改寫後欄位驗證), 預檢與寫入涵蓋同一版本(外審 A.3), 被拒時資料表零變更.
|
|
400
|
+
//寫入為引用端先、主體後, 失敗時補償還原引用端(cascadeWrite.mjs)
|
|
401
|
+
//未帶 id 之列(API 直打; 寫入時 ORM 補隨機主鍵, 即舊列刪、新列增)改依名稱對應既有列, 同名即同一實體而不清空引用(fillIdlessIds)
|
|
402
|
+
let rowsPlan = arrHas(keyTable, ['targets', 'pemis', 'grups']) ? fillIdlessIds(keyTable, ltdtOld, ltdtNew) : ltdtNew
|
|
403
|
+
let kind = arrHas(keyTable, ['targets', 'pemis', 'grups']) ? diffRefTargets({ keyTable, rowsOld: ltdtOld, rowsNew: rowsPlan, opt: { cascadeRename } }) : null
|
|
404
|
+
if (kind !== null && (kind.renames.length > 0 || kind.delNames.length > 0)) {
|
|
405
|
+
let holder = REF_OF[keyTable].holder
|
|
406
|
+
return await withTabLocks(kmx, [holder], async () => {
|
|
407
|
+
let holdersOld = await woItems[holder].select()
|
|
408
|
+
let plan = planRefChanges({ keyTable, rowsOld: ltdtOld, rowsNew: rowsPlan, holdersOld, opt: { cascadeRename } })
|
|
409
|
+
let errPlan = plan.err !== null ? plan.err : checkCascadePlan(plan)
|
|
410
|
+
if (errPlan !== null) {
|
|
411
|
+
srLog.error({ event: 'updateTabItems-refCascade-error', keyTable, msg: errPlan })
|
|
412
|
+
return Promise.reject(errPlan)
|
|
413
|
+
}
|
|
414
|
+
await writeWithCascade({ plan, holdersOld, woItems, procOrm, userId, srLog, writeMain })
|
|
415
|
+
return ltdtNew
|
|
416
|
+
}, [keyTable])
|
|
281
417
|
}
|
|
282
418
|
|
|
419
|
+
await writeMain()
|
|
420
|
+
|
|
283
421
|
return ltdtNew
|
|
284
422
|
|
|
285
423
|
}))
|
|
@@ -299,9 +437,9 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
299
437
|
|
|
300
438
|
|
|
301
439
|
//updateTargets
|
|
302
|
-
let updateTargets = async (userId, rows) => {
|
|
440
|
+
let updateTargets = async (userId, rows, opt = {}) => {
|
|
303
441
|
try {
|
|
304
|
-
await updateTabItems('targets', userId, rows, 'id')
|
|
442
|
+
await updateTabItems('targets', userId, rows, 'id', pickClientOpt(opt))
|
|
305
443
|
}
|
|
306
444
|
catch (err) { throw errLog('updateTargets-error', userId, err) }
|
|
307
445
|
srLog.info({ event: 'updateTargets-success', userId })
|
|
@@ -310,9 +448,9 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
310
448
|
|
|
311
449
|
|
|
312
450
|
//updatePemis
|
|
313
|
-
let updatePemis = async (userId, rows) => {
|
|
451
|
+
let updatePemis = async (userId, rows, opt = {}) => {
|
|
314
452
|
try {
|
|
315
|
-
await updateTabItems('pemis', userId, rows, 'name')
|
|
453
|
+
await updateTabItems('pemis', userId, rows, 'name', pickClientOpt(opt))
|
|
316
454
|
}
|
|
317
455
|
catch (err) { throw errLog('updatePemis-error', userId, err) }
|
|
318
456
|
srLog.info({ event: 'updatePemis-success', userId })
|
|
@@ -321,9 +459,9 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
321
459
|
|
|
322
460
|
|
|
323
461
|
//updateGrups
|
|
324
|
-
let updateGrups = async (userId, rows) => {
|
|
462
|
+
let updateGrups = async (userId, rows, opt = {}) => {
|
|
325
463
|
try {
|
|
326
|
-
await updateTabItems('grups', userId, rows, 'name')
|
|
464
|
+
await updateTabItems('grups', userId, rows, 'name', pickClientOpt(opt))
|
|
327
465
|
}
|
|
328
466
|
catch (err) { throw errLog('updateGrups-error', userId, err) }
|
|
329
467
|
srLog.info({ event: 'updateGrups-success', userId })
|
|
@@ -331,7 +469,8 @@ function proc(woItems, procOrm, { srLog, kmx, lockSave }) {
|
|
|
331
469
|
}
|
|
332
470
|
|
|
333
471
|
|
|
334
|
-
//updateUsers, opt.pickKeysOnly 給 ['cgrups'] 時僅採納既有使用者之群組指派變更(modeEditUsers='for:grups')
|
|
472
|
+
//updateUsers, opt.pickKeysOnly 給 ['cgrups'] 時僅採納既有使用者之群組指派變更(modeEditUsers='for:grups'; 伺服端設定, WWebPerm 組入, 不接受用戶端給);
|
|
473
|
+
//opt.baseline 為草稿載入基準(用戶端給, 其餘 opt 鍵不採納, 見 pickClientOpt 與 WWebPerm 之 kpFunExt)
|
|
335
474
|
let updateUsers = async (userId, rows, opt = {}) => {
|
|
336
475
|
try {
|
|
337
476
|
await updateTabItems('users', userId, rows, 'id', opt)
|
|
@@ -382,7 +521,7 @@ export default proc
|
|
|
382
521
|
<br class="clear">
|
|
383
522
|
|
|
384
523
|
<footer>
|
|
385
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
524
|
+
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.
|
|
386
525
|
</footer>
|
|
387
526
|
|
|
388
527
|
<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#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#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WWebPerm.html">WWebPerm</a></li></ul><h3>Global</h3><ul><li><a href="global.html#applyPurgePlan">applyPurgePlan</a></li><li><a href="global.html#auditFrom">auditFrom</a></li><li><a href="global.html#auditRefs">auditRefs</a></li><li><a href="global.html#buildReport">buildReport</a></li><li><a href="global.html#buildRowsSend">buildRowsSend</a></li><li><a href="global.html#buildSyncRowsNew">buildSyncRowsNew</a></li><li><a href="global.html#checkBaseline">checkBaseline</a></li><li><a href="global.html#checkCascadePlan">checkCascadePlan</a></li><li><a href="global.html#checkDbNotInUse">checkDbNotInUse</a></li><li><a href="global.html#checkExternalWrite">checkExternalWrite</a></li><li><a href="global.html#checkMapKeyBatch">checkMapKeyBatch</a></li><li><a href="global.html#checkUsersWrite">checkUsersWrite</a></li><li><a href="global.html#cleanOutOf">cleanOutOf</a></li><li><a href="global.html#compareBaseline">compareBaseline</a></li><li><a href="global.html#countAndRefs">countAndRefs</a></li><li><a href="global.html#countExternalRows">countExternalRows</a></li><li><a href="global.html#countIdRenumber">countIdRenumber</a></li><li><a href="global.html#createAuthorizeExec">createAuthorizeExec</a></li><li><a href="global.html#createLegendMeasurer">createLegendMeasurer</a></li><li><a href="global.html#createLockSave">createLockSave</a></li><li><a href="global.html#cvKpToArr">cvKpToArr</a></li><li><a href="global.html#cvKpToObj">cvKpToObj</a></li><li><a href="global.html#decideLegendType">decideLegendType</a></li><li><a href="global.html#describeUpstream">describeUpstream</a></li><li><a href="global.html#diffRefTargets">diffRefTargets</a></li><li><a href="global.html#dropServerOnlyDiff">dropServerOnlyDiff</a></li><li><a href="global.html#eachOwn">eachOwn</a></li><li><a href="global.html#fetchJson">fetchJson</a></li><li><a href="global.html#fillIdlessIds">fillIdlessIds</a></li><li><a href="global.html#findDupIndex">findDupIndex</a></li><li><a href="global.html#findKeyOtherFrom">findKeyOtherFrom</a></li><li><a href="global.html#findRefRows">findRefRows</a></li><li><a href="global.html#findTouchedDup">findTouchedDup</a></li><li><a href="global.html#fmtLossyMsg">fmtLossyMsg</a></li><li><a href="global.html#fmtVars">fmtVars</a></li><li><a href="global.html#fmtWarn">fmtWarn</a></li><li><a href="global.html#formatFixReport">formatFixReport</a></li><li><a href="global.html#formatFromAudit">formatFromAudit</a></li><li><a href="global.html#formatPurgeReport">formatPurgeReport</a></li><li><a href="global.html#genLegend">genLegend</a></li><li><a href="global.html#getAddConfirmText">getAddConfirmText</a></li><li><a href="global.html#getAddImpact">getAddImpact</a></li><li><a href="global.html#getBannerLines">getBannerLines</a></li><li><a href="global.html#getBaselineDigest">getBaselineDigest</a></li><li><a href="global.html#getCascadePreview">getCascadePreview</a></li><li><a href="global.html#getChangedIdx">getChangedIdx</a></li><li><a href="global.html#getDelConfirmText">getDelConfirmText</a></li><li><a href="global.html#getDelImpact">getDelImpact</a></li><li><a href="global.html#getDelRefCount">getDelRefCount</a></li><li><a href="global.html#getDigestFields">getDigestFields</a></li><li><a href="global.html#getDupRowsInconsistent">getDupRowsInconsistent</a></li><li><a href="global.html#getEffMode">getEffMode</a></li><li><a href="global.html#getExternalEdits">getExternalEdits</a></li><li><a href="global.html#getItemWarnText">getItemWarnText</a></li><li><a href="global.html#getKpLossy">getKpLossy</a></li><li><a href="global.html#getLockFields">getLockFields</a></li><li><a href="global.html#getNameErrs">getNameErrs</a></li><li><a href="global.html#getOwn">getOwn</a></li><li><a href="global.html#getPropKey">getPropKey</a></li><li><a href="global.html#getRawModeMap">getRawModeMap</a></li><li><a href="global.html#getRefRowsExtra">getRefRowsExtra</a></li><li><a href="global.html#getRefState">getRefState</a></li><li><a href="global.html#getRefWarnText">getRefWarnText</a></li><li><a href="global.html#getRefWarns">getRefWarns</a></li><li><a href="global.html#getRowIdxs">getRowIdxs</a></li><li><a href="global.html#getRowState">getRowState</a></li><li><a href="global.html#getRuleEnable">getRuleEnable</a></li><li><a href="global.html#getRuleWarnText">getRuleWarnText</a></li><li><a href="global.html#getRuleWarns">getRuleWarns</a></li><li><a href="global.html#getSavePreview">getSavePreview</a></li><li><a href="global.html#getSyncWarnLines">getSyncWarnLines</a></li><li><a href="global.html#hasDupKey">hasDupKey</a></li><li><a href="global.html#hasExternalDiff">hasExternalDiff</a></li><li><a href="global.html#isAuthMode">isAuthMode</a></li><li><a href="global.html#isBaselineStale">isBaselineStale</a></li><li><a href="global.html#isBaselineValid">isBaselineValid</a></li><li><a href="global.html#isDraftStale">isDraftStale</a></li><li><a href="global.html#isExternalRow">isExternalRow</a></li><li><a href="global.html#isJsonLossy">isJsonLossy</a></li><li><a href="global.html#isMain">isMain</a></li><li><a href="global.html#joinList">joinList</a></li><li><a href="global.html#joinNames">joinNames</a></li><li><a href="global.html#keepAssignByEntity">keepAssignByEntity</a></li><li><a href="global.html#keepAssignOnMissing">keepAssignOnMissing</a></li><li><a href="global.html#makeBaseline">makeBaseline</a></li><li><a href="global.html#makeRowStates">makeRowStates</a></li><li><a href="global.html#mapKeyOf">mapKeyOf</a></li><li><a href="global.html#mapNameKp">mapNameKp</a></li><li><a href="global.html#markDataReload">markDataReload</a></li><li><a href="global.html#markHasKey">markHasKey</a></li><li><a href="global.html#maskQuery">maskQuery</a></li><li><a href="global.html#maskTok">maskTok</a></li><li><a href="global.html#maskUrl">maskUrl</a></li><li><a href="global.html#mergePickKeysOnly">mergePickKeysOnly</a></li><li><a href="global.html#pairRowsToSnapshot">pairRowsToSnapshot</a></li><li><a href="global.html#pickUniqueUser">pickUniqueUser</a></li><li><a href="global.html#planDraftSync">planDraftSync</a></li><li><a href="global.html#planFixFromType">planFixFromType</a></li><li><a href="global.html#planPurgeChain">planPurgeChain</a></li><li><a href="global.html#planPurgeFrom">planPurgeFrom</a></li><li><a href="global.html#planRefChanges">planRefChanges</a></li><li><a href="global.html#putRefEntry">putRefEntry</a></li><li><a href="global.html#putRowsToKp">putRowsToKp</a></li><li><a href="global.html#refsChanged">refsChanged</a></li><li><a href="global.html#resolveShellWorker">resolveShellWorker</a></li><li><a href="global.html#restoreExternalOrder">restoreExternalOrder</a></li><li><a href="global.html#restoreServerFields">restoreServerFields</a></li><li><a href="global.html#reuseDeletedIds">reuseDeletedIds</a></li><li><a href="global.html#rewriteRefs">rewriteRefs</a></li><li><a href="global.html#rowDigest">rowDigest</a></li><li><a href="global.html#runAuditFrom">runAuditFrom</a></li><li><a href="global.html#runClean">runClean</a></li><li><a href="global.html#runFixFromType">runFixFromType</a></li><li><a href="global.html#runPurgeFrom">runPurgeFrom</a></li><li><a href="global.html#splitCheckedRows">splitCheckedRows</a></li><li><a href="global.html#submitGuard">submitGuard</a></li><li><a href="global.html#tabWriteKey">tabWriteKey</a></li><li><a href="global.html#validateRowsBeforeWrite">validateRowsBeforeWrite</a></li><li><a href="global.html#withTabLocks">withTabLocks</a></li><li><a href="global.html#wrapInjected">wrapInjected</a></li><li><a href="global.html#wrapOperOrm">wrapOperOrm</a></li><li><a href="global.html#writeWithCascade">writeWithCascade</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -167,7 +167,7 @@ export default resolveShellWorker
|
|
|
167
167
|
<br class="clear">
|
|
168
168
|
|
|
169
169
|
<footer>
|
|
170
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
170
|
+
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.
|
|
171
171
|
</footer>
|
|
172
172
|
|
|
173
173
|
<script>prettyPrint();</script>
|