w-serv-orm 1.1.7 → 1.1.9

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.
@@ -75,15 +75,28 @@
75
75
 
76
76
  <dt class="tag-description">Description:</dt>
77
77
  <dd class="tag-description"><ul class="dummy"><li><p>產生伺服器ORM的支援物件</p>
78
- <p>procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵(無有效主鍵者列其於輸入內之位置),錯誤原文另由console.log輸出。
79
- 單筆失敗不中斷整批,故reject時其餘筆數已寫入;save與del皆以主鍵為準,數據帶有主鍵時整批重送不會產生重複資料,未帶主鍵者重送則會由ORM另產生主鍵而重複寫入。
80
- mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。</p></li></ul></dd>
78
+ <p>每次呼叫各自建立資料表ORM、設定與擴充函數,同一行程內之多個實例互不干擾;同一url與db之資料庫資源能否並存,依所用ORM而定。</p>
79
+ <p>procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵,如'failed to save data: id[b]'(超過100字元者截斷顯示;無有效主鍵者以data[k]列其於輸入內之位置,k自0起;超過10筆者只列前10筆並附總筆數),錯誤原文另由console.log輸出。
80
+ 單筆失敗不中斷整批,故reject時其餘筆數已寫入。
81
+ mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。</p>
82
+ <p>insert、save與mix之新增、修改,數據未帶有效主鍵(非空字串或數字)者,若資料表之主鍵欄為id且為字串型(或型別未知),由本套件產生主鍵(同各ORM之genIDSeq)並於resolve時回傳;無欄位之數據不補。
83
+ reject時不回傳補出之主鍵,故重送未帶主鍵之數據會再產生新主鍵而重複寫入,需冪等重送者請自帶主鍵;save帶主鍵者重送以主鍵覆寫,del重送時已刪除者為主鍵未命中。
84
+ insert(含mix之新增)為主鍵已存在即略過之冪等新增,回傳之id含被略過者;recover亦以insert還原,故資料表須先清空。
85
+ insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi之clearXSS清除(白名單外之標籤轉義,白名單內之標籤刪除白名單外之屬性與危險網址,標籤外之文字只轉義會開始標籤之&lt;,故'a &lt; b'等數學式不改寫);select與del不經清除。
86
+ 字串主鍵若會被清除改寫(含HTML標記,如&lt;後緊接英文字母),整批reject且不寫入任何一筆(mix於任何步驟之前檢查新增與修改),訊息列其位置而不回顯主鍵,如'failed to save data, id contains HTML markup: data[1]';否則改寫後之主鍵與呼叫端所持者不同,以原主鍵查詢與刪除皆找不到。</p>
87
+ <p>擴充函數(addFunCheck、addFunPreProcessing、addFunPostProcessing)屬於各實例,重建實例後須重新註冊;註冊時須傳入函數,否則立即拋錯(addFunCheck另令該實例之procOrm一律reject);註冊後約1毫秒(以計時器)才生效,在此之前之procOrm呼叫不會套用。
88
+ 呼叫契約:check以reject或拋錯阻擋,回傳值不影響結果,且早於前處理執行;前處理逐一呼叫,各收新建之參數物件{woName,userId,mode,input},input為前一個回傳之input,回傳須為含input之物件否則reject;後處理逐一呼叫,各收新建之參數物件且其output為前一個之回傳值,最後之回傳值即為procOrm之回傳值。
89
+ 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input為呼叫端傳入之數據,不含本套件補出之主鍵,主鍵見output。
90
+ 如需觀察ORM實際寫入,可另監聽woItems內各ORM之change與error事件;惟事件由ORM發出,不含userId,mix會依步驟分次發出,經woItems直接寫入者亦會發出,insert之結果為聚合計數而非逐筆。
91
+ 錯誤型別:procOrm由本套件產生之reject一律為英文訊息字串(供前端顯示或對應多語系;ORM之錯誤原文另由console.log輸出而不轉出),擴充函數自身之reject或拋錯則原樣轉出;建構(設定或ORM無效)、擴充函數註冊、backup與recover由本套件產生之錯誤一律為Error,ORM之錯誤則原樣轉出。</p>
92
+ <p>授權:procOrm不做授權,不檢查使用者,亦不依使用者過濾數據;userId只供寫入稽核欄位(userId、userIdUpdate)與交給擴充函數。procOrm開放給前端時(如讓前端經資料通道直接操作資料表),須由開放處負責授權;addFunCheck可掛額外檢查,惟伺服端程式之呼叫亦會經過。
93
+ 已移除之設定:useCheckUser、getUserById、useExcludeWhenNotAdmin,未給、null或false者照常建構,給予其他值即於建構時拋錯。</p></li></ul></dd>
81
94
 
82
95
 
83
96
 
84
97
  <dt class="tag-source">Source:</dt>
