w-serv-orm 1.1.12 → 1.1.14

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/src/getMapOrm.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { types } from 'util'
1
2
  import get from 'lodash-es/get.js'
2
3
  import each from 'lodash-es/each.js'
3
4
  import map from 'lodash-es/map.js'
@@ -114,39 +115,176 @@ function mapOwn(o, f) {
114
115
  }
115
116
 
116
117
 
117
- //不支援之鍵或屬性的原因與訊息(R-OBJ-01 規則3、5), 訊息只列固定字句, 不回顯客戶端之值
118
+ //不支援之鍵或屬性的原因與訊息(R-OBJ-01 規則3、5、6), 訊息只列固定字句, 不回顯客戶端之值
118
119
  const MSGS_BAD_KEY = Object.freeze({
119
120
  proto: 'unsupported key[__proto__]',
120
121
  accessor: 'unsupported accessor property',
121
122
  unreadable: 'unreadable value',
122
123
  deep: 'too deep',
124
+ hidden: 'unsupported symbol key or non-enumerable property',
125
+ irregular: 'unsupported irregular object or array',
123
126
  })
124
127
 
125
128
  //find之巢狀深度上限, 以容器(一般物件與陣列)層數計, 根為第1層(R-OBJ-01 規則5)
126
- //castIdFind、castIdCond以遞迴處理$and、$or、$nor與$not, 遞迴深度即呼叫堆疊深度, 實測字串型主鍵表約1173層$and(約2346個容器)起爆堆疊;
129
+ //castIdFind以遞迴處理$and、$or, 遞迴深度即呼叫堆疊深度, 實測字串型主鍵表約1173層$and(約2346個容器)起爆堆疊;
127
130
  //64個容器層約為32層邏輯巢狀, 遠大於合理查詢(庫內最深約7個容器)而遠小於爆堆疊之閾值
128
131
  const MAX_DEPTH_FIND = 64
129
132
 
