w-orm-postgresql 1.0.10 → 1.0.12

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.
@@ -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="WOrmPostgresql.html">WOrmPostgresql</a><ul class='methods'><li data-type='method'><a href="WOrmPostgresql.html#.createTable">createTable</a></li><li data-type='method'><a href="WOrmPostgresql.html#.del">del</a></li><li data-type='method'><a href="WOrmPostgresql.html#.delAll">delAll</a></li><li data-type='method'><a href="WOrmPostgresql.html#.insert">insert</a></li><li data-type='method'><a href="WOrmPostgresql.html#.save">save</a></li><li data-type='method'><a href="WOrmPostgresql.html#.select">select</a></li><li data-type='method'><a href="WOrmPostgresql.html#.selectByPk">selectByPk</a></li></ul></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WOrmPostgresql.html">WOrmPostgresql</a><ul class='methods'><li data-type='method'><a href="WOrmPostgresql.html#.createTable">createTable</a></li><li data-type='method'><a href="WOrmPostgresql.html#.del">del</a></li><li data-type='method'><a href="WOrmPostgresql.html#.delAll">delAll</a></li><li data-type='method'><a href="WOrmPostgresql.html#.insert">insert</a></li><li data-type='method'><a href="WOrmPostgresql.html#.insertBulk">insertBulk</a></li><li data-type='method'><a href="WOrmPostgresql.html#.save">save</a></li><li data-type='method'><a href="WOrmPostgresql.html#.select">select</a></li><li data-type='method'><a href="WOrmPostgresql.html#.selectByPk">selectByPk</a></li></ul></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -45,8 +45,7 @@
45
45
 
46
46
  <section>
47
47
  <article>
48
- <pre class="prettyprint source linenums"><code>import events from 'events'
49
- import pg from 'pg'
48
+ <pre class="prettyprint source linenums"><code>import pg from 'pg'
50
49
  import mongoSql from 'mongo-sql'
51
50
  import size from 'lodash-es/size.js'
52
51
  import get from 'lodash-es/get.js'
@@ -67,6 +66,7 @@ import isint from 'wsemi/src/isint.mjs'
67
66
  import isbol from 'wsemi/src/isbol.mjs'
68
67
  import isDate from 'wsemi/src/isDate.mjs'
69
68
  import haskey from 'wsemi/src/haskey.mjs'
69
+ import evem from 'wsemi/src/evem.mjs'
70
70
  import pmSeries from 'wsemi/src/pmSeries.mjs'
71
71
 
72
72
 