85
98
  <dd class="tag-source"><ul class="dummy"><li>
86
- <a href="WServOrm.mjs.html">WServOrm.mjs</a>, <a href="WServOrm.mjs.html#line86">line 86</a>
99
+ <a href="WServOrm.mjs.html">WServOrm.mjs</a>, <a href="WServOrm.mjs.html#line115">line 115</a>
87
100
  </li></ul></dd>
88
101
 
89
102
 
@@ -130,7 +143,7 @@ mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後
130
143
 
131
144
  <h5 class="h5-examples">Example</h5>
132
145
 
133
- <pre class="prettyprint"><code>// import WOrm from 'w-orm-mongodb/src/WOrmMongodb.mjs' //自行選擇引用ORM
134
146
  getUserById: null,
135
147
  useCheckUser: false,
136
148
  useExcludeWhenNotAdmin: false,
149
+ <pre class="prettyprint"><code>// import WOrm from 'w-orm-mongodb/src/WOrmMongodb.mjs' //自行選擇引用ORM
137
150
 
138
151
 
139
152
 
@@ -341,157 +354,7 @@ mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後
341
354
  </td>
342
355
 
343
356
 
344
- <td class="description last"><p>輸入設定物件,預設{}</p>
345
- <h6>Properties</h6>
346
-
347
-
348
- <table class="params">
349
- <thead>
350
- <tr>
351
-
352
- <th>Name</th>
353
-
354
-
355
- <th>Type</th>
356
-
357
-
358
- <th>Attributes</th>
359
-
360
-
361
-
362
- <th>Default</th>
363
-
364
-
365
- <th class="last">Description</th>
366
- </tr>
367
- </thead>
368
-
369
- <tbody>
370
-
371
-
372
- <tr>
373
-
374
- <td class="name"><code>useCheckUser</code></td>
375
-
376
-
377
- <td class="type">
378
-
379
-
380
- <span class="param-type">Boolean</span>
381
-
382
-
383
-
384
-
385
- </td>
386
-
387
-
388
- <td class="attributes">
389
-
390
- &lt;optional><br>
391
-
392
-
393
-
394
-
395
-
396
- </td>
397
-
398
-
399
-
400
- <td class="default">
401
-
402
- <code>true</code>
403
-
404
- </td>
405
-
406
-
407
- <td class="description last"><p>輸入是否檢查使用者資訊布林值,預設true</p></td>
408
- </tr>
409
-
410
-
411
-
412
- <tr>
413
-
414
- <td class="name"><code>getUserById</code></td>
415
-
416
-
417
- <td class="type">
418
-
419
-
420
- <span class="param-type">function</span>
421
-
422
-
423
-
424
-
425
- </td>
426
-
427
-
428
- <td class="attributes">
429
-
430
- &lt;optional><br>
431
-
432
-
433
-
434
-
435
-
436
- </td>
437
-
438
-
439
-
440
- <td class="default">
441
-
442
- <code>null</code>
443
-
444
- </td>
445
-
446
-
447
- <td class="description last"><p>輸入當useCheckUser=true時依照使用者ID取得使用者資訊物件函數,預設null</p></td>
448
- </tr>
449
-
450
-
451
-
452
- <tr>
453
-
454
- <td class="name"><code>useExcludeWhenNotAdmin</code></td>
455
-
456
-
457
- <td class="type">
458
-
459
-
460
- <span class="param-type">Boolean</span>
461
-
462
-
463
-
464
-
465
- </td>
466
-
467
-
468
- <td class="attributes">
469
-
470
- &lt;optional><br>
471
-
472
-
473
-
474
-
475
-
476
- </td>
477
-
478
-
479
-
480
- <td class="default">
481
-
482
- <code>true</code>
483
-
484
- </td>
485
-
486
-
487
- <td class="description last"><p>輸入使用ORM的select方法時是否自動刪除數據內isActive欄位之布林值,預設true</p></td>
488
- </tr>
489
-
490
-
491
- </tbody>
492
- </table>
493
-
494
- </td>
357
+ <td class="description last"><p>輸入設定物件,預設{};目前無設定鍵,保留供日後擴充,已移除之設定見上述</p></td>
495
358
  </tr>
496
359
 
497
360
 
@@ -517,7 +380,7 @@ mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後
517
380
 
518
381
 
519
382
  <div class="param-desc">
520
- <p>回傳通訊物件,可監聽事件open、error、clientChange、execute、broadcast、deliver,可使用函數broadcast</p>
383
+ <p>回傳伺服器ORM支援物件,含woItems(各資料表ORM實例)、procOrm(資料操作函數,輸入為userId、woName、mode、input)、addFunCheck、addFunPreProcessing、addFunPostProcessing、closeAll(逐一關閉各資料表ORM之連線),以及backup(woItems)與recover(fp, woItems, genModels, needCreateStorage);後兩者不綁定本實例,須自行傳入woItems(須為非空物件,否則reject),其參數順序與getDb之recover(woItems, fp, opt)不同;recover於寫入前驗證備份內容(須為非空陣列且各筆之name為woItems內之資料表),失敗時reject,逐表寫入時中途失敗則先前之表已寫入</p>
521
384
  </div>