133
+ //find之字串碼元總長(字串值與物件鍵, 含重複出現者)逾此值不做整棵結構化複製, 改走精確品牌探針(R-OBJ-01 規則6 ⑥)
134
+ //structuredClone對重複出現之字串每次皆寫出並讀回, 記憶體與(字串碼元總長 x 出現次數)成正比, 同一64KB字串於$in內重複1萬次即邏輯長度655M碼元; 與wsemi cvOrmFind之CLONE_MAX_CHARS同值
135
+ const MAX_CHARS_CLONE = 4000000
136
+
137
+ //拒收之診斷快照的上限: 值個數(含葉)、層數、每層鍵或元素數、字串長度(R-OBJ-01 規則6 ⑤)
138
+ const SNAP_MAX_NODES = 200
139
+ const SNAP_MAX_DEPTH = 8
140
+ const SNAP_MAX_ITEMS = 50
141
+ const SNAP_MAX_STR = 200
142
+
143
+ //WeakRef、FinalizationRegistry之精確品牌探針(R-OBJ-01 規則6 ⑥): util.types無對應判定, 後端cvOrmFind亦以此二探針reject;
144
+ //於模組載入時取得內建方法(不受其後全域覆寫影響), 對非該品牌之值拋TypeError, 不執行使用端程式
145
+ const weakRefDeref = typeof WeakRef === 'function' ? Function.prototype.call.bind(WeakRef.prototype.deref) : null
146
+ const frUnregister = typeof FinalizationRegistry === 'function' ? Function.prototype.call.bind(FinalizationRegistry.prototype.unregister) : null
147
+ const probeToken = {}
148
+
149
+
150
+ function isCleanChain(o) {
151
+ //o及其原型鏈上無Proxy、且無任何自有Symbol.toStringTag(R-OBJ-01 規則6): 全程只用util.types.isProxy(不觸發陷阱)、Object.getPrototypeOf與自有屬性查詢(不讀值),
152
+ //故不執行使用端程式(Proxy之陷阱、標籤getter); 不用in運算子: 其遇Proxy原型會執行has陷阱, 令檢查之值與使用之值可不同(外審第二輪 A-02)
153
+ for (let x = o; x !== null; x = Object.getPrototypeOf(x)) {
154
+ if (types.isProxy(x) || Object.prototype.hasOwnProperty.call(x, Symbol.toStringTag)) {
155
+ return false
156
+ }
157
+ }
158
+ return true
159
+ }
160
+
161
+
162
+ function isBrandObj(o) {
163
+ //內建品牌物件與其他非容器之內建物件(R-OBJ-01 規則6): 內部槽不在自有鍵中, 原型被改為null或Object.prototype而無標籤可辨時, 會被mapOwn洗成{};
164
+ //以util.types判定(不受原型與標籤影響, 不執行使用端程式)為主要探針; 未涵蓋者(WeakRef、FinalizationRegistry、Blob等)由hasIrregularClone之結構化複製網補
165
+ return types.isMap(o) || types.isSet(o) || types.isWeakMap(o) || types.isWeakSet(o) || types.isPromise(o) ||
166
+ types.isAnyArrayBuffer(o) || types.isArrayBufferView(o) || types.isBoxedPrimitive(o) || types.isRegExp(o) || types.isNativeError(o) ||
167
+ types.isGeneratorObject(o) || types.isMapIterator(o) || types.isSetIterator(o) || types.isModuleNamespaceObject(o)
168
+ }
169
+
170
+
171
+ function isRealmObjectProto(p) {
172
+ //p是否為某realm之Object.prototype(R-OBJ-01 規則6, 跨realm之純物件用): 原型為null、自有鍵名集合同本realm之Object.prototype、constructor為(資料屬性之)函數且其原始碼同Object,
173
+ //且該函數之prototype指回p; 只有constructor一鍵、或鍵名同而constructor指向他處(含getter)之偽原型皆不通過; 全程以屬性描述子讀取, 不執行使用端程式(p已由isCleanChain確認非Proxy)
174
+ if (Object.getPrototypeOf(p) !== null) {
175
+ return false
176
+ }
177
+ let ks0 = Reflect.ownKeys(Object.prototype)
178
+ if (Reflect.ownKeys(p).length !== ks0.length) {
179
+ return false
180
+ }
181
+ for (let k of ks0) {
182
+ if (!Object.prototype.hasOwnProperty.call(p, k)) {
183
+ return false
184
+ }
185
+ }
186
+ let d = Object.getOwnPropertyDescriptor(p, 'constructor')
187
+ if (d === undefined || typeof d.value !== 'function' || types.isProxy(d.value)) {
188
+ return false
189
+ }
190
+ if (Function.prototype.toString.call(d.value) !== Function.prototype.toString.call(Object)) {
191
+ return false
192
+ }
193
+ let dp = Object.getOwnPropertyDescriptor(d.value, 'prototype')
194
+ return dp !== undefined && dp.value === p
195
+ }
196
+
130
197
 
