w-orm-lowdb 1.0.35 → 1.0.37
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 +28 -2
- package/dist/w-orm-lowdb.umd.js +2 -2
- package/dist/w-orm-lowdb.umd.js.map +1 -1
- package/docs/WOrmLowdb.html +27 -14
- package/docs/WOrmLowdb.mjs.html +192 -81
- package/docs/index.html +1 -1
- package/package.json +2 -2
- package/src/WOrmLowdb.mjs +191 -80
- package/test/unit-protokey.test.mjs +346 -0
- package/test/unit-wellformed.test.mjs +628 -0
- package/test/unit-write-pre-error.test.mjs +443 -0
package/docs/WOrmLowdb.html
CHANGED
|
@@ -88,17 +88,30 @@ mode為操作別字串,可為'select'、'selectByPk'、'insert'、'insertBulk'
|
|
|
88
88
|
data為本次操作之輸入數據,無輸入數據者為null。
|
|
89
89
|
err為錯誤訊息字串,內容與正規管道所送出者一致。
|
|
90
90
|
整批性錯誤於reject之前發出;逐筆失敗於該筆結果定案後發出,每筆一次。
|
|
91
|
+
insert、insertBulk、save與del因輸入本身導致之失敗(如原型為null之Date使複製拋錯、存取器拋錯)亦屬整批性錯誤,同樣於reject之前恰發出一次、不發出change事件、資料庫不變;
|
|
92
|
+
拋錯於複製建立之前者,data為呼叫端傳入之原值,其後者為複製;err為拋出者之原訊息(呼叫端自行拋出之值或訊息原樣通過,無法轉為字串者以固定訊息unknown error取代),本套件不另行回顯數據內容。
|
|
91
93
|
註: 逐筆失敗時整批仍resolve,故收到本事件不表示該次呼叫失敗;正常結果(如查無數據、主鍵未命中、全數已存在)不發出本事件。</p>
|
|
92
94
|
<p>註: 本套件之主鍵欄位固定為id,尚未支援指定其他欄位為主鍵。
|
|
93
95
|
註: 本套件之寫入為[整檔讀出、記憶體修改、整檔寫回],其原子性由行程內之序列化佇列提供,
|
|
94
96
|
故僅單一行程內併發成立,跨行程併發不成立,詳見README之併發保證宣告。
|
|
95
|
-
註: 數據以JSON存檔,Date存入後為ISO字串;insert、insertBulk與save寫入之Date年份須介於0000-9999,範圍外(如new Date(8.64e15))整批reject,詳見各函數與README。</p
|
|
97
|
+
註: 數據以JSON存檔,Date存入後為ISO字串;insert、insertBulk與save寫入之Date年份須介於0000-9999,範圍外(如new Date(8.64e15))整批reject,詳見各函數與README。</p>
|
|
98
|
+
<p>字串良構(w-orm系列規則,wsemi之chkOrmStr與chkOrmData): lowdb以JSON檔存放,孤立代理字元(String.prototype.isWellFormed()為false)可保真,本身不致相撞,
|
|
99
|
+
惟系列其他後端(UTF-8、BSON儲存)會改寫為U+FFFD,為使更換後端時資料行為一致,全系列一律於任何讀取與寫入之前拒收:</p>
|
|
100
|
+
<ol>
|
|
101
|
+
<li>主鍵(id)不得含孤立代理字元與NUL,不隨autoGenPk改變;</li>
|
|
102
|
+
<li>寫入資料(insert、insertBulk、save)之字串值與鍵名(含巢狀物件、陣列元素、Map與Set之項目、Error與RegExp之文字)不得含孤立代理字元,值與鍵名之NUL不拒;</li>
|
|
103
|
+
<li>opt.url、opt.db、opt.cl含孤立代理字元者於建構時throw,訊息為invalid opt.<名>: string contains lone surrogate。
|
|
104
|
+
處置與無效主鍵同形:insert與insertBulk整批reject而不寫入任何一筆(error事件先於reject),save與del該筆ok為0並附err(同批其他筆照常),selectByPk回null;
|
|
105
|
+
訊息為固定詞彙而不回顯值:invalid id: 原因 at [data[k].id]、invalid data: 原因 at [data[k].路徑](k為批次索引,單一物件為0;不合格之鍵名以<invalid key>標示),
|
|
106
|
+
原因為string contains lone surrogate、string contains NUL(僅主鍵)、not a string或string is empty(del之id未給、非字串或空字串)。
|
|
107
|
+
U+FFFD本身與成對代理(emoji等)為良構,照常接受。既有資料不遷移,檢出與清除法見README。</li>
|
|
108
|
+
</ol></li></ul></dd>
|
|
96
109
|
|
|
97
110
|
|
|
98
111
|
|
|
99
112
|
<dt class="tag-source">Source:</dt>
|
|
100
113
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
101
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
114
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line77">line 77</a>
|
|
102
115
|
</li></ul></dd>
|
|
103
116
|
|
|
104
117
|
|
|
@@ -270,7 +283,7 @@ err為錯誤訊息字串,內容與正規管道所送出者一致。
|
|
|
270
283
|
</td>
|
|
271
284
|
|
|
272
285
|
|
|
273
|
-
<td class="description last"><p>輸入資料庫位置字串,預設'./db.json'</p></td>
|
|
286
|
+
<td class="description last"><p>輸入資料庫位置字串,預設'./db.json';含孤立代理字元者建構時throw</p></td>
|
|
274
287
|
</tr>
|
|
275
288
|
|
|
276
289
|
|
|
@@ -310,7 +323,7 @@ err為錯誤訊息字串,內容與正規管道所送出者一致。
|
|
|
310
323
|
</td>
|
|
311
324
|
|
|
312
325
|
|
|
313
|
-
<td class="description last"><p>輸入使用資料庫名稱字串,預設'worm'</p></td>
|
|
326
|
+
<td class="description last"><p>輸入使用資料庫名稱字串,預設'worm';含孤立代理字元者建構時throw</p></td>
|
|
314
327
|
</tr>
|
|
315
328
|
|
|
316
329
|
|
|
@@ -350,7 +363,7 @@ err為錯誤訊息字串,內容與正規管道所送出者一致。
|
|
|
350
363
|
</td>
|
|
351
364
|
|
|
352
365
|
|
|
353
|
-
<td class="description last"><p>輸入使用資料表名稱字串,預設'test'</p></td>
|
|
366
|
+
<td class="description last"><p>輸入使用資料表名稱字串,預設'test';含孤立代理字元者建構時throw</p></td>
|
|
354
367
|
</tr>
|
|
355
368
|
|
|
356
369
|
|
|
@@ -552,7 +565,7 @@ Date條件於本後端轉為ISO字串後以字典序比較(與字串型欄位亦
|
|
|
552
565
|
|
|
553
566
|
<dt class="tag-source">Source:</dt>
|
|
554
567
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
555
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
568
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line1289">line 1289</a>
|
|
556
569
|
</li></ul></dd>
|
|
557
570
|
|
|
558
571
|
|
|
@@ -730,7 +743,7 @@ Date條件於本後端轉為ISO字串後以字典序比較(與字串型欄位亦
|
|
|
730
743
|
|
|
731
744
|
<dt class="tag-source">Source:</dt>
|
|
732
745
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
733
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
746
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line1103">line 1103</a>
|
|
734
747
|
</li></ul></dd>
|
|
735
748
|
|
|
736
749
|
|
|
@@ -896,7 +909,7 @@ Date條件於本後端轉為ISO字串後以字典序比較(與字串型欄位亦
|
|
|
896
909
|
|
|
897
910
|
<dt class="tag-source">Source:</dt>
|
|
898
911
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
899
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
912
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line774">line 774</a>
|
|
900
913
|
</li></ul></dd>
|
|
901
914
|
|
|
902
915
|
|
|
@@ -1061,7 +1074,7 @@ Date條件於本後端轉為ISO字串後以字典序比較(與字串型欄位亦
|
|
|
1061
1074
|
|
|
1062
1075
|
<dt class="tag-source">Source:</dt>
|
|
1063
1076
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
1064
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
1077
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line632">line 632</a>
|
|
1065
1078
|
</li></ul></dd>
|
|
1066
1079
|
|
|
1067
1080
|
|
|
@@ -1350,7 +1363,7 @@ autoInsert為false而主鍵不存在(不會寫入)者亦先經同一檢核;合
|
|
|
1350
1363
|
|
|
1351
1364
|
<dt class="tag-source">Source:</dt>
|
|
1352
1365
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
1353
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
1366
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line884">line 884</a>
|
|
1354
1367
|
</li></ul></dd>
|
|
1355
1368
|
|
|
1356
1369
|
|
|
@@ -1640,7 +1653,7 @@ autoInsert為false而主鍵不存在(不會寫入)者亦先經同一檢核;合
|
|
|
1640
1653
|
|
|
1641
1654
|
<dt class="tag-source">Source:</dt>
|
|
1642
1655
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
1643
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
1656
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line568">line 568</a>
|
|
1644
1657
|
</li></ul></dd>
|
|
1645
1658
|
|
|
1646
1659
|
|
|
@@ -1800,7 +1813,7 @@ autoInsert為false而主鍵不存在(不會寫入)者亦先經同一檢核;合
|
|
|
1800
1813
|
|
|
1801
1814
|
<dt class="tag-source">Source:</dt>
|
|
1802
1815
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
1803
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
1816
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line500">line 500</a>
|
|
1804
1817
|
</li></ul></dd>
|
|
1805
1818
|
|
|
1806
1819
|
|
|
@@ -1985,7 +1998,7 @@ select亦納入序列化,令其不致與寫入之整檔替換交錯。</p>
|
|
|
1985
1998
|
|
|
1986
1999
|
<dt class="tag-source">Source:</dt>
|
|
1987
2000
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
1988
|
-
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#
|
|
2001
|
+
<a href="WOrmLowdb.mjs.html">WOrmLowdb.mjs</a>, <a href="WOrmLowdb.mjs.html#line164">line 164</a>
|
|
1989
2002
|
</li></ul></dd>
|
|
1990
2003
|
|
|
1991
2004
|
|
|
@@ -2069,7 +2082,7 @@ select亦納入序列化,令其不致與寫入之整檔替換交錯。</p>
|
|
|
2069
2082
|
<br class="clear">
|
|
2070
2083
|
|
|
2071
2084
|
<footer>
|
|
2072
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
2085
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 21 2026 00:43:38 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
2073
2086
|
</footer>
|
|
2074
2087
|
|
|
2075
2088
|
<script>prettyPrint();</script>
|
package/docs/WOrmLowdb.mjs.html
CHANGED
|
@@ -67,6 +67,8 @@ import iseobj from 'wsemi/src/iseobj.mjs'
|
|
|
67
67
|
import isearr from 'wsemi/src/isearr.mjs'
|
|
68
68
|
import haskey from 'wsemi/src/haskey.mjs'
|
|
69
69
|
import cvOrmFind from 'wsemi/src/cvOrmFind.mjs'
|
|
70
|
+
import chkOrmStr from 'wsemi/src/chkOrmStr.mjs'
|
|
71
|
+
import chkOrmData from 'wsemi/src/chkOrmData.mjs'
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
//kpGqueue, 以資料庫檔案路徑為鍵之全域序列化佇列, 詳見下方serialize說明
|
|
@@ -91,6 +93,8 @@ let kpGqueue = {}
|
|
|
91
93
|
* data為本次操作之輸入數據,無輸入數據者為null。
|
|
92
94
|
* err為錯誤訊息字串,內容與正規管道所送出者一致。
|
|
93
95
|
* 整批性錯誤於reject之前發出;逐筆失敗於該筆結果定案後發出,每筆一次。
|
|
96
|
+
* insert、insertBulk、save與del因輸入本身導致之失敗(如原型為null之Date使複製拋錯、存取器拋錯)亦屬整批性錯誤,同樣於reject之前恰發出一次、不發出change事件、資料庫不變;
|
|
97
|
+
* 拋錯於複製建立之前者,data為呼叫端傳入之原值,其後者為複製;err為拋出者之原訊息(呼叫端自行拋出之值或訊息原樣通過,無法轉為字串者以固定訊息unknown error取代),本套件不另行回顯數據內容。
|
|
94
98
|
* 註: 逐筆失敗時整批仍resolve,故收到本事件不表示該次呼叫失敗;正常結果(如查無數據、主鍵未命中、全數已存在)不發出本事件。
|
|
95
99
|
*
|
|
96
100
|
* 註: 本套件之主鍵欄位固定為id,尚未支援指定其他欄位為主鍵。
|
|
@@ -98,11 +102,21 @@ let kpGqueue = {}
|
|
|
98
102
|
* 故僅單一行程內併發成立,跨行程併發不成立,詳見README之併發保證宣告。
|
|
99
103
|
* 註: 數據以JSON存檔,Date存入後為ISO字串;insert、insertBulk與save寫入之Date年份須介於0000-9999,範圍外(如new Date(8.64e15))整批reject,詳見各函數與README。
|
|
100
104
|
*
|
|
105
|
+
* 字串良構(w-orm系列規則,wsemi之chkOrmStr與chkOrmData): lowdb以JSON檔存放,孤立代理字元(String.prototype.isWellFormed()為false)可保真,本身不致相撞,
|
|
106
|
+
* 惟系列其他後端(UTF-8、BSON儲存)會改寫為U+FFFD,為使更換後端時資料行為一致,全系列一律於任何讀取與寫入之前拒收:
|
|
107
|
+
* 1. 主鍵(id)不得含孤立代理字元與NUL,不隨autoGenPk改變;
|
|
108
|
+
* 2. 寫入資料(insert、insertBulk、save)之字串值與鍵名(含巢狀物件、陣列元素、Map與Set之項目、Error與RegExp之文字)不得含孤立代理字元,值與鍵名之NUL不拒;
|
|
109
|
+
* 3. opt.url、opt.db、opt.cl含孤立代理字元者於建構時throw,訊息為invalid opt.<名>: string contains lone surrogate。
|
|
110
|
+
* 處置與無效主鍵同形:insert與insertBulk整批reject而不寫入任何一筆(error事件先於reject),save與del該筆ok為0並附err(同批其他筆照常),selectByPk回null;
|
|
111
|
+
* 訊息為固定詞彙而不回顯值:invalid id: 原因 at [data[k].id]、invalid data: 原因 at [data[k].路徑](k為批次索引,單一物件為0;不合格之鍵名以<invalid key>標示),
|
|
112
|
+
* 原因為string contains lone surrogate、string contains NUL(僅主鍵)、not a string或string is empty(del之id未給、非字串或空字串)。
|
|
113
|
+
* U+FFFD本身與成對代理(emoji等)為良構,照常接受。既有資料不遷移,檢出與清除法見README。
|
|
114
|
+
*
|
|
101
115
|
* @class
|
|
102
116
|
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
103
|
-
* @param {String} [opt.url='./db.json'] 輸入資料庫位置字串,預設'./db.json'
|
|
104
|
-
* @param {String} [opt.db='worm'] 輸入使用資料庫名稱字串,預設'worm'
|
|
105
|
-
* @param {String} [opt.cl='test'] 輸入使用資料表名稱字串,預設'test'
|
|
117
|
+
* @param {String} [opt.url='./db.json'] 輸入資料庫位置字串,預設'./db.json';含孤立代理字元者建構時throw
|
|
118
|
+
* @param {String} [opt.db='worm'] 輸入使用資料庫名稱字串,預設'worm';含孤立代理字元者建構時throw
|
|
119
|
+
* @param {String} [opt.cl='test'] 輸入使用資料表名稱字串,預設'test';含孤立代理字元者建構時throw
|
|
106
120
|
* @param {Boolean} [opt.useCache=false] 輸入是否使用select快取,適用於單程序操作,預設false
|
|
107
121
|
* @param {Boolean} [opt.autoGenPk=true] 輸入insert、insertBulk與save於數據未帶有效主鍵(本套件為id欄位)時是否自動產生主鍵值,預設true;給false代表主鍵改由呼叫端自備,套件不產生亦不檢查其唯一性與格式,未帶有效主鍵者將reject
|
|
108
122
|
* @returns {Object} 回傳操作資料庫物件,各事件功能詳見說明
|
|
@@ -112,23 +126,42 @@ function WOrmLowdb(opt = {}) {
|
|
|
112
126
|
//_cache
|
|
113
127
|
let _cache = null
|
|
114
128
|
|
|
129
|
+
//chkOptStr, 儲存位址名稱(opt.url、opt.db、opt.cl)之良構檢核, 孤立代理字元者於建構時同步throw, 先於建立任何檔案或讀寫:
|
|
130
|
+
//url為檔案路徑, 經檔案系統UTF-8化後孤立代理被改寫為U+FFFD而落到他檔; db與cl雖為JSON內之鍵可保真, 全系列仍一律拒收(同一應用更換後端時行為一致);
|
|
131
|
+
//名稱不檢NUL(opt.nul為false), 訊息不回顯值
|
|
132
|
+
let chkOptStr = (name, v) => {
|
|
133
|
+
let r = chkOrmStr(v, { nul: false })
|
|
134
|
+
if (r !== null) {
|
|
135
|
+
throw new Error(`invalid opt.${name}: ${r}`)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
115
139
|
//url
|
|
116
140
|
let url = get(opt, 'url')
|
|
117
141
|
if (!isestr(url)) {
|
|
118
142
|
url = './db.json'
|
|
119
143
|
}
|
|
144
|
+
else {
|
|
145
|
+
chkOptStr('url', url)
|
|
146
|
+
}
|
|
120
147
|
|
|
121
148
|
//db
|
|
122
149
|
let db = get(opt, 'db')
|
|
123
150
|
if (!isestr(db)) {
|
|
124
151
|
db = 'worm'
|
|
125
152
|
}
|
|
153
|
+
else {
|
|
154
|
+
chkOptStr('db', db)
|
|
155
|
+
}
|
|
126
156
|
|
|
127
157
|
//cl
|
|
128
158
|
let cl = get(opt, 'cl')
|
|
129
159
|
if (!isestr(cl)) {
|
|
130
160
|
cl = 'test'
|
|
131
161
|
}
|
|
162
|
+
else {
|
|
163
|
+
chkOptStr('cl', cl)
|
|
164
|
+
}
|
|
132
165
|
|
|
133
166
|
//useCache
|
|
134
167
|
let useCache = get(opt, 'useCache')
|
|
@@ -357,9 +390,17 @@ function WOrmLowdb(opt = {}) {
|
|
|
357
390
|
}
|
|
358
391
|
|
|
359
392
|
//emitError, 操作發生錯誤時發出, 錯誤訊息一律轉為字串
|
|
393
|
+
//拋出之值本身可能無法轉為字串(如呼叫端之存取器拋出null原型物件, 或其message存取器拋錯), 此時以固定訊息取代, 令error事件仍恰發出一次(帳本R-WRITE-ERR-01), reject之值不變
|
|
360
394
|
let emitError = (mode, data, err) => {
|
|
395
|
+
let msg = 'unknown error'
|
|
396
|
+
try {
|
|
397
|
+
msg = getErrMsg(err)
|
|
398
|
+
}
|
|
399
|
+
catch (errMsg) {
|
|
400
|
+
//無法轉為字串者沿用固定訊息
|
|
401
|
+
}
|
|
361
402
|
try {
|
|
362
|
-
ee.emit('error', mode, data,
|
|
403
|
+
ee.emit('error', mode, data, msg)
|
|
363
404
|
}
|
|
364
405
|
catch (errEmit) {
|
|
365
406
|
console.log(errEmit)
|
|
@@ -395,6 +436,45 @@ function WOrmLowdb(opt = {}) {
|
|
|
395
436
|
return data
|
|
396
437
|
}
|
|
397
438
|
|
|
439
|
+
//chkPk, 主鍵之檢核, 回傳null(合格)或固定原因字串(不回顯值), 供寫入前(procWellFormed)、selectByPk與del共用:
|
|
440
|
+
//空字串非有效主鍵(同isestr); 其餘依wsemi之chkOrmStr: 非字串、含NUL(主鍵併入NUL, 與find同標準)、含孤立代理字元
|
|
441
|
+
//(lowdb以JSON存檔可保真, 但全系列一律拒收, 使同一應用更換後端時資料行為一致; 孤立代理字元之主鍵於其他後端會被改寫為U+FFFD而命中他筆)
|
|
442
|
+
let chkPk = (id) => {
|
|
443
|
+
if (id === '') {
|
|
444
|
+
return 'string is empty'
|
|
445
|
+
}
|
|
446
|
+
return chkOrmStr(id)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
//getWellFormedMsg, 檢核第k筆之主鍵與資料, 回傳null(合格)或錯誤訊息; 訊息為固定詞彙而不回顯值, 路徑含批次索引(單一物件呼叫之索引為data[0]),
|
|
450
|
+
//與wsemi之cvOrmFind及案例表同詞彙:
|
|
451
|
+
// invalid id: <原因> at [data[<k>].id]
|
|
452
|
+
// invalid data: <原因> at [data[<k>].<路徑>]
|
|
453
|
+
//1.作用於procPk之後(已cloneDeep並補主鍵)之複本, 且其後之判定、寫入皆只用該複本, 檢查之值即使用之值; 自有存取器於cloneDeep時已轉為資料屬性
|
|
454
|
+
//2.同一筆主鍵先於資料; 資料(chkOrmData)檢鍵名與字串值(含巢狀物件、陣列、Map、Set、Error與RegExp之文字)之孤立代理字元, 值與鍵名之NUL不拒
|
|
455
|
+
//3.不隨autoGenPk改變: 如實存放性檢核, 非格式偏好
|
|
456
|
+
let getWellFormedMsg = (v, k) => {
|
|
457
|
+
let r = chkPk(v.id)
|
|
458
|
+
if (r !== null) {
|
|
459
|
+
return `invalid id: ${r} at [data[${k}].id]`
|
|
460
|
+
}
|
|
461
|
+
let e = chkOrmData(v, { root: `data[${k}]` })
|
|
462
|
+
if (e !== null) {
|
|
463
|
+
return `invalid data: ${e.reason} at [${e.path}]`
|
|
464
|
+
}
|
|
465
|
+
return null
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
//procWellFormed, 供insert與insertBulk於任何寫入之前檢核各筆: 任一筆不合格即拋出為整批性錯誤(以索引最小者為準)而不寫入任何一筆
|
|
469
|
+
let procWellFormed = (data) => {
|
|
470
|
+
each(data, function(v, k) {
|
|
471
|
+
let msg = getWellFormedMsg(v, k)
|
|
472
|
+
if (msg !== null) {
|
|
473
|
+
throw new Error(msg)
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
398
478
|
//readData, 讀取資料庫檔案並取得本資料表之陣列
|
|
399
479
|
//註: 使用lowdb.read()初始化後須馬上檢測, 若無key則須先創建空陣列
|
|
400
480
|
let readData = async() => {
|
|
@@ -419,14 +499,15 @@ function WOrmLowdb(opt = {}) {
|
|
|
419
499
|
}
|
|
420
500
|
}
|
|
421
501
|
|
|
422
|
-
//getKpPk, 由數據陣列建立[主鍵值 => {k, v}]
|
|
502
|
+
//getKpPk, 由數據陣列建立[主鍵值 => {k, v}]之Map, k為所在索引
|
|
423
503
|
//僅收錄具有效主鍵值者, 令資料表內若有主鍵值無效之殘留數據不致被誤配為命中
|
|
504
|
+
//須用Map而非一般物件加in(haskey)或lodash get: 主鍵為constructor、toString、__proto__等Object.prototype成員名時, 走原型鏈恆有值(帳本R-PKMAP-01)
|
|
424
505
|
let getKpPk = (ltdt) => {
|
|
425
|
-
let kp =
|
|
506
|
+
let kp = new Map()
|
|
426
507
|
each(ltdt, (v, k) => {
|
|
427
508
|
let id = get(v, 'id')
|
|
428
509
|
if (isestr(id)) {
|
|
429
|
-
kp
|
|
510
|
+
kp.set(id, { k, v })
|
|
430
511
|
}
|
|
431
512
|
})
|
|
432
513
|
return kp
|
|
@@ -538,8 +619,8 @@ function WOrmLowdb(opt = {}) {
|
|
|
538
619
|
let res = null
|
|
539
620
|
try {
|
|
540
621
|
|
|
541
|
-
//check
|
|
542
|
-
if (!isestr(pk)) {
|
|
622
|
+
//check, 含NUL或孤立代理字元之主鍵亦屬無效(chkPk): 此類主鍵不可能由本套件寫入(寫入側已拒收), 回null而不reject, 不發出error事件
|
|
623
|
+
if (!isestr(pk) || chkPk(pk) !== null) {
|
|
543
624
|
//未給有效主鍵值視為查無數據
|
|
544
625
|
return null
|
|
545
626
|
}
|
|
@@ -549,7 +630,7 @@ function WOrmLowdb(opt = {}) {
|
|
|
549
630
|
|
|
550
631
|
//查找資料表內pk, 判定基準與insert、insertBulk、save、del內對既有數據之認定一致(皆以id之字串值比對)
|
|
551
632
|
let kp = getKpPk(ltdt)
|
|
552
|
-
let r = get(
|
|
633
|
+
let r = kp.get(pk)
|
|
553
634
|
|
|
554
635
|
//check
|
|
555
636
|
if (iseobj(r)) {
|
|
@@ -598,31 +679,31 @@ function WOrmLowdb(opt = {}) {
|
|
|
598
679
|
async function insertCore(data, option = {}) {
|
|
599
680
|
let isErr = false
|
|
600
681
|
|
|
601
|
-
//
|
|
602
|
-
let
|
|
603
|
-
|
|
604
|
-
returnList = false
|
|
605
|
-
}
|
|
682
|
+
//res
|
|
683
|
+
let res = null
|
|
684
|
+
try {
|
|
606
685
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (returnList) {
|
|
610
|
-
|
|
611
|
-
return []
|
|
686
|
+
//returnList, 讀取呼叫端傳入之option、判定data與cloneDeep皆可能因輸入而拋錯, 故一律置於try內(帳本R-WRITE-ERR-01)
|
|
687
|
+
let returnList = get(option, 'returnList')
|
|
688
|
+
if (!isbol(returnList)) {
|
|
689
|
+
returnList = false
|
|
612
690
|
}
|
|
613
|
-
return {
|
|
614
|
-
n: 0,
|
|
615
|
-
nInserted: 0,
|
|
616
|
-
ok: 1,
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
691
|
|
|
620
|
-
|
|
621
|
-
|
|
692
|
+
//check, 輸入無效者直接回傳, 不經catch之後之收尾且不發事件
|
|
693
|
+
if (!iseobj(data) && !isearr(data)) {
|
|
694
|
+
if (returnList) {
|
|
695
|
+
//輸入無效, 對齊save與del之空陣列規定
|
|
696
|
+
return []
|
|
697
|
+
}
|
|
698
|
+
return {
|
|
699
|
+
n: 0,
|
|
700
|
+
nInserted: 0,
|
|
701
|
+
ok: 1,
|
|
702
|
+
}
|
|
703
|
+
}
|
|
622
704
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
try {
|
|
705
|
+
//cloneDeep, 與外部數據脫勾, 其拋錯者(如原型為null之Date)亦走同一錯誤路徑(error事件恰一次後reject)
|
|
706
|
+
data = cloneDeep(data)
|
|
626
707
|
|
|
627
708
|
//check
|
|
628
709
|
if (!isarr(data)) {
|
|
@@ -635,6 +716,10 @@ function WOrmLowdb(opt = {}) {
|
|
|
635
716
|
//check Date, 複本內任何深度之Date年份須介於0000-9999, 範圍外整批reject(帳本R-DATE-01)
|
|
636
717
|
chkDataDate(data)
|
|
637
718
|
|
|
719
|
+
//check well-formed, 主鍵(併入NUL)與資料之孤立代理字元, 須於任何讀取與寫入之前完成, 只用上方cloneDeep之複本;
|
|
720
|
+
//置於Date檢核之後: 兩者並存時先報Date(既有行為與測試不變)
|
|
721
|
+
procWellFormed(data)
|
|
722
|
+
|
|
638
723
|
//read
|
|
639
724
|
let arrOld = await readData()
|
|
640
725
|
|
|
@@ -648,14 +733,14 @@ function WOrmLowdb(opt = {}) {
|
|
|
648
733
|
each(data, (v) => {
|
|
649
734
|
|
|
650
735
|
//check
|
|
651
|
-
if (!
|
|
736
|
+
if (!kp.has(v.id)) {
|
|
652
737
|
//未存在v.id
|
|
653
738
|
|
|
654
739
|
//push
|
|
655
740
|
ltpush.push(v)
|
|
656
741
|
|
|
657
742
|
//update, 須同步更新kp, 否則同批含重複主鍵者會全數寫入而出現重複主鍵
|
|
658
|
-
kp
|
|
743
|
+
kp.set(v.id, { k: -1, v })
|
|
659
744
|
|
|
660
745
|
ltdone.push(true)
|
|
661
746
|
}
|
|
@@ -736,22 +821,22 @@ function WOrmLowdb(opt = {}) {
|
|
|
736
821
|
async function insertBulkCore(data) {
|
|
737
822
|
let isErr = false
|
|
738
823
|
|
|
739
|
-
//check
|
|
740
|
-
if (!iseobj(data) && !isearr(data)) {
|
|
741
|
-
return {
|
|
742
|
-
n: 0,
|
|
743
|
-
nInserted: 0,
|
|
744
|
-
ok: 1,
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
//cloneDeep, 與外部數據脫勾
|
|
749
|
-
data = cloneDeep(data)
|
|
750
|
-
|
|
751
824
|
//res
|
|
752
825
|
let res = null
|
|
753
826
|
try {
|
|
754
827
|
|
|
828
|
+
//check, 判定data與cloneDeep皆可能因輸入而拋錯, 故一律置於try內(帳本R-WRITE-ERR-01); 輸入無效者直接回傳, 不經catch之後之收尾且不發事件
|
|
829
|
+
if (!iseobj(data) && !isearr(data)) {
|
|
830
|
+
return {
|
|
831
|
+
n: 0,
|
|
832
|
+
nInserted: 0,
|
|
833
|
+
ok: 1,
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
//cloneDeep, 與外部數據脫勾, 其拋錯者(如原型為null之Date)亦走同一錯誤路徑(error事件恰一次後reject)
|
|
838
|
+
data = cloneDeep(data)
|
|
839
|
+
|
|
755
840
|
//check
|
|
756
841
|
if (!isarr(data)) {
|
|
757
842
|
data = [data]
|
|
@@ -763,6 +848,9 @@ function WOrmLowdb(opt = {}) {
|
|
|
763
848
|
//check Date, 複本內任何深度之Date年份須介於0000-9999, 範圍外整批reject(帳本R-DATE-01)
|
|
764
849
|
chkDataDate(data)
|
|
765
850
|
|
|
851
|
+
//check well-formed, 同insert(置於Date檢核之後)
|
|
852
|
+
procWellFormed(data)
|
|
853
|
+
|
|
766
854
|
//read
|
|
767
855
|
let arrOld = await readData()
|
|
768
856
|
|
|
@@ -776,11 +864,11 @@ function WOrmLowdb(opt = {}) {
|
|
|
776
864
|
//同批含重複主鍵者亦於此被偵測為衝突(kp隨檢查同步更新)
|
|
777
865
|
let pkConflict = null
|
|
778
866
|
each(data, (v) => {
|
|
779
|
-
if (
|
|
867
|
+
if (kp.has(v.id)) {
|
|
780
868
|
pkConflict = v.id
|
|
781
869
|
return false //中止each
|
|
782
870
|
}
|
|
783
|
-
kp
|
|
871
|
+
kp.set(v.id, { k: -1, v })
|
|
784
872
|
})
|
|
785
873
|
|
|
786
874
|
//check
|
|
@@ -843,21 +931,21 @@ function WOrmLowdb(opt = {}) {
|
|
|
843
931
|
async function saveCore(data, option = {}) {
|
|
844
932
|
let isErr = false
|
|
845
933
|
|
|
846
|
-
//check
|
|
847
|
-
if (!iseobj(data) && !isearr(data)) {
|
|
848
|
-
return []
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
//cloneDeep, 與外部數據脫勾
|
|
852
|
-
data = cloneDeep(data)
|
|
853
|
-
|
|
854
|
-
//autoInsert
|
|
855
|
-
let autoInsert = get(option, 'autoInsert', true)
|
|
856
|
-
|
|
857
934
|
//res
|
|
858
935
|
let res = null
|
|
859
936
|
try {
|
|
860
937
|
|
|
938
|
+
//check, 判定data、cloneDeep與讀取呼叫端傳入之option皆可能因輸入而拋錯, 故一律置於try內(帳本R-WRITE-ERR-01); 輸入無效者直接回傳, 不經catch之後之收尾且不發事件
|
|
939
|
+
if (!iseobj(data) && !isearr(data)) {
|
|
940
|
+
return []
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
//cloneDeep, 與外部數據脫勾, 其拋錯者(如原型為null之Date)亦走同一錯誤路徑(error事件恰一次後reject)
|
|
944
|
+
data = cloneDeep(data)
|
|
945
|
+
|
|
946
|
+
//autoInsert
|
|
947
|
+
let autoInsert = get(option, 'autoInsert', true)
|
|
948
|
+
|
|
861
949
|
//check
|
|
862
950
|
if (!isarr(data)) {
|
|
863
951
|
data = [data]
|
|
@@ -869,6 +957,12 @@ function WOrmLowdb(opt = {}) {
|
|
|
869
957
|
//check Date, 複本內任何深度之Date年份須介於0000-9999, 範圍外整批reject(帳本R-DATE-01); 合併與覆寫路徑之Date皆來自此複本
|
|
870
958
|
chkDataDate(data)
|
|
871
959
|
|
|
960
|
+
//msgs, 各筆主鍵(併入NUL)與資料之良構檢核結果(合格者為null), 於任何讀取與寫入之前完成, 只用上方cloneDeep之複本;
|
|
961
|
+
//save為逐筆函數, 不合格者僅該筆失敗(ok為0並附err), 同批其他筆照常處理
|
|
962
|
+
let msgs = map(data, function(v, k) {
|
|
963
|
+
return getWellFormedMsg(v, k)
|
|
964
|
+
})
|
|
965
|
+
|
|
872
966
|
//read
|
|
873
967
|
let arrOld = await readData()
|
|
874
968
|
|
|
@@ -878,17 +972,22 @@ function WOrmLowdb(opt = {}) {
|
|
|
878
972
|
//kp
|
|
879
973
|
let kp = getKpPk(arrNew)
|
|
880
974
|
|
|
881
|
-
//pmSeries
|
|
975
|
+
//pmSeries, 回呼之第二參數為批次索引
|
|
882
976
|
let b = false
|
|
883
|
-
res = await pmSeries(data, async(v) => {
|
|
977
|
+
res = await pmSeries(data, async(v, k) => {
|
|
884
978
|
|
|
885
979
|
//rest
|
|
886
980
|
let rest = null
|
|
887
981
|
|
|
888
982
|
try {
|
|
889
983
|
|
|
984
|
+
//不合格者以該筆失敗回報(下方catch組成ok為0之結果), 先於該筆之任何讀取與寫入
|
|
985
|
+
if (msgs[k] !== null) {
|
|
986
|
+
throw new Error(msgs[k])
|
|
987
|
+
}
|
|
988
|
+
|
|
890
989
|
//查找資料表內v.id
|
|
891
|
-
let r = get(
|
|
990
|
+
let r = kp.get(v.id)
|
|
892
991
|
|
|
893
992
|
//existed
|
|
894
993
|
let existed = iseobj(r)
|
|
@@ -900,8 +999,17 @@ function WOrmLowdb(opt = {}) {
|
|
|
900
999
|
//vv, 現值
|
|
901
1000
|
let vv = r.v
|
|
902
1001
|
|
|
1002
|
+
//vIn, 以merge(lodash)讀取v一次所得之純資料物件, 檢核之並以之合併: merge以keysIn取值, 會讀到繼承之可列舉存取器(類別實例之原型上),
|
|
1003
|
+
//其每次讀取可回不同值, 上方對複本之檢核與此處合併所讀者未必相同, 故檢查之值即合併之值; 不合格者該筆ok為0(尚未寫入arrNew)
|
|
1004
|
+
//只檢核來自輸入者(vIn), 不檢核現值vv之既有內容: 舊資料之孤立代理字元不遷移(lowdb保真而留存), 不得使對該列之無關更新被拒
|
|
1005
|
+
let vIn = merge({}, v)
|
|
1006
|
+
let eIn = chkOrmData(vIn, { root: `data[${k}]` })
|
|
1007
|
+
if (eIn !== null) {
|
|
1008
|
+
throw new Error(`invalid data: ${eIn.reason} at [${eIn.path}]`)
|
|
1009
|
+
}
|
|
1010
|
+
|
|
903
1011
|
//vm, 合併後之結果, 另建新物件以免污染原數據
|
|
904
|
-
let vm = merge({}, vv,
|
|
1012
|
+
let vm = merge({}, vv, vIn)
|
|
905
1013
|
|
|
906
1014
|
//判定基準為[合併後結果與現值相同], 而非[待寫入物件與現值全等],
|
|
907
1015
|
//令nModified忠實反映是否真的寫入, 僅給部份欄位且值皆相同者不應回報已修改
|
|
@@ -919,7 +1027,7 @@ function WOrmLowdb(opt = {}) {
|
|
|
919
1027
|
|
|
920
1028
|
//update
|
|
921
1029
|
arrNew[r.k] = vm
|
|
922
|
-
kp
|
|
1030
|
+
kp.set(v.id, { k: r.k, v: vm })
|
|
923
1031
|
|
|
924
1032
|
b = true
|
|
925
1033
|
|
|
@@ -939,7 +1047,7 @@ function WOrmLowdb(opt = {}) {
|
|
|
939
1047
|
//push
|
|
940
1048
|
let k = size(arrNew)
|
|
941
1049
|
arrNew.push(v)
|
|
942
|
-
kp
|
|
1050
|
+
kp.set(v.id, { k, v })
|
|
943
1051
|
|
|
944
1052
|
b = true
|
|
945
1053
|
|
|
@@ -1042,18 +1150,18 @@ function WOrmLowdb(opt = {}) {
|
|
|
1042
1150
|
async function delCore(data) {
|
|
1043
1151
|
let isErr = false
|
|
1044
1152
|
|
|
1045
|
-
//check
|
|
1046
|
-
if (!iseobj(data) && !isearr(data)) {
|
|
1047
|
-
return []
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
//cloneDeep, 與外部數據脫勾
|
|
1051
|
-
data = cloneDeep(data)
|
|
1052
|
-
|
|
1053
1153
|
//res
|
|
1054
1154
|
let res = null
|
|
1055
1155
|
try {
|
|
1056
1156
|
|
|
1157
|
+
//check, 判定data與cloneDeep皆可能因輸入而拋錯, 故一律置於try內(帳本R-WRITE-ERR-01); 輸入無效者直接回傳, 不經catch之後之收尾且不發事件
|
|
1158
|
+
if (!iseobj(data) && !isearr(data)) {
|
|
1159
|
+
return []
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
//cloneDeep, 與外部數據脫勾, 其拋錯者(如原型為null之Date)亦走同一錯誤路徑(error事件恰一次後reject)
|
|
1163
|
+
data = cloneDeep(data)
|
|
1164
|
+
|
|
1057
1165
|
//check
|
|
1058
1166
|
if (!isarr(data)) {
|
|
1059
1167
|
data = [data]
|
|
@@ -1065,10 +1173,10 @@ function WOrmLowdb(opt = {}) {
|
|
|
1065
1173
|
//kp
|
|
1066
1174
|
let kp = getKpPk(arrOld)
|
|
1067
1175
|
|
|
1068
|
-
//pmSeries
|
|
1176
|
+
//pmSeries, 回呼之第二參數為批次索引
|
|
1069
1177
|
let kpDel = {}
|
|
1070
1178
|
let b = false
|
|
1071
|
-
res = await pmSeries(data, async(v) => {
|
|
1179
|
+
res = await pmSeries(data, async(v, k) => {
|
|
1072
1180
|
|
|
1073
1181
|
//rest
|
|
1074
1182
|
let rest = null
|
|
@@ -1076,24 +1184,27 @@ function WOrmLowdb(opt = {}) {
|
|
|
1076
1184
|
try {
|
|
1077
1185
|
|
|
1078
1186
|
//id
|
|
1079
|
-
let id = get(v, 'id'
|
|
1187
|
+
let id = get(v, 'id')
|
|
1188
|
+
|
|
1189
|
+
//check, 主鍵之檢核(chkPk): 非字串、空字串、含NUL、含孤立代理字元者皆無效
|
|
1190
|
+
let msgPk = chkPk(id)
|
|
1080
1191
|
|
|
1081
1192
|
//check
|
|
1082
|
-
if (
|
|
1193
|
+
if (msgPk !== null) {
|
|
1083
1194
|
//未給有效v.id視為該筆數據有問題而無法處理, 以ok為0並附err回報,
|
|
1084
|
-
//與[已給v.id但查無數據]之ok為1有別, 二者須可由ok
|
|
1195
|
+
//與[已給v.id但查無數據]之ok為1有別, 二者須可由ok分辨; 訊息為固定詞彙, 含批次索引而不回顯值
|
|
1085
1196
|
//註: 亦不可將無效主鍵送進查詢條件, 故於此直接定案
|
|
1086
1197
|
rest = {
|
|
1087
1198
|
n: 0,
|
|
1088
1199
|
nDeleted: 0,
|
|
1089
1200
|
ok: 0,
|
|
1090
|
-
err: `
|
|
1201
|
+
err: `invalid id: ${msgPk} at [data[${k}].id]`,
|
|
1091
1202
|
}
|
|
1092
1203
|
}
|
|
1093
1204
|
else {
|
|
1094
1205
|
|
|
1095
1206
|
//查找資料表內v.id
|
|
1096
|
-
let r = get(
|
|
1207
|
+
let r = kp.get(id)
|
|
1097
1208
|
|
|
1098
1209
|
//check
|
|
1099
1210
|
if (iseobj(r)) {
|
|
@@ -1103,7 +1214,7 @@ function WOrmLowdb(opt = {}) {
|
|
|
1103
1214
|
kpDel[r.k] = true
|
|
1104
1215
|
|
|
1105
1216
|
//update, 須同步移除, 否則同批重複主鍵者會重複計入nDeleted
|
|
1106
|
-
delete
|
|
1217
|
+
kp.delete(id)
|
|
1107
1218
|
|
|
1108
1219
|
b = true
|
|
1109
1220
|
|
|
@@ -1340,7 +1451,7 @@ export default WOrmLowdb
|
|
|
1340
1451
|
<br class="clear">
|
|
1341
1452
|
|
|
1342
1453
|
<footer>
|
|
1343
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
1454
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 21 2026 00:43:38 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
1344
1455
|
</footer>
|
|
1345
1456
|
|
|
1346
1457
|
<script>prettyPrint();</script>
|