522
385
 
523
386
 
@@ -576,7 +439,7 @@ mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後
576
439
  <br class="clear">
577
440
 
578
441
  <footer>
579
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 21:04:11 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
442
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Oct 13 2026 00:02:33 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
580
443
  </footer>
581
444
 
582
445
  <script>prettyPrint();</script>
@@ -45,7 +45,8 @@
45
45
 
46
46
  <section>
47
47
  <article>
48
- <pre class="prettyprint source linenums"><code>import pmSeries from 'wsemi/src/pmSeries.mjs'
48
+ <pre class="prettyprint source linenums"><code>import get from 'lodash-es/get.js'
49
+ import pmSeries from 'wsemi/src/pmSeries.mjs'
49
50
  import isfun from 'wsemi/src/isfun.mjs'
50
51
  import getDbBackupAndRecover from './getDbBackupAndRecover.mjs'
51
52
  import getWoItems from './getWoItems.mjs'
@@ -53,23 +54,56 @@ import getMapOrm from './getMapOrm.mjs'
53
54
  import getProc from './getProc.mjs'
54
55
 
55
56
 
57
+ function checkOptRemoved(opt) {
58
+ //已移除之設定: 未給、null或false者接受(使用端現行之設定), 其餘值拋錯, 避免使用端以為仍有檢查而無聲失去
59
+ //useCheckUser只查使用者存在且有效, 屬認證而非授權, 卻曾被當成授權; procOrm不做授權, 須由procOrm開放給前端之處負責
60
+ //useExcludeWhenNotAdmin依賴只有useCheckUser才產生之isAdmin, 未開useCheckUser時會把所有人當非管理者過濾, 一併移除
61
+ let msgAuth = 'procOrm does not authorize users, authorize where procOrm is exposed to clients'
62
+ let ks = [
63
+ ['useCheckUser', msgAuth],
64
+ ['getUserById', msgAuth],
65
+ ['useExcludeWhenNotAdmin', 'procOrm does not filter select results by user, use addFunPostProcessing if needed'],
66
+ ]
67
+ for (let [k, hint] of ks) {
68
+ let v = get(opt, k)
69
+ if (v !== undefined &amp;&amp; v !== null &amp;&amp; v !== false) {
70
+ throw new Error(`opt.${k} has been removed, ${hint}`)
71
+ }
72
+ }
73
+ }
74
+
75
+
56
76
  /**
57
77
  * 產生伺服器ORM的支援物件
58
78
  *
59
- * procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵(無有效主鍵者列其於輸入內之位置),錯誤原文另由console.log輸出。
60
- * 單筆失敗不中斷整批,故reject時其餘筆數已寫入;save與del皆以主鍵為準,數據帶有主鍵時整批重送不會產生重複資料,未帶主鍵者重送則會由ORM另產生主鍵而重複寫入。
79
+ * 每次呼叫各自建立資料表ORM、設定與擴充函數,同一行程內之多個實例互不干擾;同一url與db之資料庫資源能否並存,依所用ORM而定。
80
+ *
81
+ * procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵,如'failed to save data: id[b]'(超過100字元者截斷顯示;無有效主鍵者以data[k]列其於輸入內之位置,k自0起;超過10筆者只列前10筆並附總筆數),錯誤原文另由console.log輸出。
82
+ * 單筆失敗不中斷整批,故reject時其餘筆數已寫入。
61
83
  * mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。
62
84
  *
85
+ * insert、save與mix之新增、修改,數據未帶有效主鍵(非空字串或數字)者,若資料表之主鍵欄為id且為字串型(或型別未知),由本套件產生主鍵(同各ORM之genIDSeq)並於resolve時回傳;無欄位之數據不補。
86
+ * reject時不回傳補出之主鍵,故重送未帶主鍵之數據會再產生新主鍵而重複寫入,需冪等重送者請自帶主鍵;save帶主鍵者重送以主鍵覆寫,del重送時已刪除者為主鍵未命中。
87
+ * insert(含mix之新增)為主鍵已存在即略過之冪等新增,回傳之id含被略過者;recover亦以insert還原,故資料表須先清空。
88
+ * insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi之clearXSS清除(白名單外之標籤轉義,白名單內之標籤刪除白名單外之屬性與危險網址,標籤外之文字只轉義會開始標籤之&lt;,故'a &lt; b'等數學式不改寫);select與del不經清除。
89
+ * 字串主鍵若會被清除改寫(含HTML標記,如&lt;後緊接英文字母),整批reject且不寫入任何一筆(mix於任何步驟之前檢查新增與修改),訊息列其位置而不回顯主鍵,如'failed to save data, id contains HTML markup: data[1]';否則改寫後之主鍵與呼叫端所持者不同,以原主鍵查詢與刪除皆找不到。
90
+ *
91
+ * 擴充函數(addFunCheck、addFunPreProcessing、addFunPostProcessing)屬於各實例,重建實例後須重新註冊;註冊時須傳入函數,否則立即拋錯(addFunCheck另令該實例之procOrm一律reject);註冊後約1毫秒(以計時器)才生效,在此之前之procOrm呼叫不會套用。
92
+ * 呼叫契約:check以reject或拋錯阻擋,回傳值不影響結果,且早於前處理執行;前處理逐一呼叫,各收新建之參數物件{woName,userId,mode,input},input為前一個回傳之input,回傳須為含input之物件否則reject;後處理逐一呼叫,各收新建之參數物件且其output為前一個之回傳值,最後之回傳值即為procOrm之回傳值。
93
+ * 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input為呼叫端傳入之數據,不含本套件補出之主鍵,主鍵見output。
94
+ * 如需觀察ORM實際寫入,可另監聽woItems內各ORM之change與error事件;惟事件由ORM發出,不含userId,mix會依步驟分次發出,經woItems直接寫入者亦會發出,insert之結果為聚合計數而非逐筆。
95
+ * 錯誤型別:procOrm由本套件產生之reject一律為英文訊息字串(供前端顯示或對應多語系;ORM之錯誤原文另由console.log輸出而不轉出),擴充函數自身之reject或拋錯則原樣轉出;建構(設定或ORM無效)、擴充函數註冊、backup與recover由本套件產生之錯誤一律為Error,ORM之錯誤則原樣轉出。
96
+ *
97
+ * 授權:procOrm不做授權,不檢查使用者,亦不依使用者過濾數據;userId只供寫入稽核欄位(userId、userIdUpdate)與交給擴充函數。procOrm開放給前端時(如讓前端經資料通道直接操作資料表),須由開放處負責授權;addFunCheck可掛額外檢查,惟伺服端程式之呼叫亦會經過。
98
+ * 已移除之設定:useCheckUser、getUserById、useExcludeWhenNotAdmin,未給、null或false者照常建構,給予其他值即於建構時拋錯。
99
+ *
63
100
  * @class
64
101
  * @param {Object} ds 輸入資料表設定物件
65
102
  * @param {Object} WOrm 輸入資料庫ORM物件
66
103
  * @param {String} url 輸入資料庫連線位址字串
67
104
  * @param {String} db 輸入資料庫名稱字串
68
- * @param {Object} [opt={}] 輸入設定物件,預設{}
69
- * @param {Boolean} [opt.useCheckUser=true] 輸入是否檢查使用者資訊布林值,預設true
70
- * @param {Function} [opt.getUserById=null] 輸入當useCheckUser=true時依照使用者ID取得使用者資訊物件函數,預設null
71
- * @param {Boolean} [opt.useExcludeWhenNotAdmin=true] 輸入使用ORM的select方法時是否自動刪除數據內isActive欄位之布林值,預設true
72
- * @returns {Object} 回傳通訊物件,可監聽事件open、error、clientChange、execute、broadcast、deliver,可使用函數broadcast
105
+ * @param {Object} [opt={}] 輸入設定物件,預設{};目前無設定鍵,保留供日後擴充,已移除之設定見上述
106
+ * @returns {Object} 回傳伺服器ORM支援物件,含woItems(各資料表ORM實例)、procOrm(資料操作函數,輸入為userId、woName、mode、input)、addFunCheck、addFunPreProcessing、addFunPostProcessing、closeAll(逐一關閉各資料表ORM之連線),以及backup(woItems)與recover(fp, woItems, genModels, needCreateStorage);後兩者不綁定本實例,須自行傳入woItems(須為非空物件,否則reject),其參數順序與getDb之recover(woItems, fp, opt)不同;recover於寫入前驗證備份內容(須為非空陣列且各筆之name為woItems內之資料表),失敗時reject,逐表寫入時中途失敗則先前之表已寫入
73
107
  * @example
74
108
  *
75
109
  * // import WOrm from 'w-orm-mongodb/src/WOrmMongodb.mjs' //自行選擇引用ORM
@@ -93,12 +127,7 @@ import getProc from './getProc.mjs'
93
127
  * let db = 'worm'
94
128
  *
95
129
  * //WServOrm
96
- * let opt = {
97
- * getUserById: null,
98
- * useCheckUser: false,
99
- * useExcludeWhenNotAdmin: false,
100
- * }
101
- * let r = WServOrm(ds, WOrm, url, db, opt)
130
+ * let r = WServOrm(ds, WOrm, url, db)
102
131
  * console.log(r) //回傳server用orm相關函數
103
132
  * // => {
104
133
  * // backup: [AsyncFunction: backup],
@@ -132,6 +161,9 @@ import getProc from './getProc.mjs'
132
161
  */