@@ -220,8 +220,40 @@ function WOrmPostgresql(opt = {}) {
220
220
  let connectionString = `${url}/${db}`
221
221
  // console.log('connectionString', connectionString)
222
222
 
223
- //ee
224
- let ee = new events.EventEmitter()
223
+ //ee, 採eventemitter3而不採Node內建之events.EventEmitter,
224
+ //因後者於'error'無監聽者時會將錯誤直接拋出, 將使操作行為隨呼叫端是否註冊監聽而異
225
+ let ee = evem()
226
+
227
+ //getErrMsg, 取錯誤訊息字串, 供逐筆結果之err欄位與error事件使用
228
+ let getErrMsg = (err) => {
229
+ let m = get(err, 'message')
230
+ if (isestr(m)) {
231
+ return m
232
+ }
233
+ return String(err)
234
+ }
235
+
236
+ //emitChange, 資料實際異動成功後發出, 事件僅為附加通知不承擔回傳義務
237
+ //訂閱函數拋錯不得影響本次操作, 故一律以try/catch包覆, 收斂於此以免逐處遺漏
238
+ let emitChange = (mode, data, res) => {
239
+ try {
240
+ ee.emit('change', mode, data, res)
241
+ }
242
+ catch (errEmit) {
243
+ console.log(errEmit)
244
+ }
245
+ }
246
+
247
+ //emitError, 操作發生錯誤時發出, 錯誤訊息一律轉為字串
248
+ //正常結果(如查無數據、主鍵未命中、內容相同而未寫入)不得發出, 以免監聽者將常態當異常
249
+ let emitError = (mode, data, err) => {
250
+ try {
251
+ ee.emit('error', mode, data, getErrMsg(err))
252
+ }
253
+ catch (errEmit) {
254
+ console.log(errEmit)
255
+ }
256
+ }
225
257
 
226
258
  //PgClient
227
259
  let PgClient = pg.Client
@@ -260,6 +292,77 @@ function WOrmPostgresql(opt = {}) {
260
292
  _cache[key] = cloneDeep(data) //與外部使用數據脫勾
261
293
  }
262
294
 
295
+ //insertBatches, 分批送出插入語句並以交易包覆, 回傳實際插入筆數, 供insert與insertBulk共用
296
+ //PostgreSQL之協定以int16記綁定參數個數故上限為65535, 單一語句可送之筆數受欄位數所限,
297
+ //超出者須分批送出; 分批後單語句之原子性已不足以維持[失敗即不留下部份寫入], 故一律以交易包覆,
298
+ //令單批與分批之保證來源一致而不因批數而異
299
+ //兩函數之衝突政策由useConflict區分, 語句各自獨立:
300
+ // true : 添加ON CONFLICT DO NOTHING, 已存在主鍵者跳過而不中斷整批, 為insert之語義
301
+ // false: 不添加, 任一筆撞主鍵之唯一約束即整句失敗且不寫入任何一筆, 為insertBulk之全有全無語義
302
+ let insertBatches = async (client, data, useConflict) => {
303
+
304
+ //nCols, 取各筆欄位數之最大值
305
+ let nCols = 1
306
+ data.forEach(function(v) {
307
+ nCols = Math.max(nCols, size(keys(v)))
308
+ })
309
+
310
+ //nPerBatch
311
+ let nPerBatch = Math.max(1, Math.floor(65535 / nCols))
312
+
313
+ //ltdata, 分批
314
+ let ltdata = []
315
+ for (let i = 0; i &lt; size(data); i += nPerBatch) {
316
+ ltdata.push(data.slice(i, i + nPerBatch))
317
+ }
318
+
319
+ //transaction
320
+ await client.query('BEGIN')
321
+
322
+ try {
323
+
324
+ //nIns
325
+ let nIns = 0
326
+
327
+ //each batch
328
+ for (let dt of ltdata) {
329
+
330
+ //mr
331
+ let mr = mongoSql.sql({
332
+ type: 'insert',
333
+ table: cl,
334
+ values: dt,
335
+ })
336
+
337
+ //sql
338
+ let sql = mr.query
339
+ if (useConflict) {
340
+ sql = `${sql} ON CONFLICT (${pkName}) DO NOTHING`
341
+ }
342
+
343
+ //insert
344
+ let r = await client.query(sql, mr.values)
345
+
346
+ //nIns
347
+ nIns += r.rowCount
348
+
349
+ }
350
+
351
+ //commit
352
+ await client.query('COMMIT')
353
+
354
+ return nIns
355
+ }
356
+ catch (err) {
357
+
358
+ //rollback, 令已送出之批次一併回滾
359
+ await client.query('ROLLBACK').catch(() => {})
360
+
361
+ throw err
362
+ }
363
+
364
+ }
365
+
263
366
  /**
264
367
  * 創建資料表
265
368
  * 註: pk未給時採建構時之opt.pk。因insert與save倚賴主鍵之唯一約束達成原子性,
@@ -299,6 +402,10 @@ function WOrmPostgresql(opt = {}) {
299
402
 
300
403
  //check
301
404
  if (isErr) {
405
+
406
+ //emit, 整批性錯誤於reject之前發出
407
+ emitError('createTable', arr, res)
408
+
302
409
  return Promise.reject(res)
303
410
  }
304
411
 
@@ -349,6 +456,10 @@ function WOrmPostgresql(opt = {}) {
349
456
 
350
457
  //check
351
458
  if (isErr) {
459
+
460
+ //emit, 整批性錯誤於reject之前發出
461
+ emitError('createTable', arr, res)
462
+
352
463
  return Promise.reject(res)
353
464
  }
354
465
 
@@ -393,6 +504,10 @@ function WOrmPostgresql(opt = {}) {
393
504
 
394
505
  //check
395
506
  if (isErr) {
507
+
508
+ //emit, 整批性錯誤於reject之前發出, 讀取函數無輸入數據故data為null
509
+ emitError('select', null, res)
510
+
396
511
  return Promise.reject(res)
397
512
  }
398
513
 
@@ -441,6 +556,10 @@ function WOrmPostgresql(opt = {}) {
441
556
 
442
557
  //check
443
558
  if (isErr) {
559
+
560
+ //emit, 整批性錯誤於reject之前發出, 讀取函數無輸入數據故data為null
561
+ emitError('select', null, res)
562
+
444
563
  return Promise.reject(res)
445
564
  }
446
565
 
@@ -499,6 +618,10 @@ function WOrmPostgresql(opt = {}) {
499
618
 
500
619
  //check
501
620
  if (isErr) {
621
+
622
+ //emit, 整批性錯誤於reject之前發出, 讀取函數無輸入數據故data為null
623
+ emitError('selectByPk', null, res)
624
+
502
625
  return Promise.reject(res)
503
626
  }
504
627
 
@@ -565,6 +688,11 @@ function WOrmPostgresql(opt = {}) {
565
688
 
566
689
  //check
567
690
  if (isErr) {
691
+
692
+ //emit, 整批性錯誤於reject之前發出, 讀取函數無輸入數據故data為null
693
+ //主鍵未命中與主鍵值無效皆為正常結果而不設isErr, 故不會誤發error
694
+ emitError('selectByPk', null, res)
695
+
568
696
  return Promise.reject(res)
569
697
  }
570
698
 
@@ -616,6 +744,10 @@ function WOrmPostgresql(opt = {}) {
616
744
 
617
745
  //check
618
746
  if (isErr) {
747
+
748
+ //emit, 整批性錯誤於reject之前發出
749
+ emitError('insert', data, res)
750
+
619
751
  return Promise.reject(res)
620
752
  }
621
753
 
@@ -634,40 +766,137 @@ function WOrmPostgresql(opt = {}) {
634
766
  return v
635
767
  })
636
768
 
637
- //mr
638
- let mr = mongoSql.sql({
639
- type: 'insert',
640
- table: cl,
641
- values: data,
642
- })
643
- // console.log('mr', mr)
644
- // console.log('mr.query', mr.query)
645
- // console.log('mr.values', mr.values)
769
+ //nAll
770
+ let nAll = size(data)
646
771
 
647
- //添加conflict, 令已存在主鍵者跳過而不中斷整批插入, 主鍵認定與genConflictSQL一致
772
+ //insert, 以ON CONFLICT DO NOTHING令已存在主鍵者跳過而不中斷整批插入,
648
773
  //由PostgreSQL於單一語句內原子完成[檢查主鍵未存在]與[寫入], 併發時同一主鍵僅有一次成功,
649
774
  //同批含重複主鍵時亦僅首筆成功, 故不須逐筆插入即可取得實際插入筆數
650
- let sql = `${mr.query} ON CONFLICT (${pkName}) DO NOTHING`
775
+ //筆數過多而超出綁定參數上限者由insertBatches分批送出, 主鍵認定與genConflictSQL一致
776
+ let nIns = await insertBatches(client, data, true)
777
+
778
+ //res
779
+ res = {
780
+ n: nAll,
781
+ nInserted: nIns,
782
+ ok: 1,
783
+ }
784
+
785
+ }
786
+ catch (err) {
787
+ isErr = true
788
+ res = err
789
+ }
790
+ finally {
791
+ await client.end()
792
+ client = null
793
+ }
794
+
795
+ //update
796
+ if (useCache) {
797
+ clearCache()
798
+ }
799
+
800
+ //emit
801
+ if (!isErr) {
802
+ emitChange('insert', data, res)
803
+ }
804
+
805
+ //check
806
+ if (isErr) {
807
+
808
+ //emit, 整批性錯誤於reject之前發出
809
+ emitError('insert', data, res)
810
+
811
+ return Promise.reject(res)
812
+ }
813
+
814
+ return res
815
+ }
816
+
817
+ /**
818
+ * 批次插入數據,全批視為一個單位,全部插入成功或一筆都不寫入
819
+ *
820
+ * 本函數非insert之加速版,兩者衝突政策不同:insert於主鍵已存在時跳過該筆且整批ok為1,
821
+ * 本函數則於任一筆之主鍵已存在時整批reject且不寫入任何一筆,同批含重複主鍵者亦視為衝突。
822
+ * n為輸入筆數,nInserted為實際插入筆數且成功時恆等於n。
823
+ * 不提供逐筆結果,故不出現ok:0與err,需要逐筆處置者改用insert。
824
+ * 因主鍵得承載業務語義,未帶有效主鍵值時不自動補值,一律以reject回報,
825
+ * 且主鍵檢查於任何寫入之前一次完成,故整批reject時同批之有效筆數亦不會被寫入。
826
+ *
827
+ * @memberOf WOrmPostgresql
828
+ * @param {Object|Array} data 輸入數據物件或陣列
829
+ * @returns {Promise} 回傳Promise,resolve回傳插入結果物件{n,nInserted,ok},reject回傳錯誤訊息
830
+ */
831
+ async function insertBulk(data) {
832
+ let isErr = false
833
+ let res = null
834
+
835
+ //check
836
+ if (!iseobj(data) &amp;&amp; !isearr(data)) {
837
+ return {
838
+ n: 0,
839
+ nInserted: 0,
840
+ ok: 1,
841
+ }
842
+ }
843
+
844
+ //cloneDeep
845
+ data = cloneDeep(data)
846
+
847
+ //client
848
+ let client = new PgClient({ connectionString })
849
+
850
+ //connect
851
+ try {
852
+
853
+ //connect
854
+ await client.connect()
855
+
856
+ }
857
+ catch (err) {
858
+ isErr = true
859
+ res = err
860
+ client = null
861
+ }
862
+
863
+ //check
864
+ if (isErr) {
865
+
866
+ //emit, 整批性錯誤於reject之前發出
867
+ emitError('insertBulk', data, res)
868
+
869
+ return Promise.reject(res)
870
+ }
871
+
872
+ try {
873
+
874
+ //check
875
+ if (!isarr(data)) {
876
+ data = [data]
877
+ }
878
+
879
+ //check pk, 因主鍵得承載業務語義故不自動補值, 未帶有效主鍵值者屬整批性錯誤
880
+ data = map(data, function(v, k) {
881
+ if (!checkPk(get(v, pkName))) {
882
+ throw new Error(`invalid data[${k}].${pkName}[${get(v, pkName)}]`)
883
+ }
884
+ return v
885
+ })
651
886
 
652
887
  //nAll
653
888
  let nAll = size(data)
654
889
 
655
- //insert, ON CONFLICT DO NOTHING時rowCount即為實際插入筆數
656
- await client.query(sql, mr.values)
657
- .then((r) => {
658
-
659
- //res
660
- res = {
661
- n: nAll,
662
- nInserted: r.rowCount,
663
- ok: 1,
664
- }
890
+ //insertBulk, 不添加conflict, 令任一筆撞主鍵之唯一約束即整句失敗且不寫入任何一筆,
891
+ //同批含重複主鍵者亦於此被偵測為衝突; 筆數過多者由insertBatches分批送出並以交易維持全有全無
892
+ let nIns = await insertBatches(client, data, false)
665
893
 
666
- })
667
- .catch((err) => {
668
- isErr = true
669
- res = err.message
670
- })
894
+ //res
895
+ res = {
896
+ n: nAll,
897
+ nInserted: nIns,
898
+ ok: 1,
899
+ }
671
900
 
672
901
  }
673
902
  catch (err) {
@@ -686,16 +915,15 @@ function WOrmPostgresql(opt = {}) {
686
915
 
687
916
  //emit
688
917
  if (!isErr) {
689
- try {
690
- ee.emit('change', 'insert', data, res)
691
- }
692
- catch (err) {
693
- console.log(err)
694
- }
918
+ emitChange('insertBulk', data, res)
695
919
  }
696
920
 
697
921
  //check
698
922
  if (isErr) {
923
+
924
+ //emit, 整批性錯誤於reject之前發出
925
+ emitError('insertBulk', data, res)
926
+
699
927
  return Promise.reject(res)
700
928
  }
701
929
 
@@ -703,7 +931,7 @@ function WOrmPostgresql(opt = {}) {
703
931
  }
704
932
 
705
933
  /**
706
- * 儲存數據,以主鍵time為準更新既有數據,未給之欄位保留
934
+ * 儲存數據,以主鍵為準更新既有數據,未給之欄位保留
707
935
  *
708
936
  * 回傳陣列恆與輸入等長,輸入單一物件亦回傳長度1之陣列。
709
937
  * 各筆之n為主鍵命中筆數,值為0或1,命中(不論內容有無變更)或經插入而產生皆為1;
@@ -752,6 +980,10 @@ function WOrmPostgresql(opt = {}) {
752
980
 
753
981
  //check
754
982
  if (isErr) {
983
+
984
+ //emit, 整批性錯誤於reject之前發出
985
+ emitError('save', data, res)
986
+
755
987
  return Promise.reject(res)
756
988
  }
757
989
 
@@ -1017,6 +1249,16 @@ function WOrmPostgresql(opt = {}) {
1017
1249
 
1018
1250
  }
1019
1251
 
1252
+ //emit, 該筆經插入而產生者另發mode為insert之change事件, 供呼叫端區辨新增與更新
1253
+ if (rest.nInserted === 1) {
1254
+ emitChange('insert', [v], rest)
1255
+ }
1256
+
1257
+ //emit, 逐筆失敗於該筆結果定案後發出, 每筆一次
1258
+ if (rest.ok === 0) {
1259
+ emitError('save', [v], rest.err)
1260
+ }
1261
+
1020
1262
  return rest
1021
1263
  })
1022
1264
 
@@ -1037,16 +1279,15 @@ function WOrmPostgresql(opt = {}) {
1037
1279
 
1038
1280
  //emit
1039
1281
  if (!isErr) {
1040
- try {
1041
- ee.emit('change', 'save', data, res)
1042
- }
1043
- catch (err) {
1044
- console.log(err)
1045
- }
1282
+ emitChange('save', data, res)
1046
1283
  }
1047
1284
 
1048
1285
  //check
1049
1286
  if (isErr) {
1287
+
1288
+ //emit, 整批性錯誤於reject之前發出
1289
+ emitError('save', data, res)
1290
+
1050
1291
  return Promise.reject(res)
1051
1292
  }
1052
1293
 
@@ -1095,6 +1336,10 @@ function WOrmPostgresql(opt = {}) {
1095
1336
 
1096
1337
  //check
1097
1338
  if (isErr) {
1339
+
1340
+ //emit, 整批性錯誤於reject之前發出
1341
+ emitError('del', data, res)
1342
+
1098
1343
  return Promise.reject(res)
1099
1344
  }
1100
1345
 
@@ -1130,6 +1375,9 @@ function WOrmPostgresql(opt = {}) {
1130
1375
  err: `invalid ${pkName}[${pkv}]`,
1131
1376
  }
1132
1377
 
1378
+ //emit, 逐筆失敗於該筆結果定案後發出, 每筆一次
1379
+ emitError('del', [v], rest.err)
1380
+
1133
1381
  return rest
1134
1382
  }
1135
1383
 
@@ -1169,6 +1417,11 @@ function WOrmPostgresql(opt = {}) {
1169
1417
 
1170
1418
  })
1171
1419
 
1420
+ //emit, 逐筆失敗於該筆結果定案後發出, 每筆一次
1421
+ if (rest.ok === 0) {
1422
+ emitError('del', [v], rest.err)
1423
+ }
1424
+
1172
1425
  return rest
1173
1426
  })
1174
1427
 
@@ -1189,16 +1442,15 @@ function WOrmPostgresql(opt = {}) {
1189
1442
 
1190
1443
  //emit
1191
1444
  if (!isErr) {
1192
- try {
1193
- ee.emit('change', 'del', data, res)
1194
- }
1195
- catch (err) {
1196
- console.log(err)
1197
- }
1445
+ emitChange('del', data, res)
1198
1446
  }
1199
1447
 
1200
1448
  //check
1201
1449
  if (isErr) {
1450
+
1451
+ //emit, 整批性錯誤於reject之前發出
1452
+ emitError('del', data, res)
1453
+
1202
1454
  return Promise.reject(res)
1203
1455
  }
1204
1456
 
@@ -1237,6 +1489,10 @@ function WOrmPostgresql(opt = {}) {
1237
1489
 
1238
1490
  //check
1239
1491
  if (isErr) {
1492
+
1493
+ //emit, 整批性錯誤於reject之前發出, 無輸入數據故data為null
1494
+ emitError('delAll', null, res)
1495
+
1240
1496
  return Promise.reject(res)
1241
1497
  }
1242
1498
 
@@ -1300,16 +1556,15 @@ function WOrmPostgresql(opt = {}) {
1300
1556
 
1301
1557
  //emit
1302
1558
  if (!isErr) {
1303
- try {
1304
- ee.emit('change', 'delAll', null, res)
1305
- }
1306
- catch (err) {
1307
- console.log(err)
1308
- }
1559
+ emitChange('delAll', null, res)
1309
1560
  }
1310
1561
 
1311
1562
  //check
1312
1563
  if (isErr) {
1564
+
1565
+ //emit, 整批性錯誤於reject之前發出
1566
+ emitError('delAll', null, res)
1567
+
1313
1568
  return Promise.reject(res)
1314
1569
  }
1315
1570
 
@@ -1321,6 +1576,7 @@ function WOrmPostgresql(opt = {}) {
1321
1576
  ee.select = select
1322
1577
  ee.selectByPk = selectByPk
1323
1578
  ee.insert = insert
1579
+ ee.insertBulk = insertBulk
1324
1580
  ee.save = save
1325
1581
  ee.del = del
1326
1582
  ee.delAll = delAll
@@ -1344,7 +1600,7 @@ export default WOrmPostgresql
1344
1600
  <br class="clear">
1345
1601
 
1346
1602
  <footer>
1347
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Aug 16 2026 23:54:17 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1603
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 17 2026 11:51:27 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1348
1604
  </footer>
1349
1605
 
1350
1606
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WOrmPostgresql.html">WOrmPostgresql</a><ul class='methods'><li data-type='method'><a href="WOrmPostgresql.html#.createTable">createTable</a></li><li data-type='method'><a href="WOrmPostgresql.html#.del">del</a></li><li data-type='method'><a href="WOrmPostgresql.html#.delAll">delAll</a></li><li data-type='method'><a href="WOrmPostgresql.html#.insert">insert</a></li><li data-type='method'><a href="WOrmPostgresql.html#.save">save</a></li><li data-type='method'><a href="WOrmPostgresql.html#.select">select</a></li><li data-type='method'><a href="WOrmPostgresql.html#.selectByPk">selectByPk</a></li></ul></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WOrmPostgresql.html">WOrmPostgresql</a><ul class='methods'><li data-type='method'><a href="WOrmPostgresql.html#.createTable">createTable</a></li><li data-type='method'><a href="WOrmPostgresql.html#.del">del</a></li><li data-type='method'><a href="WOrmPostgresql.html#.delAll">delAll</a></li><li data-type='method'><a href="WOrmPostgresql.html#.insert">insert</a></li><li data-type='method'><a href="WOrmPostgresql.html#.insertBulk">insertBulk</a></li><li data-type='method'><a href="WOrmPostgresql.html#.save">save</a></li><li data-type='method'><a href="WOrmPostgresql.html#.select">select</a></li><li data-type='method'><a href="WOrmPostgresql.html#.selectByPk">selectByPk</a></li></ul></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Aug 16 2026 23:54:17 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 Mon Aug 17 2026 11:51:27 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>