131
198
  function isPlainObj(v) {
132
- //純物件(R-INPUT-01 規則3): isobj為真且原型為null, 或為某realm之Object.prototype(同lodash isPlainObject: 原型之原型為null、有constructor函數、其函數原始碼同Object, 故跨realm之物件亦通過)
133
- //類別實例、Object.create(proto)、原型為null原型之一般物件、偽造Symbol.toStringTag之Map或包裝物件, 其自有可列舉鍵可能為空而被複製成{}, 令條件消失, 故不算
134
- if (!isobj(v)) {
199
+ //純物件(R-INPUT-01 規則3、R-OBJ-01 規則6): 無Proxy與Symbol.toStringTag(原型鏈上皆不得有, 見isCleanChain)、isobj為真,
200
+ //且原型為null、本realm之Object.prototype,或某realm之Object.prototype(isRealmObjectProto, 故跨realm(vm)之物件通過)
201
+ //類別實例、Object.create(proto)、原型為null原型之一般物件、宣稱Symbol.toStringTag之Map或包裝物件, 其自有可列舉鍵可能為空而被複製成{}, 令條件消失, 故不算
202
+ if (typeof v !== 'object' || v === null || !isCleanChain(v) || !isobj(v)) {
135
203
  return false
136
204
  }
137
205
  let p = Object.getPrototypeOf(v)
138
- if (p === null) {
206
+ return p === null || p === Object.prototype || isRealmObjectProto(p)
207
+ }
208
+
209
+
210
+ function getKindFind(o) {
211
+ //find節點之分類(R-OBJ-01 規則6), 檢查(getReasonBadKey strict)與複製(castIdFind、castIdCond)共用同一判準, 使複本與原物件在後端所見等價:
212
+ // 'arr': 真陣列(Array.isArray); 'obj': 一般物件(isobj); null: 不是容器, 原值或原參考交後端(原始值、函數、Date等)
213
+ //有Proxy、有Symbol.toStringTag(自有或繼承)、或為內建品牌物件者不分類(null), 由檢查reject: 以isCleanChain判斷而不讀標籤、不觸發陷阱,
214
+ //故不執行使用端程式, 亦不致檢查與複製各讀一次而得不同結果
215
+ if (typeof o !== 'object' || o === null || !isCleanChain(o) || isBrandObj(o)) {
216
+ return null
217
+ }
218
+ if (Array.isArray(o)) {
219
+ return 'arr'
220
+ }
221
+ return isobj(o) ? 'obj' : null
222
+ }
223
+
224
+
225
+ function hasIrregularClone(v) {
226
+ //品牌物件之通用網(R-OBJ-01 規則6): 以一次結構化複製證明v之容器皆為一般物件、陣列或Date; isBrandObj(util.types)已涵蓋常見內建品牌物件, 此網補其餘(Blob、WeakRef、FinalizationRegistry、Intl.*等):
227
+ //複製拋錯(Proxy、WeakRef、FinalizationRegistry、函數值、Symbol值等),或複本之容器原型不是Object.prototype、Array.prototype、Date.prototype(Blob等複製後仍為原品牌, 含原型被改為null者,
228
+ //其內部槽不在自有鍵中, 無標籤可辨, 又會被mapOwn洗成{})者, 回傳true; 呼叫時v已確認無Proxy與存取器(複製不執行使用端程式)
229
+ if (typeof structuredClone !== 'function') {
230
+ return false //執行環境無結構化複製(Node 17以前)則無此網(呼叫端getReasonBadKey此時改走hasWeakSlot之精確探針)
231
+ }
232
+ let c
233
+ try {
234
+ c = structuredClone(v)
235
+ }
236
+ catch (e) {
139
237
  return true
140
238
  }
141
- //p之原型為null, 故p.constructor只可能是p之自有屬性
142
- return Object.getPrototypeOf(p) === null &&
143
- typeof p.constructor === 'function' &&
144
- Function.prototype.toString.call(p.constructor) === Function.prototype.toString.call(Object)
239
+ let seen = new Set()
240
+ let stack = [c]
241
+ while (stack.length > 0) {
242
+ let o = stack.pop()
243
+ if (typeof o !== 'object' || o === null || seen.has(o)) {
244
+ continue
245
+ }
246
+ seen.add(o)
247
+ let p = Object.getPrototypeOf(o)
248
+ if (p === Date.prototype) {
249
+ continue
250
+ }
251
+ if (p !== Object.prototype && p !== Array.prototype) {
252
+ return true
253
+ }
254
+ for (let k of Object.keys(o)) {
255
+ stack.push(o[k])
256
+ }
257
+ }
258
+ return false
259
+ }
260
+
261
+
262
+ function hasWeakSlot(o) {
263
+ //o是否為WeakRef或FinalizationRegistry(R-OBJ-01 規則6 ⑥, 品牌網逾字元預算或無structuredClone時用): 以內建方法之品牌檢查判斷, 非該品牌即拋TypeError; 呼叫時o已確認非Proxy
264
+ if (weakRefDeref !== null) {
265
+ try {
266
+ weakRefDeref(o)
267
+ return true
268
+ }
269
+ catch (e) {
270
+ //非WeakRef
271
+ }
272
+ }
273
+ if (frUnregister !== null) {
274
+ try {
275
+ frUnregister(o, probeToken)
276
+ return true
277
+ }
278
+ catch (e) {
279
+ //非FinalizationRegistry
280
+ }
281
+ }
282
+ return false
145
283
  }
146
284
 
147
285
 
148
286
  function logSafe(label, ...args) {
149
- //診斷用之console.log: 輸出使用端原物件時會呼叫其util.inspect.custom, 拋錯不得使procOrm之reject變成例外物件(R-ERR-01 規則5)
287
+ //診斷用之console.log: 引數若含使用端物件(如mapOrm catch輸出之被拋出的值)會被格式化而呼叫其util.inspect.custom等, 拋錯不得使procOrm之reject變成例外物件(R-ERR-01 規則5); find一律經logFind輸出快照(R-OBJ-01 規則6 ⑤)
150
288
  try {
151
289
  console.log(label, ...args)
152
290
  }
@@ -161,24 +299,135 @@ function logSafe(label, ...args) {
161
299
  }
162
300
 
163
301
 
164
- function getReasonBadKey(v, maxDepth = Infinity) {
165
- //v之任何深度(一般物件之值、陣列之元素)含不支援之鍵或屬性者回傳原因(R-OBJ-01 規則3、5), 通過回傳null:
302
+ function snapSafe(v, st = { n: 0 }, depth = 0) {
303
+ //診斷輸出用之安全快照(R-OBJ-01 規則6 ⑤): 回傳本層新建之純資料, 全程只用型別探針(util.types)與屬性描述子, 不呼叫getter、不讀Symbol.toStringTag、不觸發Proxy陷阱、不呼叫util.inspect.custom,
304
+ //故console.log格式化的是快照而非使用端物件(原以console.log輸出原物件時, 格式化會在拒收之後執行使用端程式, 其副作用可改動呼叫端的find);
305
+ //Proxy、函數、Symbol、品牌物件、存取器與Date以固定字樣代替, 不可列舉鍵加註, 並有值個數(含葉)、層數、每層項數與字串長度之上限(診斷輸出不隨輸入放大)
306
+ if (st.n >= SNAP_MAX_NODES) {
307
+ return '[...]'
308
+ }
309
+ st.n++
310
+ if (typeof v === 'string') {
311
+ return truncate(v, { length: SNAP_MAX_STR })
312
+ }
313
+ if (typeof v === 'function') {
314
+ return '[Function]'
315
+ }
316
+ if (typeof v === 'symbol') {
317
+ return '[Symbol]'
318
+ }
319
+ if (typeof v !== 'object' || v === null) {
320
+ return v
321
+ }
322
+ if (types.isProxy(v)) {
323
+ return '[Proxy]'
324
+ }
325
+ if (types.isDate(v)) {
326
+ return '[Date]'
327
+ }
328
+ if (isBrandObj(v)) {
329
+ return '[Object]'
330
+ }
331
+ if (depth >= SNAP_MAX_DEPTH) {
332
+ return '[...]'
333
+ }
334
+ let snapDesc = (d) => {
335
+ if (d === undefined) {
336
+ return '[empty]'
337
+ }
338
+ if (d.get !== undefined || d.set !== undefined) {
339
+ return '[Getter/Setter]'
340
+ }
341
+ return snapSafe(d.value, st, depth + 1)
342
+ }
343
+ if (Array.isArray(v)) {
344
+ let n = Math.min(v.length, SNAP_MAX_ITEMS)
345
+ let rs = []
346
+ for (let i = 0; i < n; i++) {
347
+ rs.push(snapDesc(Object.getOwnPropertyDescriptor(v, i)))
348
+ }
349
+ if (v.length > n) {
350
+ rs.push(`[${v.length - n} more]`)
351
+ }
352
+ return rs
353
+ }
354
+ let ks = Reflect.ownKeys(v)
355
+ let r = {}
356
+ for (let k of ks.slice(0, SNAP_MAX_ITEMS)) {
357
+ let d = Object.getOwnPropertyDescriptor(v, k)
358
+ let name = typeof k === 'symbol' ? String(k) : k
359
+ Object.defineProperty(r, d.enumerable ? name : `${name} (non-enumerable)`, { value: snapDesc(d), enumerable: true, writable: true, configurable: true })
360
+ }
361
+ if (ks.length > SNAP_MAX_ITEMS) {
362
+ Object.defineProperty(r, '[more]', { value: ks.length - SNAP_MAX_ITEMS, enumerable: true, writable: true, configurable: true })
363
+ }
364
+ return r
365
+ }
366
+
367
+
368
+ function logFind(label, input) {
369
+ //被拒之find的診斷輸出(R-OBJ-01 規則6 ⑤): 輸出snapSafe之快照而非原物件, 不執行使用端程式
370
+ let snap
371
+ try {
372
+ snap = snapSafe(input)
373
+ }
374
+ catch (e) {
375
+ snap = '(unprintable)'
376
+ }
377
+ logSafe(label, snap)
378
+ }
379
+
380
+
381
+ function getReasonBadKey(v, maxDepth = Infinity, strict = false) {
382
+ //v之任何深度(一般物件之值、陣列之元素)含不支援之鍵或屬性者回傳原因(R-OBJ-01 規則3、5、6), 通過回傳null:
166
383
  // proto: 含自有__proto__鍵, 不論可否列舉; 自有鍵可由JSON.parse、Object.fromEntries、展開、structuredClone等產生, 物件字面量之__proto__為設原型而不算
167
384
  // accessor: 含存取器屬性(getter或setter), 其值每讀一次可能不同, 令檢查之值與使用之值無法保證相同, 且讀取可能有副作用
168
385
  // unreadable: 檢查時拋錯, 如Proxy之陷阱或已撤銷之Proxy
169
386
  // deep: 容器(一般物件與陣列)層數超過maxDepth(根為第1層), 走訪到第maxDepth+1層即停止; 只有find給予maxDepth, 寫入列不設上限(見R-OBJ-01 規則5)
170
- //只走一般物件與陣列(JSON可表達之資料)之可列舉資料屬性, 以屬性描述子讀值而不呼叫getter; 其餘型別(Date、Map、Set、函數等)不進入
387
+ //strict(只有find開啟, 規則6): castIdFind、castIdCond以mapOwn(只走自有可列舉字串鍵)與lodash map(只走索引)複製find, 複本會洗掉下列結構,
388
+ //令後端白名單版本會reject之條件變成較寬甚至無條件(delAll刪全表), 故走訪到之每個節點須為複本與原物件等價者, 容器之分類與複製共用getKindFind:
389
+ // irregular: 有Symbol.toStringTag(原型鏈上任一層之自有屬性, 含內建品牌物件與宣稱標籤者, 以hasOwnProperty判斷而不讀取)、物件節點非純物件(類別實例、偽原型等, 同頂層之isPlainObj)、
390
+ // 陣列節點有空洞、不可列舉元素或額外屬性(含Symbol鍵), 或結構化複製證明有品牌物件與不可複製者(Proxy、函數值等, 見hasIrregularClone);
391
+ // 字串碼元總長逾MAX_CHARS_CLONE或無structuredClone時不做結構化複製, 改以hasWeakSlot逐一探針WeakRef與FinalizationRegistry(規則6 ⑥)
392
+ // hidden: 物件節點有Symbol鍵或不可列舉鍵
393
+ //寫入列不開strict: 後端自己亦略過Symbol鍵與不可列舉鍵, 複本不改變其語意
394
+ //只走一般物件與陣列(JSON可表達之資料)之可列舉資料屬性, 以屬性描述子讀值而不呼叫getter; 其餘型別(Date、函數等)不進入
171
395
  //以堆疊迭代避免本檢查爆呼叫堆疊(其後castIdFind、castIdCond之遞迴由maxDepth保護), seen避免循環結構無窮迭代(循環不會被判為過深, 見R-OBJ-01 已知限制)
172
396
  try {
173
397
  let seen = new Set()
174
398
  let stack = [v, 1] //成對: 節點, 其層數
399
+ let chars = 0 //strict: 走訪到之字串碼元總長(字串值與物件鍵, 含重複出現者), 供品牌網之預算判斷
400
+ let nodes = [] //strict: 走訪到之物件節點(純物件), 供逾預算時逐一探針
175
401
  while (stack.length > 0) {
176
402
  let depth = stack.pop()
177
403
  let o = stack.pop()
178
- let arr = Array.isArray(o) //不用isarr: 其以Object.prototype.toString判定, 可被Symbol.toStringTag偽造而走訪其length getter; 偽造者isobj亦為假, 故不進入
179
- if (seen.has(o) || (!arr && !isobj(o))) {
404
+ if (seen.has(o)) {
180
405
  continue
181
406
  }
407
+ let kind
408
+ if (strict) {
409
+ //Proxy(含原型鏈上者)、標籤與內建品牌物件須先於任何會執行使用端程式之判定(isobj讀標籤、in運算子、讀屬性), 且以不觸發陷阱之方式判斷(isCleanChain、isBrandObj)
410
+ if (typeof o === 'object' && o !== null) {
411
+ if (!isCleanChain(o) || isBrandObj(o)) {
412
+ return 'irregular'
413
+ }
414
+ if (types.isDate(o)) {
415
+ //Date為葉(原值交後端, 後端只讀其時間槽), 但須乾淨: 附加之自有屬性(含__proto__鍵、getter)會被前後處理函數與診斷輸出看到(外審第二輪 A-06)
416
+ if (Reflect.ownKeys(o).length > 0) {
417
+ return 'irregular'
418
+ }
419
+ continue
420
+ }
421
+ }
422
+ kind = getKindFind(o)
423
+ }
424
+ else {
425
+ kind = Array.isArray(o) ? 'arr' : (isobj(o) ? 'obj' : null) //寫入列維持原判準; 不用isarr: 其以Object.prototype.toString判定, 可被Symbol.toStringTag宣稱而走訪其length getter
426
+ }
427
+ if (kind === null) {
428
+ continue
429
+ }
430
+ let arr = kind === 'arr'
182
431
  if (depth > maxDepth) {
183
432
  return 'deep'
184
433
  }
@@ -187,19 +436,59 @@ function getReasonBadKey(v, maxDepth = Infinity) {
187
436
  return 'proto'
188
437
  }
189
438
  let ks = arr ? null : Object.keys(o)
439
+ if (strict && !arr) {
440
+ if (!isPlainObj(o)) {
441
+ return 'irregular'
442
+ }
443
+ if (Reflect.ownKeys(o).length !== ks.length) {
444
+ return 'hidden'
445
+ }
446
+ nodes.push(o)
447
+ }
190
448
  let n = arr ? o.length : ks.length
191
449
  for (let i = 0; i < n; i++) {
192
450
  let d = Object.getOwnPropertyDescriptor(o, arr ? i : ks[i])
193
451
  if (d === undefined) {
452
+ if (strict) {
453
+ return 'irregular' //空洞: map以array[index]讀到原型上之繼承值而成為密陣列(寫入列則不檢查)
454
+ }
194
455
  continue //稀疏陣列之空洞
195
456
  }
196
457
  if (d.get !== undefined || d.set !== undefined) {
197
458
  return 'accessor'
198
459
  }
460
+ if (strict && arr && !d.enumerable) {
461
+ return 'irregular'
462
+ }
463
+ if (strict) {
464
+ chars += arr ? 0 : ks[i].length
465
+ if (typeof d.value === 'string') {
466
+ chars += d.value.length
467
+ }
468
+ }
199
469
  if (typeof d.value === 'object' && d.value !== null) {
200
470
  stack.push(d.value, depth + 1)
201
471
  }
202
472
  }
473
+ if (strict && arr && Reflect.ownKeys(o).length !== n + 1) {
474
+ return 'irregular' //除索引與length外另有自有鍵
475
+ }
476
+ }
477
+ //品牌物件與不可複製者之通用網, 於走訪全數通過(已無存取器)之後: 字串碼元總長在預算內且有structuredClone時以一次結構化複製證明,
478
+ //否則(逾預算或無structuredClone)對走訪到之物件節點逐一以精確探針判斷util.types無對應判定之WeakRef與FinalizationRegistry(規則6 ⑥)
479
+ if (strict && typeof v === 'object' && v !== null) {
480
+ if (chars <= MAX_CHARS_CLONE && typeof structuredClone === 'function') {
481
+ if (hasIrregularClone(v)) {
482
+ return 'irregular'
483
+ }
484
+ }
485
+ else {
486
+ for (let i = 0; i < nodes.length; i++) {
487
+ if (hasWeakSlot(nodes[i])) {
488
+ return 'irregular'
489
+ }
490
+ }
491
+ }
203
492
  }
204
493
  return null
205
494
  }
@@ -211,13 +500,17 @@ function getReasonBadKey(v, maxDepth = Infinity) {
211
500
 
212
501
  function getErrFind(mode, find) {
213
502
  //select、delAll之find檢查(R-INPUT-01 規則3、R-OBJ-01 規則3), 通過回傳null, 否則回傳reject之字串
214
- //find給予(非undefined)須為純物件: 未給、undefined與空物件{}為無條件(select回全部、delAll刪全部), 各後端對非物件之find多視為無條件而回全表或刪全表, 故由本套件先擋
503
+ //find給予(非undefined)須為純物件: 未給、undefined與空物件{}為無條件(select回全部、delAll刪全部), 舊版(未實作系列條件白名單)後端對非物件之find多視為無條件而回全表或刪全表, 且本套件之複本會洗掉類別實例等之條件, 故由本套件先擋
215
504
  //任何深度不得含自有__proto__鍵(複製時該鍵會被丟棄, 令ORM所見之條件與呼叫端含授權函數所見者不同)與存取器屬性(見getReasonBadKey), 且巢狀深度不得超過MAX_DEPTH_FIND
505
+ //並以strict檢查複本會洗掉之結構(Symbol鍵、不可列舉鍵、巢狀非純物件、不規則陣列; R-OBJ-01 規則6), 不論資料表型態
216
506
  try {
507
+ if (find !== undefined && typeof find === 'object' && find !== null && types.isProxy(find)) {
508
+ return `invalid find of ${mode}, ${MSGS_BAD_KEY.irregular}` //Proxy(含已撤銷者)於任何位置皆reject, 不觸發任何陷阱(R-OBJ-01 規則6)
509
+ }
217
510
  if (find !== undefined && !isPlainObj(find)) {
218
511
  return `invalid find of ${mode}, need an object`
219
512
  }
220
- let reason = getReasonBadKey(find, MAX_DEPTH_FIND)
513
+ let reason = getReasonBadKey(find, MAX_DEPTH_FIND, true)
221
514
  if (reason !== null) {
222
515
  return `invalid find of ${mode}, ${MSGS_BAD_KEY[reason]}`
223
516
  }
@@ -285,16 +578,15 @@ function castIdRows(rows) {
285
578
 
286
579
 
287
580
  function castIdCond(c) {
288
- //主鍵之條件: 值或運算子物件; 比較運算子之值(陣列逐一)轉型, $not遞迴, 其餘運算子($regex、$exists等)原樣
289
- if (!isobj(c)) {
581
+ //主鍵之條件: 值或運算子物件; 只轉型系列白名單內比較運算子($eq、$ne、$gt、$gte、$lt、$lte、$in、$nin)之值(陣列逐一), 其餘運算子原樣
582
+ //白名單外之運算子($not、$all、$nor、$regex、$exists等)由各w-orm後端整批reject, 本套件不轉型亦不攔截(R-WL-01)
583
+ //容器之判定用getKindFind, 與getErrFind之檢查同一判準(R-OBJ-01 規則6); 非'obj'、'arr'者原值或原參考交後端
584
+ if (getKindFind(c) !== 'obj') {
290
585
  return castIdValue(c)
291
586
  }
292
587
  return mapOwn(c, (v, k) => {
293
- if (k === '$not') {
294
- return castIdCond(v)
295
- }
296
- if (['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in', '$nin', '$all'].indexOf(k) >= 0) {
297
- return isarr(v) ? map(v, castIdValue) : castIdValue(v)
588
+ if (['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in', '$nin'].indexOf(k) >= 0) {
589
+ return getKindFind(v) === 'arr' ? map(v, castIdValue) : castIdValue(v)
298
590
  }
299
591
  return v
300
592
  })
@@ -302,15 +594,16 @@ function castIdCond(c) {
302
594
 
303
595
 
304
596
  function castIdFind(find) {
305
- //查詢條件(select、delAll之find): id之條件轉型, 遞迴$and、$or、$nor; 他欄原樣; 寫於複本, 非物件原樣
306
- if (!isobj(find)) {
597
+ //查詢條件(select、delAll之find): id之條件轉型, 遞迴文件層$and、$or; 他欄原樣; 寫於複本, 非物件原樣
598
+ //容器之判定用getKindFind, 與getErrFind之檢查同一判準(R-OBJ-01 規則6)
599
+ if (getKindFind(find) !== 'obj') {
307
600
  return find
308
601
  }
309
602
  return mapOwn(find, (v, k) => {
310
603
  if (k === 'id') {
311
604
  return castIdCond(v)
312
605
  }
313
- if (['$and', '$or', '$nor'].indexOf(k) >= 0 && isarr(v)) {
606
+ if (['$and', '$or'].indexOf(k) >= 0 && getKindFind(v) === 'arr') {
314
607
  return map(v, castIdFind)
315
608
  }
316
609
  return v
@@ -549,7 +842,7 @@ async function execMapOrm(ds, woItems, userId, woName, mode, input) {
549
842
  //check, 條件若給予須為一般物件且不含自有__proto__鍵(R-INPUT-01 規則3、R-OBJ-01 規則3), 於轉型與查詢之前
550
843
  let errFind = getErrFind('select', input)
551
844
  if (errFind !== null) {
552
- logSafe(`${woName}.select invalid find`, input)
845
+ logFind(`${woName}.select invalid find`, input)
553
846
  return Promise.reject(errFind)
554
847
  }
555
848
 
@@ -679,10 +972,10 @@ async function execMapOrm(ds, woItems, userId, woName, mode, input) {
679
972
  }
680
973
  else if (mode === 'delAll') {
681
974
 
682
- //check, 條件若給予須為一般物件(R-INPUT-01 規則3): 各後端對非物件之條件一律視為無條件而刪除全表; 未給(undefined)與空物件依規格為刪除全部; 另不得含自有__proto__鍵(R-OBJ-01 規則3)
975
+ //check, 條件若給予須為一般物件(R-INPUT-01 規則3): 舊版(未實作系列條件白名單)後端對非物件之條件一律視為無條件而刪除全表; 未給(undefined)與空物件依規格為刪除全部; 另不得含自有__proto__鍵(R-OBJ-01 規則3)
683
976
  let errFind = getErrFind('delAll', input)
684
977
  if (errFind !== null) {
685
- logSafe(`${woName}.delAll invalid find`, input)
978
+ logFind(`${woName}.delAll invalid find`, input)
686
979
  return Promise.reject(errFind)
687
980
  }
688
981