133
162
  function WServOrm(ds, WOrm, url, db, opt = {}) {
134
163
 
164
+ //checkOptRemoved, 須於建立各資料表ORM之前, 否則拋錯時已開啟之ORM(如lmdb之檔案)無從關閉
165
+ checkOptRemoved(opt)
166
+
135
167
  //getWoItems
136
168
  let woItems = getWoItems(ds, WOrm, url, db)
137
169
  // console.log('woItems', woItems)
@@ -141,7 +173,7 @@ function WServOrm(ds, WOrm, url, db, opt = {}) {
141
173
  // console.log('mapOrm', mapOrm)
142
174
 
143
175
  //getProc
144
- let proc = getProc(mapOrm, opt)
176
+ let proc = getProc(mapOrm)
145
177
  // console.log('proc', proc)
146
178
 
147
179
  //closeAll, 逐一關閉woItems內各資料表ORM實例之連線(env), 釋放檔案鎖, 供測試隔離或優雅關機使用
@@ -178,7 +210,7 @@ export default WServOrm
178
210
  <br class="clear">
179
211
 
180
212
  <footer>
181
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 21:04:11 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
213
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Oct 13 2026 00:02:33 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
182
214
  </footer>
183
215
 
184
216
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 21:04:11 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
74
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Oct 13 2026 00:02:33 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>
package/g-mOrm.mjs CHANGED
@@ -20,12 +20,7 @@ let url = './db.json'
20
20
  let db = 'worm'
21
21
 
22
22
  //WServOrm
23
- let opt = {
24
- getUserById: null,
25
- useCheckUser: false,
26
- useExcludeWhenNotAdmin: false,
27
- }
28
- let r = WServOrm(ds, WOrm, url, db, opt)
23
+ let r = WServOrm(ds, WOrm, url, db)
29
24
  console.log(r) //回傳server用orm相關函數
30
25
  // => {
31
26
  // backup: [AsyncFunction: backup],
@@ -47,6 +42,7 @@ console.log(r) //回傳server用orm相關函數
47
42
  // [Symbol(kCapture)]: false
48
43
  // }
49
44
  // },
45
+ // closeAll: [AsyncFunction: closeAll],
50
46
  // addFunCheck: [Function: addFunCheck],
51
47
  // addFunPreProcessing: [Function: addFunPreProcessing],
52
48
  // addFunPostProcessing: [Function: addFunPostProcessing],
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "w-serv-orm",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "main": "dist/w-serv-orm.umd.js",
5
5
  "dependencies": {
6
6
  "json5": "^2.2.3",
7
- "wsemi": "^1.9.12"
7
+ "w-data-collector": "^1.0.46",
8
+ "wsemi": "^1.9.13"
8
9
  },
9
10
  "devDependencies": {
10
- "w-data-collector": "^1.0.43",
11
11
  "w-orm-lowdb": "^1.0.31",
12
12
  "w-package-tools": "^1.1.16"
13
13
  },
@@ -60,7 +60,7 @@ let funTest = () => {
60
60
  v = dtpick(v, keys(settings))
61
61
  return v
62
62
  })
63
- console.log(`已產生: {keyTable} 測試資料`, rs)
63
+ console.log(`generated test data for table[${keyTable}]`, rs)
64
64
  return rs
65
65
  }
66
66
 
package/src/WServOrm.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import get from 'lodash-es/get.js'
1
2
  import pmSeries from 'wsemi/src/pmSeries.mjs'
2
3
  import isfun from 'wsemi/src/isfun.mjs'
3
4
  import getDbBackupAndRecover from './getDbBackupAndRecover.mjs'
@@ -6,23 +7,56 @@ import getMapOrm from './getMapOrm.mjs'
6
7
  import getProc from './getProc.mjs'
7
8
 
8
9
 
10
+ function checkOptRemoved(opt) {
11
+ //已移除之設定: 未給、null或false者接受(使用端現行之設定), 其餘值拋錯, 避免使用端以為仍有檢查而無聲失去
12
+ //useCheckUser只查使用者存在且有效, 屬認證而非授權, 卻曾被當成授權; procOrm不做授權, 須由procOrm開放給前端之處負責
13
+ //useExcludeWhenNotAdmin依賴只有useCheckUser才產生之isAdmin, 未開useCheckUser時會把所有人當非管理者過濾, 一併移除
14
+ let msgAuth = 'procOrm does not authorize users, authorize where procOrm is exposed to clients'
15
+ let ks = [
16
+ ['useCheckUser', msgAuth],
17
+ ['getUserById', msgAuth],
18
+ ['useExcludeWhenNotAdmin', 'procOrm does not filter select results by user, use addFunPostProcessing if needed'],
19
+ ]
20
+ for (let [k, hint] of ks) {
21
+ let v = get(opt, k)
22
+ if (v !== undefined && v !== null && v !== false) {
23
+ throw new Error(`opt.${k} has been removed, ${hint}`)
24
+ }
25
+ }
26
+ }
27
+
28
+
9
29
  /**
10
30
  * 產生伺服器ORM的支援物件
11
31
  *
12
- * procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵(無有效主鍵者列其於輸入內之位置),錯誤原文另由console.log輸出。
13
- * 單筆失敗不中斷整批,故reject時其餘筆數已寫入;save與del皆以主鍵為準,數據帶有主鍵時整批重送不會產生重複資料,未帶主鍵者重送則會由ORM另產生主鍵而重複寫入。
32
+ * 每次呼叫各自建立資料表ORM、設定與擴充函數,同一行程內之多個實例互不干擾;同一url與db之資料庫資源能否並存,依所用ORM而定。
33
+ *
34
+ * procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵,如'failed to save data: id[b]'(超過100字元者截斷顯示;無有效主鍵者以data[k]列其於輸入內之位置,k自0起;超過10筆者只列前10筆並附總筆數),錯誤原文另由console.log輸出。
35
+ * 單筆失敗不中斷整批,故reject時其餘筆數已寫入。
14
36
  * mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。
15
37
  *
38
+ * insert、save與mix之新增、修改,數據未帶有效主鍵(非空字串或數字)者,若資料表之主鍵欄為id且為字串型(或型別未知),由本套件產生主鍵(同各ORM之genIDSeq)並於resolve時回傳;無欄位之數據不補。
39
+ * reject時不回傳補出之主鍵,故重送未帶主鍵之數據會再產生新主鍵而重複寫入,需冪等重送者請自帶主鍵;save帶主鍵者重送以主鍵覆寫,del重送時已刪除者為主鍵未命中。
40
+ * insert(含mix之新增)為主鍵已存在即略過之冪等新增,回傳之id含被略過者;recover亦以insert還原,故資料表須先清空。
41
+ * insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi之clearXSS清除(白名單外之標籤轉義,白名單內之標籤刪除白名單外之屬性與危險網址,標籤外之文字只轉義會開始標籤之<,故'a < b'等數學式不改寫);select與del不經清除。
42
+ * 字串主鍵若會被清除改寫(含HTML標記,如<後緊接英文字母),整批reject且不寫入任何一筆(mix於任何步驟之前檢查新增與修改),訊息列其位置而不回顯主鍵,如'failed to save data, id contains HTML markup: data[1]';否則改寫後之主鍵與呼叫端所持者不同,以原主鍵查詢與刪除皆找不到。
43
+ *
44
+ * 擴充函數(addFunCheck、addFunPreProcessing、addFunPostProcessing)屬於各實例,重建實例後須重新註冊;註冊時須傳入函數,否則立即拋錯(addFunCheck另令該實例之procOrm一律reject);註冊後約1毫秒(以計時器)才生效,在此之前之procOrm呼叫不會套用。
45
+ * 呼叫契約:check以reject或拋錯阻擋,回傳值不影響結果,且早於前處理執行;前處理逐一呼叫,各收新建之參數物件{woName,userId,mode,input},input為前一個回傳之input,回傳須為含input之物件否則reject;後處理逐一呼叫,各收新建之參數物件且其output為前一個之回傳值,最後之回傳值即為procOrm之回傳值。
46
+ * 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input為呼叫端傳入之數據,不含本套件補出之主鍵,主鍵見output。
47
+ * 如需觀察ORM實際寫入,可另監聽woItems內各ORM之change與error事件;惟事件由ORM發出,不含userId,mix會依步驟分次發出,經woItems直接寫入者亦會發出,insert之結果為聚合計數而非逐筆。
48
+ * 錯誤型別:procOrm由本套件產生之reject一律為英文訊息字串(供前端顯示或對應多語系;ORM之錯誤原文另由console.log輸出而不轉出),擴充函數自身之reject或拋錯則原樣轉出;建構(設定或ORM無效)、擴充函數註冊、backup與recover由本套件產生之錯誤一律為Error,ORM之錯誤則原樣轉出。
49
+ *
50
+ * 授權:procOrm不做授權,不檢查使用者,亦不依使用者過濾數據;userId只供寫入稽核欄位(userId、userIdUpdate)與交給擴充函數。procOrm開放給前端時(如讓前端經資料通道直接操作資料表),須由開放處負責授權;addFunCheck可掛額外檢查,惟伺服端程式之呼叫亦會經過。
51
+ * 已移除之設定:useCheckUser、getUserById、useExcludeWhenNotAdmin,未給、null或false者照常建構,給予其他值即於建構時拋錯。
52
+ *
16
53
  * @class
17
54
  * @param {Object} ds 輸入資料表設定物件
18
55
  * @param {Object} WOrm 輸入資料庫ORM物件
19
56
  * @param {String} url 輸入資料庫連線位址字串
20
57
  * @param {String} db 輸入資料庫名稱字串
21
- * @param {Object} [opt={}] 輸入設定物件,預設{}
22
- * @param {Boolean} [opt.useCheckUser=true] 輸入是否檢查使用者資訊布林值,預設true
23
- * @param {Function} [opt.getUserById=null] 輸入當useCheckUser=true時依照使用者ID取得使用者資訊物件函數,預設null
24
- * @param {Boolean} [opt.useExcludeWhenNotAdmin=true] 輸入使用ORM的select方法時是否自動刪除數據內isActive欄位之布林值,預設true
25
- * @returns {Object} 回傳通訊物件,可監聽事件open、error、clientChange、execute、broadcast、deliver,可使用函數broadcast
58
+ * @param {Object} [opt={}] 輸入設定物件,預設{};目前無設定鍵,保留供日後擴充,已移除之設定見上述
59
+ * @returns {Object} 回傳伺服器ORM支援物件,含woItems(各資料表ORM實例)、procOrm(資料操作函數,輸入為userId、woName、mode、input)、addFunCheck、addFunPreProcessing、addFunPostProcessing、closeAll(逐一關閉各資料表ORM之連線),以及backup(woItems)與recover(fp, woItems, genModels, needCreateStorage);後兩者不綁定本實例,須自行傳入woItems(須為非空物件,否則reject),其參數順序與getDb之recover(woItems, fp, opt)不同;recover於寫入前驗證備份內容(須為非空陣列且各筆之name為woItems內之資料表),失敗時reject,逐表寫入時中途失敗則先前之表已寫入
26
60
  * @example
27
61
  *
28
62
  * // import WOrm from 'w-orm-mongodb/src/WOrmMongodb.mjs' //自行選擇引用ORM
@@ -46,12 +80,7 @@ import getProc from './getProc.mjs'
46
80
  * let db = 'worm'
47
81
  *
48
82
  * //WServOrm
49
- * let opt = {
50
- * getUserById: null,
51
- * useCheckUser: false,
52
- * useExcludeWhenNotAdmin: false,
53
- * }
54
- * let r = WServOrm(ds, WOrm, url, db, opt)
83
+ * let r = WServOrm(ds, WOrm, url, db)
55
84
  * console.log(r) //回傳server用orm相關函數
56
85
  * // => {
57
86
  * // backup: [AsyncFunction: backup],
@@ -85,6 +114,9 @@ import getProc from './getProc.mjs'
85
114
  */
86
115
  function WServOrm(ds, WOrm, url, db, opt = {}) {
87
116
 
117
+ //checkOptRemoved, 須於建立各資料表ORM之前, 否則拋錯時已開啟之ORM(如lmdb之檔案)無從關閉
118
+ checkOptRemoved(opt)
119
+
88
120
  //getWoItems
89
121
  let woItems = getWoItems(ds, WOrm, url, db)
90
122
  // console.log('woItems', woItems)
@@ -94,7 +126,7 @@ function WServOrm(ds, WOrm, url, db, opt = {}) {
94
126
  // console.log('mapOrm', mapOrm)
95
127
 
96
128
  //getProc
97
- let proc = getProc(mapOrm, opt)
129
+ let proc = getProc(mapOrm)
98
130
  // console.log('proc', proc)
99
131
 
100
132
  //closeAll, 逐一關閉woItems內各資料表ORM實例之連線(env), 釋放檔案鎖, 供測試隔離或優雅關機使用
@@ -3,7 +3,7 @@ import size from 'lodash-es/size.js'
3
3
  import isbol from 'wsemi/src/isbol.mjs'
4
4
  import isfun from 'wsemi/src/isfun.mjs'
5
5
  import pmSeries from 'wsemi/src/pmSeries.mjs'
6
- import getItemsFail from './getItemsFail.mjs'
6
+ import getItemsFail from 'w-data-collector/src/getItemsFail.mjs'
7
7
 
8
8
 
9
9
  async function genTestData(ds, woItems, opt = {}) {
@@ -51,7 +51,7 @@ async function genTestData(ds, woItems, opt = {}) {
51
51
  //check, 單筆失敗時整批仍resolve並以該筆ok為0回報, 須逐筆檢查
52
52
  let fails = getItemsFail(rss, rs)
53
53
  if (size(fails) > 0) {
54
- throw new Error(`資料表[${k}]寫入測試資料失敗: ${JSON.stringify(fails)}`)
54
+ throw new Error(`failed to write test data into table[${k}]: ${JSON.stringify(fails)}`)
55
55
  }
56
56
 
57
57
  if (bLog) {
package/src/getDb.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs'
3
- import get from 'lodash-es/get.js'
4
- import each from 'lodash-es/each.js'
5
- import keys from 'lodash-es/keys.js'
3
+ import get from 'lodash-es/get.js'
4
+ import each from 'lodash-es/each.js'
5
+ import keys from 'lodash-es/keys.js'
6
6
  import iseobj from 'wsemi/src/iseobj.mjs'
7
7
  import isbol from 'wsemi/src/isbol.mjs'
8
8
  import isestr from 'wsemi/src/isestr.mjs'
9
+ import isfun from 'wsemi/src/isfun.mjs'
9
10
  import o2j from 'wsemi/src/o2j.mjs'
10
11
  import now2strp from 'wsemi/src/now2strp.mjs'
11
12
  import haskey from 'wsemi/src/haskey.mjs'
@@ -38,13 +39,13 @@ function genModels(ds, genModelsByTabs, opt = {}) {
38
39
 
39
40
  //check
40
41
  if (!haskey(ds[name], 'settings')) {
41
- console.log(`資料表[${name}]無settings資訊`)
42
+ console.log(`table[${name}] has no settings`)
42
43
  return true //跳出換下一個
43
44
  }
44
45
 
45
46
  //check
46
47
  if (!iseobj(ds[name].settings)) {
47
- console.log(`ds[${name}].settings非有效物件`)
48
+ console.log(`invalid ds[${name}].settings`)
48
49
  return true //跳出換下一個
49
50
  }
50
51
 
@@ -66,6 +67,7 @@ function genModels(ds, genModelsByTabs, opt = {}) {
66
67
 
67
68
 
68
69
  async function backup(woItems, opt = {}) {
70
+ //備份各資料表至cwd(模組載入時之工作目錄)下之backup-時間.json; 失敗時印log後reject原錯誤, 令呼叫端(含未await之頂層腳本)可偵測
69
71
 
70
72
  //bLog
71
73
  let bLog = get(opt, 'bLog')
@@ -80,6 +82,11 @@ async function backup(woItems, opt = {}) {
80
82
  //o2j
81
83
  let j = o2j(res)
82
84
 
85
+ //check, o2j無法序列化時回空字串, 不得寫出空備份而回報成功
86
+ if (!isestr(j)) {
87
+ throw new Error('backup data can not be serialized')
88
+ }
89
+
83
90
  //fn
84
91
  let fn = `backup-${now2strp()}.json`
85
92
 
@@ -95,12 +102,15 @@ async function backup(woItems, opt = {}) {
95
102
  })
96
103
  .catch((err) => {
97
104
  console.log(err)
105
+ throw err
98
106
  })
99
107
 
100
108
  }
101
109
 
102
110
 
103
111
  async function recover(woItems, fp, opt = {}) {
112
+ //由備份檔以insert還原各資料表; 主鍵已存在者略過, 故資料表須先清空; 逐表寫入, 中途失敗時先前之表已寫入
113
+ //失敗時印log後reject原錯誤; bRela為true時須提供opt.ds與opt.genModelsByTabs以產生model
104
114
 
105
115
  //bRela, 是否為關聯資料庫
106
116
  let bRela = get(opt, 'bRela')
@@ -115,10 +125,20 @@ async function recover(woItems, fp, opt = {}) {
115
125
  }
116
126
 
117
127
  //funGenModels and bNeedCreateStorage, 若為關聯資料庫則需使用genModels與needCreateStorage=true
128
+ //genModels須以ds與genModelsByTabs產生model, 低階recover則以無參數呼叫, 故以閉包帶入; 缺則於任何寫入之前reject
118
129
  let funGenModels = null
119
130
  let bNeedCreateStorage = false
120
131
  if (bRela) {
121
- funGenModels = genModels
132
+ let ds = get(opt, 'ds')
133
+ let genModelsByTabs = get(opt, 'genModelsByTabs')
134
+ if (!iseobj(ds) || !isfun(genModelsByTabs)) {
135
+ let err = new Error('bRela needs opt.ds and opt.genModelsByTabs')
136
+ console.log(err)
137
+ return Promise.reject(err)
138
+ }
139
+ funGenModels = () => {
140
+ genModels(ds, genModelsByTabs, opt)
141
+ }
122
142
  bNeedCreateStorage = true
123
143
  }
124
144
 
@@ -131,6 +151,7 @@ async function recover(woItems, fp, opt = {}) {
131
151
  })
132
152
  .catch((err) => {
133
153
  console.log(err)
154
+ throw err
134
155
  })
135
156
 
136
157
  }