wsemi 1.9.19 → 1.9.21

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.
Files changed (507) hide show
  1. package/README.md +1 -1
  2. package/dist/wsemi.umd.js +5 -5
  3. package/dist/wsemi.umd.js.map +1 -1
  4. package/docs/-_class.mjs.html +1 -1
  5. package/docs/-_const.mjs.html +1 -1
  6. package/docs/-_detector.mjs.html +1 -1
  7. package/docs/-_errToStr.mjs.html +1 -1
  8. package/docs/-_execThrownMsg.mjs.html +1 -1
  9. package/docs/-_isRealError.mjs.html +1 -1
  10. package/docs/-_optNum.mjs.html +1 -1
  11. package/docs/-_pmMarkHandled.mjs.html +1 -1
  12. package/docs/-_pmReadItems.mjs.html +1 -1
  13. package/docs/-_pmSettleByState.mjs.html +1 -1
  14. package/docs/-_realErrorToStr.mjs.html +1 -1
  15. package/docs/-_toResultError.mjs.html +1 -1
  16. package/docs/ab2blob.mjs.html +1 -1
  17. package/docs/ab2u8arr.mjs.html +1 -1
  18. package/docs/addExcelWorksheetFromData.mjs.html +1 -1
  19. package/docs/addMonth.mjs.html +1 -1
  20. package/docs/aes2str.mjs.html +1 -1
  21. package/docs/alive.mjs.html +1 -1
  22. package/docs/arr2dt.mjs.html +1 -1
  23. package/docs/arrAccum.mjs.html +1 -1
  24. package/docs/arrAdd.mjs.html +1 -1
  25. package/docs/arrAt.mjs.html +1 -1
  26. package/docs/arrDiff.mjs.html +1 -1
  27. package/docs/arrFilterByKeywords.mjs.html +1 -1
  28. package/docs/arrFilterByNum.mjs.html +1 -1
  29. package/docs/arrFilterByPnum.mjs.html +1 -1
  30. package/docs/arrFilterByPnumAndToLog.mjs.html +1 -1
  31. package/docs/arrFind.mjs.html +1 -1
  32. package/docs/arrGroupByMaxmin.mjs.html +1 -1
  33. package/docs/arrHas.mjs.html +1 -1
  34. package/docs/arrInsert.mjs.html +1 -1
  35. package/docs/arrLookupByMaxmin.mjs.html +1 -1
  36. package/docs/arrMax.mjs.html +1 -1
  37. package/docs/arrMin.mjs.html +1 -1
  38. package/docs/arrMulti.mjs.html +1 -1
  39. package/docs/arrNorm.mjs.html +1 -1
  40. package/docs/arrProduct.mjs.html +1 -1
  41. package/docs/arrPull.mjs.html +1 -1
  42. package/docs/arrPullAt.mjs.html +1 -1
  43. package/docs/arrReduce.mjs.html +1 -1
  44. package/docs/arrReverse.mjs.html +1 -1
  45. package/docs/arrSep.mjs.html +1 -1
  46. package/docs/arrSort.mjs.html +1 -1
  47. package/docs/asind.mjs.html +1 -1
  48. package/docs/atan2d.mjs.html +1 -1
  49. package/docs/atand.mjs.html +1 -1
  50. package/docs/attstr.mjs.html +1 -1
  51. package/docs/b642obj.mjs.html +1 -1
  52. package/docs/b642str.mjs.html +1 -1
  53. package/docs/b642u16arr.mjs.html +1 -1
  54. package/docs/b642u8arr.mjs.html +1 -1
  55. package/docs/binstr.mjs.html +1 -1
  56. package/docs/blob2ab.mjs.html +1 -1
  57. package/docs/blob2b64.mjs.html +1 -1
  58. package/docs/blob2str.mjs.html +1 -1
  59. package/docs/blob2u8arr.mjs.html +1 -1
  60. package/docs/blobs2b64s.mjs.html +1 -1
  61. package/docs/blobs2u8arrs.mjs.html +1 -1
  62. package/docs/bodyLog.mjs.html +1 -1
  63. package/docs/browserView.mjs.html +1 -1
  64. package/docs/browserViewFromBlob.mjs.html +1 -1
  65. package/docs/browserViewFromText.mjs.html +1 -1
  66. package/docs/browserViewFromU8arr.mjs.html +1 -1
  67. package/docs/bs2u8arr.mjs.html +1 -1
  68. package/docs/bufRead.mjs.html +1 -1
  69. package/docs/bufReadDbl.mjs.html +1 -1
  70. package/docs/bufWrite.mjs.html +1 -1
  71. package/docs/bufWriteDbl.mjs.html +1 -1
  72. package/docs/cache.mjs.html +1 -1
  73. package/docs/cacheBd.mjs.html +1 -1
  74. package/docs/cacheSt.mjs.html +1 -1
  75. package/docs/cbol.mjs.html +1 -1
  76. package/docs/cdbl.mjs.html +1 -1
  77. package/docs/ceil.mjs.html +1 -1
  78. package/docs/cfilesize.mjs.html +1 -1
  79. package/docs/cfinancial.mjs.html +1 -1
  80. package/docs/cint.mjs.html +1 -1
  81. package/docs/clearXSS.mjs.html +1 -1
  82. package/docs/clearXSSDyn.mjs.html +1 -1
  83. package/docs/color.mjs.html +1 -1
  84. package/docs/composeToTree.mjs.html +1 -1
  85. package/docs/convertToTree.mjs.html +1 -1
  86. package/docs/copyStrToClipboard.mjs.html +1 -1
  87. package/docs/cosd.mjs.html +1 -1
  88. package/docs/cotd.mjs.html +1 -1
  89. package/docs/createExcelWorkbook.mjs.html +1 -1
  90. package/docs/cscd.mjs.html +1 -1
  91. package/docs/cstr.mjs.html +1 -1
  92. package/docs/cvOrmFind.mjs.html +161 -23
  93. package/docs/day2cht.mjs.html +1 -1
  94. package/docs/debounce.mjs.html +1 -1
  95. package/docs/delay.mjs.html +1 -1
  96. package/docs/detectXSS.mjs.html +1 -1
  97. package/docs/detectXSSDyn.mjs.html +1 -1
  98. package/docs/dig.mjs.html +1 -1
  99. package/docs/digExp.mjs.html +1 -1
  100. package/docs/domAlert.mjs.html +1 -1
  101. package/docs/domAppend.mjs.html +1 -1
  102. package/docs/domCancelEvent.mjs.html +1 -1
  103. package/docs/domCompareText.mjs.html +1 -1
  104. package/docs/domConvertToPic.mjs.html +1 -1
  105. package/docs/domConvertToPicDyn.mjs.html +1 -1
  106. package/docs/domDetect.mjs.html +1 -1
  107. package/docs/domDrag.mjs.html +1 -1
  108. package/docs/domDragBarAndScroll.mjs.html +1 -1
  109. package/docs/domDragPointer.mjs.html +1 -1
  110. package/docs/domDragPreview.mjs.html +1 -1
  111. package/docs/domDropFiles.mjs.html +1 -1
  112. package/docs/domElementsFromPoint.mjs.html +1 -1
  113. package/docs/domFadeIn.mjs.html +1 -1
  114. package/docs/domFadeOut.mjs.html +1 -1
  115. package/docs/domFind.mjs.html +1 -1
  116. package/docs/domFinds.mjs.html +1 -1
  117. package/docs/domGetAttr.mjs.html +1 -1
  118. package/docs/domGetBoudRect.mjs.html +1 -1
  119. package/docs/domGetBoudRectRefSelf.mjs.html +1 -1
  120. package/docs/domGetFileAccept.mjs.html +1 -1
  121. package/docs/domGetFiles.mjs.html +1 -1
  122. package/docs/domGetParents.mjs.html +1 -1
  123. package/docs/domGetPointFromEvent.mjs.html +1 -1
  124. package/docs/domGetScrollableParent.mjs.html +1 -1
  125. package/docs/domGetWindowSize.mjs.html +1 -1
  126. package/docs/domIsClientXYIn.mjs.html +1 -1
  127. package/docs/domIsFocused.mjs.html +1 -1
  128. package/docs/domIsRendered.mjs.html +1 -1
  129. package/docs/domIsStable.mjs.html +1 -1
  130. package/docs/domIsVisible.mjs.html +1 -1
  131. package/docs/domPrepend.mjs.html +1 -1
  132. package/docs/domRemove.mjs.html +1 -1
  133. package/docs/domRenderMarkdownMind.mjs.html +1 -1
  134. package/docs/domRenderMarkdownMindDyn.mjs.html +1 -1
  135. package/docs/domRipple.mjs.html +1 -1
  136. package/docs/domScrollIntoView.mjs.html +1 -1
  137. package/docs/domScrollTo.mjs.html +1 -1
  138. package/docs/domSetAttr.mjs.html +1 -1
  139. package/docs/domShowImages.mjs.html +1 -1
  140. package/docs/domShowImagesDyn.mjs.html +1 -1
  141. package/docs/domShowInputAndGetFiles.mjs.html +1 -1
  142. package/docs/domShowInputAndGetFilesU8Arrs.mjs.html +1 -1
  143. package/docs/domShowInputDatatime.mjs.html +1 -1
  144. package/docs/domTooltip.mjs.html +1 -1
  145. package/docs/domTriggerEvent.mjs.html +1 -1
  146. package/docs/domVirtualCreate.mjs.html +1 -1
  147. package/docs/domVirtualCreateQueue.mjs.html +1 -1
  148. package/docs/downloadExcelFileFromData.mjs.html +1 -1
  149. package/docs/downloadExcelFileFromDataDyn.mjs.html +1 -1
  150. package/docs/downloadFileFromB64.mjs.html +1 -1
  151. package/docs/downloadFileFromBlob.mjs.html +1 -1
  152. package/docs/downloadFileFromText.mjs.html +1 -1
  153. package/docs/downloadFileFromU8Arr.mjs.html +1 -1
  154. package/docs/dtmapping.mjs.html +1 -1
  155. package/docs/dtpick.mjs.html +1 -1
  156. package/docs/estimateTicks.mjs.html +1 -1
  157. package/docs/evEmit.mjs.html +1 -1
  158. package/docs/evEmitDelay.mjs.html +1 -1
  159. package/docs/evem.mjs.html +1 -1
  160. package/docs/examples/ex-blob2b64.html +1 -1
  161. package/docs/examples/ex-clearXSS.html +1 -1
  162. package/docs/examples/ex-clearXSSDyn.html +1 -1
  163. package/docs/examples/ex-colorConverter.html +1 -1
  164. package/docs/examples/ex-detectXSS.html +1 -1
  165. package/docs/examples/ex-detectXSSDyn.html +1 -1
  166. package/docs/examples/ex-domCompareText.html +1 -1
  167. package/docs/examples/ex-domConvertToPic.html +1 -1
  168. package/docs/examples/ex-domDrag.html +1 -1
  169. package/docs/examples/ex-domDropFiles.html +1 -1
  170. package/docs/examples/ex-domIsVisible.html +1 -1
  171. package/docs/examples/ex-domRenderMarkdownMind.html +1 -1
  172. package/docs/examples/ex-domRenderMarkdownMindDyn.html +1 -1
  173. package/docs/examples/ex-domRipple.html +1 -1
  174. package/docs/examples/ex-domScrollIntoView.html +1 -1
  175. package/docs/examples/ex-domScrollTo.html +1 -1
  176. package/docs/examples/ex-domShowImages.html +1 -1
  177. package/docs/examples/ex-domShowImagesDyn.html +1 -1
  178. package/docs/examples/ex-domShowInputAndGetFiles.html +1 -1
  179. package/docs/examples/ex-domTooltip.html +1 -1
  180. package/docs/examples/ex-getFileHash.html +1 -1
  181. package/docs/examples/ex-getUserAgent.html +1 -1
  182. package/docs/examples/ex-getUserAgentDyn.html +1 -1
  183. package/docs/examples/ex-html2str.html +1 -1
  184. package/docs/examples/ex-obj2u8arr-u8arr2obj.html +1 -1
  185. package/docs/examples/ex-openLink.html +1 -1
  186. package/docs/examples/ex-str2aes-aes2str.html +1 -1
  187. package/docs/examples/ex-str2b64-b642str.html +1 -1
  188. package/docs/examples/ex-strFindFuzz.html +1 -1
  189. package/docs/examples/ex-timeTZ2expire-timeTZ2past.html +1 -1
  190. package/docs/examples/ex-xlsx.html +1 -1
  191. package/docs/execCli.mjs.html +1 -1
  192. package/docs/execCliCore.mjs.html +1 -1
  193. package/docs/execCliJsonRpc.mjs.html +1 -1
  194. package/docs/execCliSession.mjs.html +1 -1
  195. package/docs/execJsCode.mjs.html +1 -1
  196. package/docs/execProcess.mjs.html +1 -1
  197. package/docs/execProcessKillPid.mjs.html +1 -1
  198. package/docs/filepathToTree.mjs.html +1 -1
  199. package/docs/files2data.mjs.html +1 -1
  200. package/docs/flattenToConn.mjs.html +1 -1
  201. package/docs/flattenTree.mjs.html +1 -1
  202. package/docs/floor.mjs.html +1 -1
  203. package/docs/fsBuildReadStreamText.mjs.html +1 -1
  204. package/docs/fsBuildWriteStreamText.mjs.html +1 -1
  205. package/docs/fsCleanFolder.mjs.html +1 -1
  206. package/docs/fsCleanFolderCore.mjs.html +1 -1
  207. package/docs/fsCopyFile.mjs.html +1 -1
  208. package/docs/fsCopyFileCore.mjs.html +1 -1
  209. package/docs/fsCopyFolder.mjs.html +1 -1
  210. package/docs/fsCopyFolderCore.mjs.html +1 -1
  211. package/docs/fsCreateFile.mjs.html +1 -1
  212. package/docs/fsCreateFileCore.mjs.html +1 -1
  213. package/docs/fsCreateFolder.mjs.html +1 -1
  214. package/docs/fsCreateFolderCore.mjs.html +1 -1
  215. package/docs/fsDeleteFile.mjs.html +1 -1
  216. package/docs/fsDeleteFileCore.mjs.html +1 -1
  217. package/docs/fsDeleteFileSafe.mjs.html +1 -1
  218. package/docs/fsDeleteFileSafeCore.mjs.html +1 -1
  219. package/docs/fsDeleteFolder.mjs.html +1 -1
  220. package/docs/fsDeleteFolderCore.mjs.html +1 -1
  221. package/docs/fsDeleteFolderSafe.mjs.html +1 -1
  222. package/docs/fsDeleteFolderSafeCore.mjs.html +1 -1
  223. package/docs/fsDownloadFile.mjs.html +1 -1
  224. package/docs/fsDownloadFileCore.mjs.html +1 -1
  225. package/docs/fsEvem.mjs.html +1 -1
  226. package/docs/fsExists.mjs.html +1 -1
  227. package/docs/fsExistsCore.mjs.html +1 -1
  228. package/docs/fsGetFileBasicHash.mjs.html +1 -1
  229. package/docs/fsGetFileBasicHashCore.mjs.html +1 -1
  230. package/docs/fsGetFileHash.mjs.html +1 -1
  231. package/docs/fsGetFileHashCore.mjs.html +1 -1
  232. package/docs/fsGetFileXxHash.mjs.html +1 -1
  233. package/docs/fsGetFileXxHashCore.mjs.html +1 -1
  234. package/docs/fsGetFilesInFolder.mjs.html +1 -1
  235. package/docs/fsGetFilesInFolderCore.mjs.html +1 -1
  236. package/docs/fsGetFilesWithHashInFolder.mjs.html +1 -1
  237. package/docs/fsGetFilesWithHashInFolderCore.mjs.html +1 -1
  238. package/docs/fsGetFolderBasicHash.mjs.html +1 -1
  239. package/docs/fsGetFolderBasicHashCore.mjs.html +1 -1
  240. package/docs/fsGetFoldersInFolder.mjs.html +1 -1
  241. package/docs/fsGetFoldersInFolderCore.mjs.html +1 -1
  242. package/docs/fsIsFile.mjs.html +1 -1
  243. package/docs/fsIsFileCore.mjs.html +1 -1
  244. package/docs/fsIsFolder.mjs.html +1 -1
  245. package/docs/fsIsFolderCore.mjs.html +1 -1
  246. package/docs/fsMergeFiles.mjs.html +1 -1
  247. package/docs/fsMergeFilesCore.mjs.html +1 -1
  248. package/docs/fsReadJson.mjs.html +1 -1
  249. package/docs/fsReadText.mjs.html +1 -1
  250. package/docs/fsReadTextCore.mjs.html +1 -1
  251. package/docs/fsRenameFile.mjs.html +1 -1
  252. package/docs/fsRenameFileCore.mjs.html +1 -1
  253. package/docs/fsRenameFolder.mjs.html +1 -1
  254. package/docs/fsRenameFolderCore.mjs.html +1 -1
  255. package/docs/fsSrlog.mjs.html +1 -1
  256. package/docs/fsSyncFolder.mjs.html +1 -1
  257. package/docs/fsSyncFolderCore.mjs.html +1 -1
  258. package/docs/fsTask.mjs.html +1 -1
  259. package/docs/fsTaskCp.mjs.html +1 -1
  260. package/docs/fsTreeFolder.mjs.html +1 -1
  261. package/docs/fsTreeFolderCore.mjs.html +1 -1
  262. package/docs/fsTreeFolderWithHash.mjs.html +1 -1
  263. package/docs/fsTreeFolderWithHashCore.mjs.html +1 -1
  264. package/docs/fsWatchFile.mjs.html +1 -1
  265. package/docs/fsWatchFolder.mjs.html +1 -1
  266. package/docs/fsWriteJson.mjs.html +1 -1
  267. package/docs/fsWriteText.mjs.html +1 -1
  268. package/docs/fsWriteTextCore.mjs.html +1 -1
  269. package/docs/genGradientColor.mjs.html +1 -1
  270. package/docs/genID.mjs.html +1 -1
  271. package/docs/genIDSeq.mjs.html +1 -1
  272. package/docs/genPm.mjs.html +1 -1
  273. package/docs/getBufferSize.mjs.html +1 -1
  274. package/docs/getClearPathName.mjs.html +1 -1
  275. package/docs/getCsvStrFromData.mjs.html +1 -1
  276. package/docs/getDataFromExcelFileU8Arr.mjs.html +1 -1
  277. package/docs/getDataFromExcelFileU8ArrDyn.mjs.html +1 -1
  278. package/docs/getEnv.mjs.html +1 -1
  279. package/docs/getErrorMessage.mjs.html +1 -1
  280. package/docs/getExcelU8ArrFromData.mjs.html +1 -1
  281. package/docs/getExcelU8ArrFromWorkbook.mjs.html +1 -1
  282. package/docs/getExcelWorkbookFromData.mjs.html +1 -1
  283. package/docs/getExcelWorkbookFromWorksheet.mjs.html +1 -1
  284. package/docs/getExcelWorksheetFromData.mjs.html +1 -1
  285. package/docs/getFileAccept.mjs.html +1 -1
  286. package/docs/getFileBasicHash.mjs.html +1 -1
  287. package/docs/getFileHash.mjs.html +1 -1
  288. package/docs/getFileName.mjs.html +1 -1
  289. package/docs/getFileNameExt.mjs.html +1 -1
  290. package/docs/getFileTrueName.mjs.html +1 -1
  291. package/docs/getFileType.mjs.html +1 -1
  292. package/docs/getFileXxHash.mjs.html +1 -1
  293. package/docs/getGlobal.mjs.html +1 -1
  294. package/docs/getObjSize.mjs.html +1 -1
  295. package/docs/getPathParent.mjs.html +1 -1
  296. package/docs/getTimeBetween.mjs.html +1 -1
  297. package/docs/getTimeFromUnit.mjs.html +1 -1
  298. package/docs/getTimeHp.mjs.html +1 -1
  299. package/docs/getTimeObject.mjs.html +1 -1
  300. package/docs/getTimeRange.mjs.html +1 -1
  301. package/docs/getUserAgent.mjs.html +1 -1
  302. package/docs/getUserAgentDyn.mjs.html +1 -1
  303. package/docs/getltdtkeys.mjs.html +1 -1
  304. package/docs/global.html +1 -1
  305. package/docs/haskey.mjs.html +1 -1
  306. package/docs/html2str.mjs.html +1 -1
  307. package/docs/htmlDecode.mjs.html +1 -1
  308. package/docs/htmlEncode.mjs.html +1 -1
  309. package/docs/importResources.mjs.html +1 -1
  310. package/docs/index.html +1 -1
  311. package/docs/interp1.mjs.html +1 -1
  312. package/docs/isDate.mjs.html +1 -1
  313. package/docs/isDev.mjs.html +1 -1
  314. package/docs/isEle.mjs.html +1 -1
  315. package/docs/isEleExistByID.mjs.html +1 -1
  316. package/docs/isEmail.mjs.html +1 -1
  317. package/docs/isErr.mjs.html +1 -1
  318. package/docs/isIE.mjs.html +1 -1
  319. package/docs/isNarrow.mjs.html +1 -1
  320. package/docs/isStrHasCapital.mjs.html +1 -1
  321. package/docs/isStrHasLetter.mjs.html +1 -1
  322. package/docs/isStrHasLowerCase.mjs.html +1 -1
  323. package/docs/isStrHasNumber.mjs.html +1 -1
  324. package/docs/isStrHasSpace.mjs.html +1 -1
  325. package/docs/isStrHasSymbol.mjs.html +1 -1
  326. package/docs/isUserIdentify.mjs.html +1 -1
  327. package/docs/isUserName.mjs.html +1 -1
  328. package/docs/isUserPw.mjs.html +1 -1
  329. package/docs/isWindow.mjs.html +1 -1
  330. package/docs/isab.mjs.html +1 -1
  331. package/docs/isarr.mjs.html +1 -1
  332. package/docs/isarr0.mjs.html +1 -1
  333. package/docs/isarr1.mjs.html +1 -1
  334. package/docs/isbigint.mjs.html +1 -1
  335. package/docs/isblob.mjs.html +1 -1
  336. package/docs/isbol.mjs.html +1 -1
  337. package/docs/isday.mjs.html +1 -1
  338. package/docs/isearr.mjs.html +1 -1
  339. package/docs/iseobj.mjs.html +1 -1
  340. package/docs/iser.mjs.html +1 -1
  341. package/docs/isernot.mjs.html +1 -1
  342. package/docs/isestr.mjs.html +1 -1
  343. package/docs/isfile.mjs.html +1 -1
  344. package/docs/isfun.mjs.html +1 -1
  345. package/docs/isint.mjs.html +1 -1
  346. package/docs/ismonth.mjs.html +1 -1
  347. package/docs/isn0int.mjs.html +1 -1
  348. package/docs/isn0num.mjs.html +1 -1
  349. package/docs/isnan.mjs.html +1 -1
  350. package/docs/isnbr.mjs.html +1 -1
  351. package/docs/isnint.mjs.html +1 -1
  352. package/docs/isnnum.mjs.html +1 -1
  353. package/docs/isnull.mjs.html +1 -1
  354. package/docs/isnum.mjs.html +1 -1
  355. package/docs/isobj.mjs.html +1 -1
  356. package/docs/isobj0.mjs.html +1 -1
  357. package/docs/isp0int.mjs.html +1 -1
  358. package/docs/isp0num.mjs.html +1 -1
  359. package/docs/ispint.mjs.html +1 -1
  360. package/docs/ispm.mjs.html +1 -1
  361. package/docs/ispnum.mjs.html +1 -1
  362. package/docs/isstr.mjs.html +1 -1
  363. package/docs/isstr0.mjs.html +1 -1
  364. package/docs/istime.mjs.html +1 -1
  365. package/docs/istimeTZ.mjs.html +1 -1
  366. package/docs/istimems.mjs.html +1 -1
  367. package/docs/istimemsTZ.mjs.html +1 -1
  368. package/docs/isu16arr.mjs.html +1 -1
  369. package/docs/isu8arr.mjs.html +1 -1
  370. package/docs/isundefined.mjs.html +1 -1
  371. package/docs/j2o.mjs.html +1 -1
  372. package/docs/judge.mjs.html +1 -1
  373. package/docs/keysmat2ltdt.mjs.html +1 -1
  374. package/docs/ltdt2vrtsable.mjs.html +1 -1
  375. package/docs/ltdtDiff.mjs.html +1 -1
  376. package/docs/ltdtDiffByKey.mjs.html +1 -1
  377. package/docs/ltdtkeys2mat.mjs.html +1 -1
  378. package/docs/ltdtkeysheads2mat.mjs.html +1 -1
  379. package/docs/ltdtmapping.mjs.html +1 -1
  380. package/docs/ltdtmerge.mjs.html +1 -1
  381. package/docs/ltdtpick.mjs.html +1 -1
  382. package/docs/ltdtpull.mjs.html +1 -1
  383. package/docs/mat2ltdt.mjs.html +1 -1
  384. package/docs/matConcat.mjs.html +1 -1
  385. package/docs/matDiff.mjs.html +1 -1
  386. package/docs/matat.mjs.html +1 -1
  387. package/docs/now2str.mjs.html +1 -1
  388. package/docs/now2strp.mjs.html +1 -1
  389. package/docs/nowDay2str.mjs.html +1 -1
  390. package/docs/nowms2str.mjs.html +1 -1
  391. package/docs/nowms2strp.mjs.html +1 -1
  392. package/docs/o2j.mjs.html +1 -1
  393. package/docs/obj2b64.mjs.html +1 -1
  394. package/docs/obj2pb64.mjs.html +1 -1
  395. package/docs/obj2str.mjs.html +1 -1
  396. package/docs/obj2stru8arr.mjs.html +1 -1
  397. package/docs/obj2u8arr.mjs.html +1 -1
  398. package/docs/objSortBy.mjs.html +1 -1
  399. package/docs/objSortByKeys.mjs.html +1 -1
  400. package/docs/oo.mjs.html +1 -1
  401. package/docs/openLink.mjs.html +1 -1
  402. package/docs/pb642obj.mjs.html +1 -1
  403. package/docs/pmChain.mjs.html +1 -1
  404. package/docs/pmConvertResolve.mjs.html +1 -1
  405. package/docs/pmDebounce.mjs.html +1 -1
  406. package/docs/pmHook.mjs.html +1 -1
  407. package/docs/pmHookReject.mjs.html +1 -1
  408. package/docs/pmIni.mjs.html +1 -1
  409. package/docs/pmInvResolve.mjs.html +1 -1
  410. package/docs/pmKeyMutex.mjs.html +1 -1
  411. package/docs/pmMap.mjs.html +1 -1
  412. package/docs/pmQueue.mjs.html +1 -1
  413. package/docs/pmSeries.mjs.html +1 -1
  414. package/docs/pmThrottle.mjs.html +1 -1
  415. package/docs/pmTimeout.mjs.html +1 -1
  416. package/docs/preciseNum.mjs.html +1 -1
  417. package/docs/pseudoRandom.mjs.html +1 -1
  418. package/docs/pseudoRandomIntRange.mjs.html +1 -1
  419. package/docs/pseudoRandomIntsNdpRange.mjs.html +1 -1
  420. package/docs/pseudoRandomIntsRange.mjs.html +1 -1
  421. package/docs/pseudoRandomRange.mjs.html +1 -1
  422. package/docs/pseudoRandomsRange.mjs.html +1 -1
  423. package/docs/queue.mjs.html +1 -1
  424. package/docs/random.mjs.html +1 -1
  425. package/docs/randomIntRange.mjs.html +1 -1
  426. package/docs/randomIntsNdpRange.mjs.html +1 -1
  427. package/docs/randomIntsRange.mjs.html +1 -1
  428. package/docs/randomRange.mjs.html +1 -1
  429. package/docs/randomsRange.mjs.html +1 -1
  430. package/docs/rang.mjs.html +1 -1
  431. package/docs/rep.mjs.html +1 -1
  432. package/docs/repObj.mjs.html +1 -1
  433. package/docs/replace.mjs.html +1 -1
  434. package/docs/replaceObj.mjs.html +1 -1
  435. package/docs/replacePlus.mjs.html +1 -1
  436. package/docs/round.mjs.html +1 -1
  437. package/docs/secd.mjs.html +1 -1
  438. package/docs/sep.mjs.html +1 -1
  439. package/docs/sepInt.mjs.html +1 -1
  440. package/docs/setIntervalHp.mjs.html +1 -1
  441. package/docs/setTimeoutHp.mjs.html +1 -1
  442. package/docs/sind.mjs.html +1 -1
  443. package/docs/split.mjs.html +1 -1
  444. package/docs/str2aes.mjs.html +1 -1
  445. package/docs/str2b64.mjs.html +1 -1
  446. package/docs/str2hint.mjs.html +1 -1
  447. package/docs/str2md5.mjs.html +1 -1
  448. package/docs/str2obj.mjs.html +1 -1
  449. package/docs/str2sha.mjs.html +1 -1
  450. package/docs/str2sha512.mjs.html +1 -1
  451. package/docs/str2u8arr.mjs.html +1 -1
  452. package/docs/strCompare.mjs.html +1 -1
  453. package/docs/strCompareForVersion.mjs.html +1 -1
  454. package/docs/strDiff.mjs.html +1 -1
  455. package/docs/strDiffToHtml.mjs.html +1 -1
  456. package/docs/strFindFuzz.mjs.html +1 -1
  457. package/docs/strFindSimilar.mjs.html +1 -1
  458. package/docs/strTruncate.mjs.html +1 -1
  459. package/docs/strdelleft.mjs.html +1 -1
  460. package/docs/strdelright.mjs.html +1 -1
  461. package/docs/strleft.mjs.html +1 -1
  462. package/docs/strmid.mjs.html +1 -1
  463. package/docs/strright.mjs.html +1 -1
  464. package/docs/stru8arr2obj.mjs.html +1 -1
  465. package/docs/tand.mjs.html +1 -1
  466. package/docs/time2day.mjs.html +1 -1
  467. package/docs/time2hour.mjs.html +1 -1
  468. package/docs/time2min.mjs.html +1 -1
  469. package/docs/timeTZ2day.mjs.html +1 -1
  470. package/docs/timeTZ2expire.mjs.html +1 -1
  471. package/docs/timeTZ2hour.mjs.html +1 -1
  472. package/docs/timeTZ2min.mjs.html +1 -1
  473. package/docs/timeTZ2past.mjs.html +1 -1
  474. package/docs/timems2day.mjs.html +1 -1
  475. package/docs/timems2hour.mjs.html +1 -1
  476. package/docs/timems2min.mjs.html +1 -1
  477. package/docs/timems2time.mjs.html +1 -1
  478. package/docs/timemsTZ2day.mjs.html +1 -1
  479. package/docs/timemsTZ2expire.mjs.html +1 -1
  480. package/docs/timemsTZ2hour.mjs.html +1 -1
  481. package/docs/timemsTZ2min.mjs.html +1 -1
  482. package/docs/timemsTZ2past.mjs.html +1 -1
  483. package/docs/timemsTZ2time.mjs.html +1 -1
  484. package/docs/treeObj.mjs.html +1 -1
  485. package/docs/treeToFilepath.mjs.html +1 -1
  486. package/docs/trim.mjs.html +1 -1
  487. package/docs/u16arr2b64.mjs.html +1 -1
  488. package/docs/u16arr2u8arr.mjs.html +1 -1
  489. package/docs/u8arr2ab.mjs.html +1 -1
  490. package/docs/u8arr2b64.mjs.html +1 -1
  491. package/docs/u8arr2blob.mjs.html +1 -1
  492. package/docs/u8arr2bs.mjs.html +1 -1
  493. package/docs/u8arr2obj.mjs.html +1 -1
  494. package/docs/u8arr2str.mjs.html +1 -1
  495. package/docs/u8arr2u16arr.mjs.html +1 -1
  496. package/docs/urlParse.mjs.html +1 -1
  497. package/docs/verifyValue.mjs.html +1 -1
  498. package/docs/waitFun.mjs.html +1 -1
  499. package/docs/wsemi.html +5 -5
  500. package/package.json +2 -2
  501. package/src/_cvOrmFindCases.mjs +16 -0
  502. package/src/cvOrmFind.mjs +160 -22
  503. package/test/cvOrmFind.test.mjs +666 -17
  504. package/test/tools/cvOrmFindRunner.mjs +7 -1
  505. package/toolfp/dist/wsemip.umd.js +1 -1
  506. package/toolparfor/dist/wpf.umd.js +1 -1
  507. package/toolparfor/dist/wpf_pmSeries.umd.js +1 -1
@@ -758,21 +758,21 @@ describe('cvOrmFind 異常結構(規則10)', function() {
758
758
  get a() {
759
759
  return 1
760
760
  }
761
- }), 'accessor property is not allowed', 'find')
761
+ }), 'accessor property is not allowed', 'find.a')
762
762
  rej(() => ({
763
763
  a: {
764
764
  get $gt() {
765
765
  return 1
766
766
  }
767
767
  }
768
- }), 'accessor property is not allowed', 'find.a')
768
+ }), 'accessor property is not allowed', 'find.a.$gt')
769
769
  rej(() => ({
770
770
  $and: [{
771
771
  get a() {
772
772
  return 1
773
773
  }
774
774
  }]
775
- }), 'accessor property is not allowed', 'find.$and[0]')
775
+ }), 'accessor property is not allowed', 'find.$and[0].a')
776
776
  rej(() => ({
777
777
  a: {
778
778
  $in: Object.defineProperty([], '0', {
@@ -806,12 +806,12 @@ describe('cvOrmFind 異常結構(規則10)', function() {
806
806
  })
807
807
 
808
808
  it('should reject non-enumerable properties and elements', function() {
809
- rej(() => hidden({}, 'g', 'A'), 'non-enumerable property is not allowed', 'find')
810
- rej(() => ({ a: hidden({}, '$gt', 1) }), 'non-enumerable property is not allowed', 'find.a')
811
- rej(() => ({ $and: [hidden({}, 'g', 'A')] }), 'non-enumerable property is not allowed', 'find.$and[0]')
809
+ rej(() => hidden({}, 'g', 'A'), 'non-enumerable property is not allowed', 'find.g')
810
+ rej(() => ({ a: hidden({}, '$gt', 1) }), 'non-enumerable property is not allowed', 'find.a.$gt')
811
+ rej(() => ({ $and: [hidden({}, 'g', 'A')] }), 'non-enumerable property is not allowed', 'find.$and[0].g')
812
812
  rej(() => ({ g: { $in: hidden(['A'], '0', 'A') } }), 'non-enumerable element is not allowed', 'find.g.$in[0]')
813
813
  rej(() => ({ $or: hidden([{ g: 'A' }], '0', { g: 'A' }) }), 'non-enumerable element is not allowed', 'find.$or[0]')
814
- rej(() => withEnumerable(hidden({}, 'a', 1), 'b', 2), 'non-enumerable property is not allowed', 'find')
814
+ rej(() => withEnumerable(hidden({}, 'a', 1), 'b', 2), 'non-enumerable property is not allowed', 'find.a')
815
815
  })
816
816
 
817
817
  it('should reject sparse arrays, extra own properties on arrays (string, symbol, index-like) and non-array array-likes', function() {
@@ -1038,8 +1038,8 @@ describe('cvOrmFind 檢查之值即使用之值(規則11)', function() {
1038
1038
  get a() {
1039
1039
  return 1
1040
1040
  }
1041
- }), 'accessor property is not allowed', 'find')
1042
- rej(() => hidden({}, 'a', 1), 'non-enumerable property is not allowed', 'find')
1041
+ }), 'accessor property is not allowed', 'find.a')
1042
+ rej(() => hidden({}, 'a', 1), 'non-enumerable property is not allowed', 'find.a')
1043
1043
  rej(() => ({
1044
1044
  a: {
1045
1045
  $in: Object.defineProperty([], '0', {
@@ -1262,7 +1262,7 @@ describe('cvOrmFind 錯誤格式與例外轉換(規則12)', function() {
1262
1262
  })
1263
1263
  rej(() => trap('ownKeys', new Error('SECRET-keys')), why, 'find')
1264
1264
  rej(() => ({ x: trap('ownKeys', new Error('SECRET-keys')) }), why, 'find.x')
1265
- rej(() => ({ $and: [{ a: 1 }, trap('getOwnPropertyDescriptor', new Error('SECRET-gopd'))] }), why, 'find.$and[1]')
1265
+ rej(() => ({ $and: [{ a: 1 }, trap('getOwnPropertyDescriptor', new Error('SECRET-gopd'))] }), why, 'find.$and[1].a') //描述子陷阱: 路徑含屬性鍵(規則12)
1266
1266
  rej(() => ({ $and: [{ a: 1 }, trap('getPrototypeOf', new Error('SECRET-gpo'))] }), why, 'find.$and[1]')
1267
1267
  rej(() => ({
1268
1268
  a: {
@@ -1370,7 +1370,7 @@ describe('cvOrmFind 錯誤格式與例外轉換(規則12)', function() {
1370
1370
  throw new Error('outer failure')
1371
1371
  }
1372
1372
  })
1373
- rej(() => ({ a: 1, c: q }), 'an exception was thrown', 'find.c')
1373
+ rej(() => ({ a: 1, c: q }), 'an exception was thrown', 'find.c.$gt')
1374
1374
  })
1375
1375
 
1376
1376
  })
@@ -1759,10 +1759,10 @@ describe('cvOrmFind 外審補強(R-FIND-01 規則2、9~12)', function() {
1759
1759
  rej(() => ({ a: { $eq: ta() } }), 'unsupported value type', 'find.a.$eq')
1760
1760
  })
1761
1761
 
1762
- it('documents the limit: a re-prototyped Map that carries own enumerable data properties is read as a plain bag of its own properties', function() {
1762
+ it('should reject a re-prototyped Map or Set that carries own enumerable data properties, at the top and as an element (formerly a documented limit, now rule 16)', function() {
1763
1763
  let m = Object.assign(nullp(new Map([['x', 1]])), { g: 'A' })
1764
- assert.deepStrictEqual(toPlain(cvOrmFind(m)), { g: 'A' })
1765
- assert.deepStrictEqual(toPlain(cvOrmFind({ $and: [Object.assign(nullp(new Set()), { g: 'A' })] })), { $and: [{ g: 'A' }] })
1764
+ rej(() => m, 'condition must be a plain object, got [object]', 'find')
1765
+ rej(() => ({ $and: [Object.assign(nullp(new Set()), { g: 'A' })] }), 'condition must be a plain object, got [object]', 'find.$and[0]')
1766
1766
  })
1767
1767
 
1768
1768
  it('should return a fresh object for {} and for an empty null-prototype input, and keep no state between results', function() {
@@ -2340,17 +2340,17 @@ describe('cvOrmFind 極大輸入(子行程, R-FIND-01)', function() {
2340
2340
 
2341
2341
  let run = (name, execArgv = ['--max-old-space-size=64', '--expose-gc'], timeout = 60000) => runChildCase(fpRunner, name, { execArgv, timeout })
2342
2342
 
2343
- let rejectedFast = (name, why) => {
2343
+ let rejectedFast = (name, why, maxMB = 40) => {
2344
2344
  let { r, status } = run(name)
2345
2345
  assert.strictEqual(status, 0, name)
2346
2346
  assert.strictEqual(r.ok, false, `${name}: must reject`)
2347
2347
  assert.ok(r.err.startsWith('invalid find: ') && r.err.includes(why), `${name}: ${r.err}`)
2348
2348
  assert.ok(r.ms < 5000, `${name}: ${r.ms} ms`)
2349
- assert.ok(r.heapMB < 40, `${name}: ${r.heapMB} MB`)
2349
+ assert.ok(r.heapMB < maxMB, `${name}: ${r.heapMB} MB`)
2350
2350
  }
2351
2351
 
2352
2352
  it('should reject exponential shared references (depth 25 and 40) within the node budget, quickly and without much memory', function() {
2353
- rejectedFast('dag:25', 'condition is too large')
2353
+ rejectedFast('dag:25', 'condition is too large', 56) //規則16之記錄陣列與路徑節點在約10萬節點下多保留約18 MB(HEAD 19 MB、本版 37~44 MB; 未強制GC之heapUsed增量)
2354
2354
  rejectedFast('dag:40', 'nesting is too deep')
2355
2355
  })
2356
2356
 
@@ -2386,6 +2386,16 @@ describe('cvOrmFind 極大輸入(子行程, R-FIND-01)', function() {
2386
2386
  }
2387
2387
  })
2388
2388
 
2389
+ it('should accept one string referenced 10000 times (100M code units in total) in a 64 MB process: the clone is skipped above the string limit', function() {
2390
+ for (let name of ['shared:10000:10000', 'shared:100:30000']) {
2391
+ let { r, status } = run(name)
2392
+ assert.strictEqual(status, 0, name)
2393
+ assert.strictEqual(r.ok, true, `${name}: ${r.err}`)
2394
+ assert.ok(r.ms < 5000, `${name}: ${r.ms} ms`)
2395
+ assert.ok(r.heapMB < 40, `${name}: ${r.heapMB} MB`)
2396
+ }
2397
+ })
2398
+
2389
2399
  it('should accept a 5MB string and 99999 fields in a limited process', function() {
2390
2400
  let { r, status } = run('str:5000000', ['--max-old-space-size=96', '--expose-gc'])
2391
2401
  assert.strictEqual(status, 0)
@@ -2398,3 +2408,642 @@ describe('cvOrmFind 極大輸入(子行程, R-FIND-01)', function() {
2398
2408
  })
2399
2409
 
2400
2410
  })
2411
+
2412
+
2413
+ describe('cvOrmFind 內建品牌物件於任何位置與 Proxy 之處置(規則2、16)', function() {
2414
+
2415
+ //可改原型之內建品牌(mk 每次回傳全新物件); 規則16: 探針名單內者於任何位置、空或帶自有鍵皆 reject
2416
+ let brands = [
2417
+ ['Map', () => new Map([['id', 'a1']])],
2418
+ ['Set', () => new Set(['a1'])],
2419
+ ['WeakMap', () => new WeakMap()],
2420
+ ['WeakSet', () => new WeakSet()],
2421
+ ['Promise', () => Promise.resolve({})],
2422
+ ['ArrayBuffer', () => new ArrayBuffer(4)],
2423
+ ['SharedArrayBuffer', () => new SharedArrayBuffer(4)],
2424
+ ['DataView', () => new DataView(new ArrayBuffer(4))],
2425
+ ['Uint8Array', () => new Uint8Array(2)],
2426
+ ['Float64Array', () => new Float64Array(2)],
2427
+ ['BigInt64Array', () => new BigInt64Array(2)],
2428
+ ['Symbol wrapper', () => Object(Symbol('s'))],
2429
+ ['BigInt wrapper', () => Object(BigInt(1))],
2430
+ ['WeakRef', () => new WeakRef({ id: 'a1' })],
2431
+ ['FinalizationRegistry', () => new FinalizationRegistry(() => {})],
2432
+ ]
2433
+ let noIndexKeys = brands.filter(([n]) => !/Array$/.test(n)) //TypedArray之自有索引鍵與元素數成正比
2434
+ let protos = [['null', null], ['Object.prototype', Object.prototype]]
2435
+ let hijack = (mk, proto, keys) => Object.assign(Object.setPrototypeOf(mk(), proto), keys)
2436
+ let PLAIN = 'condition must be a plain object, got [object]'
2437
+ let TYPE = 'unsupported value type [object]'
2438
+
2439
+ it('should reject every built-in brand with a changed prototype at the top (null and Object.prototype; empty or with own keys)', function() {
2440
+ let n = 0
2441
+ for (let [name, mk] of brands) {
2442
+ for (let [pn, p] of protos) {
2443
+ rej(() => hijack(mk, p, { g: 'A' }), PLAIN, 'find')
2444
+ rej(() => hijack(mk, p, {}), 'condition must be a plain object', 'find')
2445
+ n += 2
2446
+ assert.ok(true, `${name} ${pn}`)
2447
+ }
2448
+ }
2449
+ assert.strictEqual(n, 60)
2450
+ })
2451
+
2452
+ it('should reject them as $and / $or elements and in nested logic when they carry own keys (the path is the element)', function() {
2453
+ let positions = [
2454
+ ['$or element', (o) => ({ $or: [o] }), 'find.$or[0]'],
2455
+ ['$and element', (o) => ({ $and: [{ a: 1 }, o] }), 'find.$and[1]'],
2456
+ ['$or after a field', (o) => ({ g: 'A', $or: [{ b: 1 }, o] }), 'find.$or[1]'],
2457
+ ['nested', (o) => ({ g: 'A', $and: [{ $or: [{ b: 1 }, o] }] }), 'find.$and[0].$or[1]'],
2458
+ ]
2459
+ for (let [, mk] of brands) {
2460
+ for (let [, p] of protos) {
2461
+ for (let [, wrap, at] of positions) {
2462
+ rej(() => wrap(hijack(mk, p, { g: 'A' })), PLAIN, at)
2463
+ }
2464
+ }
2465
+ }
2466
+ })
2467
+
2468
+ it('should reject them as field operator objects when they carry operator-shaped own keys', function() {
2469
+ for (let [, mk] of brands) {
2470
+ for (let [, p] of protos) {
2471
+ rej(() => ({ a: hijack(mk, p, { $gt: 1 }) }), TYPE, 'find.a')
2472
+ rej(() => ({ a: hijack(mk, p, { $in: [1] }) }), TYPE, 'find.a')
2473
+ rej(() => ({ $or: [{ x: 1 }, { a: hijack(mk, p, { $eq: 'A' }) }] }), TYPE, 'find.$or[1].a')
2474
+ }
2475
+ }
2476
+ })
2477
+
2478
+ it('should reject them empty as elements and operator objects (empty is never a condition there)', function() {
2479
+ for (let [, mk] of brands) {
2480
+ for (let [, p] of protos) {
2481
+ let e1 = errOf(() => ({ $or: [hijack(mk, p, {})] }))
2482
+ assert.ok(reMsg.test(e1.message) && e1.message.endsWith(' at [find.$or[0]]'), e1.message)
2483
+ let e2 = errOf(() => ({ a: hijack(mk, p, {}) }))
2484
+ assert.ok(reMsg.test(e2.message) && e2.message.endsWith(' at [find.a]'), e2.message)
2485
+ }
2486
+ }
2487
+ })
2488
+
2489
+ it('should keep rejecting the brands whose internal tag stays visible after the prototype change (RegExp, Error, wrappers, arguments) at every position', function() {
2490
+ let tagged = [
2491
+ ['RegExp', () => /x/],
2492
+ ['Error', () => new Error('x')],
2493
+ ['Number wrapper', () => new Number(1)],
2494
+ ['String wrapper', () => new String('s')],
2495
+ ['Boolean wrapper', () => new Boolean(true)],
2496
+ ['Date', () => new Date(0)],
2497
+ ['arguments', () => (function() {
2498
+ return arguments
2499
+ })(1)],
2500
+ ]
2501
+ for (let [name, mk] of tagged) {
2502
+ for (let [, p] of protos) {
2503
+ rej(() => hijack(mk, p, { g: 'A' }), 'condition must be a plain object', 'find')
2504
+ rej(() => ({ $or: [hijack(mk, p, { g: 'A' })] }), 'condition must be a plain object', 'find.$or[0]')
2505
+ if (name !== 'Date') {
2506
+ rej(() => ({ a: hijack(mk, p, { $gt: 1 }) }), 'unsupported value type', 'find.a') //Date於欄位值位置為合法純量
2507
+ }
2508
+ }
2509
+ }
2510
+ })
2511
+
2512
+ it('should not mistake plain objects for brands (keys named like brand methods, null prototype, constructor-like names), through the clone path and the exact-probe path', function() {
2513
+ let trees = [
2514
+ () => ({ size: 1, has: 2, then: 3, byteLength: 4, deref: 5, unregister: 6, valueOf: 7, constructor: 8 }),
2515
+ () => ({ $or: [{ size: 1, then: 2 }, { byteLength: 3 }] }),
2516
+ () => ({ a: { $gt: 1 }, $and: [Object.assign(Object.create(null), { has: 1, size: 2 })] }),
2517
+ () => ({ $and: [{ $or: [{ then: 1 }, Object.assign(Object.create(null), { deref: 'x' })] }] }),
2518
+ () => ({ then: { $in: [1, 2] }, size: { $gt: 1, $lt: 5 }, constructor: null }),
2519
+ ]
2520
+ for (let mk of trees) {
2521
+ ok(mk)
2522
+ ok(() => ({ $and: [new Proxy({ z: 1 }, {}), mk()] })) //樹內有Proxy: 結構化複製失敗, 改由探針逐物件判定, 不得把一般物件判成品牌
2523
+ }
2524
+ })
2525
+
2526
+ it('should report the outermost brand object first (objects are recorded before their children)', function() {
2527
+ let inner = hijack(() => new Set(), null, { h: 1 })
2528
+ let outer = hijack(() => new Map(), null, { $or: [inner] })
2529
+ rej(() => ({ $or: [outer] }), PLAIN, 'find.$or[0]')
2530
+ })
2531
+
2532
+ it('should report the key of a property that the ownKeys trap lists but that does not exist', function() {
2533
+ rej(() => new Proxy({ a: 1 }, { ownKeys: () => ['a', 'g'] }), 'non-enumerable property is not allowed', 'find.g')
2534
+ })
2535
+
2536
+ it('should not depend on the internal tag of the copies: a polluted Object.prototype[@@toStringTag] after load keeps a real empty find as "all"', function() {
2537
+ Object.defineProperty(Object.prototype, Symbol.toStringTag, { value: 'Polluted', configurable: true })
2538
+ try {
2539
+ assert.deepStrictEqual(Reflect.ownKeys(cvOrmFind(Object.create(null))), [])
2540
+ assert.deepStrictEqual(toPlain(cvOrmFind(Object.assign(Object.create(null), { a: Object.assign(Object.create(null), { $gt: 1 }) }))), { a: { $gt: 1 } })
2541
+ rej(() => Object.setPrototypeOf(new Map(), null), 'condition must be a plain object', 'find')
2542
+ }
2543
+ finally {
2544
+ delete Object.prototype[Symbol.toStringTag]
2545
+ }
2546
+ })
2547
+
2548
+ it('should still reject brand objects when the total string length is above the clone limit (the exact probes decide)', function() {
2549
+ this.timeout(60000)
2550
+ let big = 'x'.repeat(5000000)
2551
+ rej(() => ({ big, $or: [hijack(() => new Map(), null, { g: 'A' })] }), PLAIN, 'find.$or[0]')
2552
+ ok(() => ({ big, $or: [{ g: 'A' }] }))
2553
+ })
2554
+
2555
+ it('should use the cheap path for a single condition object and for a typical find (the exact-probe path is far slower)', function() {
2556
+ let best = (f) => {
2557
+ let b = Infinity
2558
+ for (let r = 0; r < 30; r++) {
2559
+ let t0 = process.hrtime.bigint()
2560
+ for (let i = 0; i < 40; i++) {
2561
+ f()
2562
+ }
2563
+ b = Math.min(b, Number(process.hrtime.bigint() - t0))
2564
+ }
2565
+ return b
2566
+ }
2567
+ let typical = () => ({ name: 'a', age: { $gt: 20, $in: [21, 22] }, $or: [{ g: 'A' }, { g: null }] })
2568
+ let fast1 = best(() => cvOrmFind({ g: 'A' }))
2569
+ let slow1 = best(() => cvOrmFind(new Proxy({ g: 'A' }, {}))) //1個條件物件, 逐物件探針約25倍
2570
+ let fast4 = best(() => cvOrmFind(typical()))
2571
+ let slow4 = best(() => cvOrmFind({ $and: [new Proxy({ z: 1 }, {}), typical()] })) //約20倍
2572
+ assert.ok(fast1 * 4 < slow1, `${fast1} vs ${slow1}`)
2573
+ assert.ok(fast4 * 4 < slow4, `${fast4} vs ${slow4}`)
2574
+ })
2575
+
2576
+ it('should reject an empty-looking Proxy at the top: the only place where emptiness means "all" must be provably a real empty object', function() {
2577
+ let why = 'an empty find must be an ordinary object'
2578
+ rej(() => new Proxy({}, {}), why, 'find')
2579
+ rej(() => new Proxy(Object.create(null), {}), why, 'find')
2580
+ rej(() => new Proxy(Object.setPrototypeOf(new Map(), null), {}), why, 'find') //外審證據: 曾被當空條件而使 delAll 刪光全表
2581
+ rej(() => new Proxy(Object.setPrototypeOf(new Set(), Object.prototype), {}), why, 'find')
2582
+ for (let [, mk] of noIndexKeys) {
2583
+ for (let [, p] of protos) {
2584
+ rej(() => new Proxy(hijack(mk, p, {}), {}), why, 'find')
2585
+ }
2586
+ }
2587
+ //陷阱回報零個自有鍵而目標有鍵
2588
+ rej(() => new Proxy({ g: 'A' }, {
2589
+ ownKeys() {
2590
+ return []
2591
+ }
2592
+ }), why, 'find')
2593
+ //非頂層之空物件本即 reject(沿用既有原因)
2594
+ rej(() => ({ $or: [new Proxy({}, {})] }), 'empty condition', 'find.$or[0]')
2595
+ rej(() => ({ a: new Proxy({}, {}) }), 'empty operator object', 'find.a')
2596
+ })
2597
+
2598
+ it('should accept a non-empty Proxy for what its traps expose (read once), also a Proxy over a re-prototyped built-in with own keys: known limitation 5', function() {
2599
+ ok(() => new Proxy({ g: 'A' }, {}))
2600
+ ok(() => ({ $or: [new Proxy({ g: 'A' }, {}), { b: 1 }] }))
2601
+ let m = hijack(() => new Map(), null, { g: 'A' })
2602
+ assert.deepStrictEqual(toPlain(cvOrmFind(new Proxy(m, {}))), { g: 'A' })
2603
+ assert.deepStrictEqual(toPlain(cvOrmFind({ $or: [new Proxy(m, {})] })), { $or: [{ g: 'A' }] })
2604
+ //原型未改之品牌物件被Proxy包住: 原型檢查(Map.prototype)擋下
2605
+ rej(() => new Proxy(new Map([['g', 'A']]), {}), 'condition must be a plain object', 'find')
2606
+ rej(() => ({ $or: [new Proxy(new Set(), {})] }), 'condition must be a plain object', 'find.$or[0]')
2607
+ })
2608
+
2609
+ it('should give the same verdicts through the exact-probe path (a Proxy elsewhere in the tree makes the structured clone fail)', function() {
2610
+ let px = () => new Proxy({ z: 1 }, {})
2611
+ let pop = () => new Proxy({ $gt: 1 }, {})
2612
+ ok(() => ({ $and: [px(), { g: 'A' }] }))
2613
+ ok(() => ({ a: pop(), $or: [{ g: 'A' }, px()] }))
2614
+ for (let [, mk] of brands) {
2615
+ for (let [, p] of protos) {
2616
+ rej(() => ({ $and: [px(), hijack(mk, p, { g: 'A' })] }), PLAIN, 'find.$and[1]')
2617
+ rej(() => ({ $and: [px(), { $or: [{ b: 1 }, hijack(mk, p, { g: 'A' })] }] }), PLAIN, 'find.$and[1].$or[1]')
2618
+ rej(() => ({ a: pop(), b: hijack(mk, p, { $gt: 1 }) }), TYPE, 'find.b')
2619
+ rej(() => ({ $or: [hijack(mk, p, { g: 'A' }), px()] }), PLAIN, 'find.$or[0]')
2620
+ }
2621
+ }
2622
+ })
2623
+
2624
+ it('should report the first brand object in visiting order, with its own path', function() {
2625
+ let m1 = () => hijack(() => new Map(), null, { g: 'A' })
2626
+ let s2 = () => hijack(() => new Set(), null, { g: 'B' })
2627
+ rej(() => ({ $or: [{ x: 1 }, m1(), s2()] }), PLAIN, 'find.$or[1]')
2628
+ rej(() => ({ $or: [{ x: 1 }, s2(), m1()] }), PLAIN, 'find.$or[1]')
2629
+ rej(() => ({ $and: [{ $or: [{ x: 1 }, m1()] }, s2()] }), PLAIN, 'find.$and[0].$or[1]')
2630
+ })
2631
+
2632
+ let code = fs.readFileSync(fpSrc, 'utf8').replace(/^export default cvOrmFind\s*$/m, 'globalThis.__f = cvOrmFind')
2633
+
2634
+ //於 vm 內建構品牌物件與呼叫(同領域): ctxObj 決定該領域有無 structuredClone 及其行為
2635
+ let runInRealm = (ctxObj) => {
2636
+ let ctx = vm.createContext(ctxObj)
2637
+ vm.runInContext(code, ctx)
2638
+ return JSON.parse(vm.runInContext(`(function() {
2639
+ let f = globalThis.__f
2640
+ let t = (v) => { try { f(v); return 'ACCEPT' } catch (e) { return e.message } }
2641
+ let hijack = (o, p, keys) => Object.assign(Object.setPrototypeOf(o, p), keys)
2642
+ let mks = {
2643
+ Map: () => new Map(), Set: () => new Set(), WeakMap: () => new WeakMap(), WeakSet: () => new WeakSet(), Promise: () => Promise.resolve(1),
2644
+ ArrayBuffer: () => new ArrayBuffer(2), DataView: () => new DataView(new ArrayBuffer(2)), Uint8Array: () => new Uint8Array(2),
2645
+ WeakRef: () => new WeakRef({}), FinalizationRegistry: () => new FinalizationRegistry(() => {}),
2646
+ SymbolWrapper: () => Object(Symbol('s')), BigIntWrapper: () => Object(BigInt(1))
2647
+ }
2648
+ let res = { brands: {} }
2649
+ for (let n of Object.keys(mks)) {
2650
+ let mk = mks[n]
2651
+ res.brands[n] = [
2652
+ t(hijack(mk(), null, { g: 'A' })),
2653
+ t(hijack(mk(), Object.prototype, { g: 'A' })),
2654
+ t({ $or: [hijack(mk(), null, { g: 'A' })] }),
2655
+ t({ a: hijack(mk(), null, { $gt: 1 }) }),
2656
+ t(hijack(mk(), null, {})),
2657
+ ]
2658
+ }
2659
+ res.proxyEmpty = t(new Proxy({}, {}))
2660
+ res.proxyHiddenMap = t(new Proxy(Object.setPrototypeOf(new Map(), null), {}))
2661
+ res.proxyNonEmpty = t(new Proxy({ g: 'A' }, {}))
2662
+ res.plain = [t({ g: 'A' }), t({ $or: [{ g: 'A' }, { a: { $gt: 1 } }] }), t({}), t(undefined)]
2663
+ res.hasClone = typeof structuredClone
2664
+ return JSON.stringify(res)
2665
+ })()`, ctx))
2666
+ }
2667
+ let wantBrand = [
2668
+ `invalid find: ${PLAIN} at [find]`,
2669
+ `invalid find: ${PLAIN} at [find]`,
2670
+ `invalid find: ${PLAIN} at [find.$or[0]]`,
2671
+ `invalid find: ${TYPE} at [find.a]`,
2672
+ `invalid find: ${PLAIN} at [find]`,
2673
+ ]
2674
+
2675
+ it('should reject all brands in a realm without structuredClone (exact probes only); an empty Proxy cannot be told apart there (known limitation 9)', function() {
2676
+ let r = runInRealm({})
2677
+ assert.strictEqual(r.hasClone, 'undefined')
2678
+ for (let [n, v] of Object.entries(r.brands)) {
2679
+ assert.deepStrictEqual(v, wantBrand, n)
2680
+ }
2681
+ assert.strictEqual(r.proxyEmpty, 'ACCEPT')
2682
+ assert.strictEqual(r.proxyHiddenMap, 'ACCEPT')
2683
+ assert.strictEqual(r.proxyNonEmpty, 'ACCEPT')
2684
+ assert.deepStrictEqual(r.plain, ['ACCEPT', 'ACCEPT', 'ACCEPT', 'ACCEPT'])
2685
+ })
2686
+
2687
+ it('should not trust a structuredClone that fails its self-check at load (no throw on a Proxy, always throwing, a Map not kept as a Map): exact probes decide', function() {
2688
+ let throwOnce = () => {
2689
+ let n = 0
2690
+ return () => {
2691
+ n++
2692
+ if (n === 1) {
2693
+ throw new Error('proxy refused')
2694
+ }
2695
+ return {} //Map與{}皆複製成一般物件(Map之品牌不保留)
2696
+ }
2697
+ }
2698
+ //Proxy拒絕、Map保留品牌, 但{}複製成null原型物件(與執行期要求之一般物件不符)
2699
+ let nullProtoCopy = () => {
2700
+ let n = 0
2701
+ return () => {
2702
+ n++
2703
+ if (n === 1) {
2704
+ throw new Error('proxy refused')
2705
+ }
2706
+ return n === 3 ? new Map() : Object.create(null) //自檢呼叫順序: Proxy、{}、Map、{a:1}
2707
+ }
2708
+ }
2709
+ //Map被複製成一般物件(品牌樣本為null原型、一律拋錯, 故只有Map子句能擋下它)
2710
+ let tagOf = (v) => Object.prototype.toString.call(v)
2711
+ let flattenMap = () => {
2712
+ let n = 0
2713
+ return (v) => {
2714
+ n++
2715
+ if (n === 1) {
2716
+ throw new Error('proxy refused')
2717
+ }
2718
+ if (tagOf(v) === '[object Map]') {
2719
+ return {}
2720
+ }
2721
+ if (Array.isArray(v)) {
2722
+ return Array.prototype.map.call(v, () => ({})) //記錄陣列(null原型陣列)之元素全複製成一般物件
2723
+ }
2724
+ if (Object.getPrototypeOf(v) === null) {
2725
+ throw new Error('sample refused')
2726
+ }
2727
+ return {}
2728
+ }
2729
+ }
2730
+ //{}與{a:1}之複本原型非Object.prototype(其原型本身仍有原型): 原型有效性之自檢擋下; 若未擋下, 其後對真正之 {} 拋錯將使 {} 被拒
2731
+ let badProtoShim = () => {
2732
+ let n = 0
2733
+ let p2 = Object.create({})
2734
+ return (v) => {
2735
+ n++
2736
+ if (n === 1) {
2737
+ throw new Error('proxy refused')
2738
+ }
2739
+ if (n === 3) {
2740
+ return new Map()
2741
+ }
2742
+ if (n <= 4) {
2743
+ return Object.create(p2)
2744
+ }
2745
+ if (Object.getPrototypeOf(v) === null) {
2746
+ return Object.create(null)
2747
+ }
2748
+ if (Array.isArray(v)) {
2749
+ return Array.prototype.map.call(v, () => Object.create(null))
2750
+ }
2751
+ throw new Error('refused after self-check')
2752
+ }
2753
+ }
2754
+ //{}之複本帶自有鍵(Proxy拋錯、Map保留、品牌樣本拋錯、記錄陣列複本皆一般): 僅「{}複本須無自有鍵」之自檢子句能擋下; 未擋下則真正之 {} 會被拒
2755
+ let keyedEmpty = () => {
2756
+ let n = 0
2757
+ return (v) => {
2758
+ n++
2759
+ if (n === 1) {
2760
+ throw new Error('proxy refused')
2761
+ }
2762
+ if (n === 3) {
2763
+ return new Map()
2764
+ }
2765
+ if (Array.isArray(v)) {
2766
+ return Array.prototype.map.call(v, () => ({}))
2767
+ }
2768
+ if (Object.getPrototypeOf(v) === null) {
2769
+ throw new Error('sample refused')
2770
+ }
2771
+ return { x: 1 }
2772
+ }
2773
+ }
2774
+ for (let mkShim of [() => (v) => v, () => () => {
2775
+ throw new Error('no clone')
2776
+ }, throwOnce, nullProtoCopy, flattenMap, badProtoShim, keyedEmpty]) {
2777
+ let r = runInRealm({ structuredClone: mkShim() })
2778
+ assert.strictEqual(r.hasClone, 'function')
2779
+ for (let [n, v] of Object.entries(r.brands)) {
2780
+ assert.deepStrictEqual(v, wantBrand, n)
2781
+ }
2782
+ assert.strictEqual(r.proxyEmpty, 'ACCEPT')
2783
+ assert.deepStrictEqual(r.plain, ['ACCEPT', 'ACCEPT', 'ACCEPT', 'ACCEPT'])
2784
+ }
2785
+ })
2786
+
2787
+ it('should require every copy to carry the Object.prototype learned at load, and refuse a clone that turns re-prototyped brand samples into plain objects', function() {
2788
+ //通過前四次自檢(呼叫順序: Proxy拋錯、{}、Map保留、{a:1}; {}與{a:1}皆為同一原型之空物件)後, 對其餘呼叫(品牌樣本與正式呼叫)回傳 other(v)
2789
+ let afterBasicChecks = (other) => () => {
2790
+ let n = 0
2791
+ return (v) => {
2792
+ n++
2793
+ if (n === 1) {
2794
+ throw new Error('proxy refused')
2795
+ }
2796
+ if (n === 3) {
2797
+ return new Map()
2798
+ }
2799
+ if (n <= 4) {
2800
+ return {}
2801
+ }
2802
+ return other(v)
2803
+ }
2804
+ }
2805
+ //(a)品牌樣本之複本原型為null(不是自檢時學到之Object.prototype)而被接受使用; 其後陣列內元素之複本原型亦為null, 永不構成證明
2806
+ let protoOff = afterBasicChecks((v) => (Array.isArray(v) ? Array.prototype.map.call(v, () => Object.create(null)) : Object.create(null)))
2807
+ let r = runInRealm({ structuredClone: protoOff() })
2808
+ for (let [n, v] of Object.entries(r.brands)) {
2809
+ assert.deepStrictEqual(v, wantBrand, n) //不合要求之複本不得當作證明, 改由探針判定
2810
+ }
2811
+ assert.strictEqual(r.plain[0], 'ACCEPT')
2812
+ assert.strictEqual(r.plain[3], 'ACCEPT')
2813
+ //(b)把品牌樣本也複製成一般物件(原型為學到之Object.prototype): 自檢不通過而停用, 全程改走探針, 真正之 {} 仍為全部
2814
+ let samplesPlain = afterBasicChecks(() => ({}))
2815
+ let r2 = runInRealm({ structuredClone: samplesPlain() })
2816
+ for (let [n, v] of Object.entries(r2.brands)) {
2817
+ assert.deepStrictEqual(v, wantBrand, n)
2818
+ }
2819
+ assert.deepStrictEqual(r2.plain, ['ACCEPT', 'ACCEPT', 'ACCEPT', 'ACCEPT'])
2820
+ assert.strictEqual(r2.proxyEmpty, 'ACCEPT') //已停用: 空Proxy無法辨出(已知限制9)
2821
+ })
2822
+
2823
+ it('should work with a structuredClone that belongs to another realm (the global of a vm sandbox such as jest): same verdicts, a real {} is still "all"', function() {
2824
+ let r = runInRealm({ structuredClone }) //主領域之原生函數, 複本之原型為主領域之Object.prototype
2825
+ assert.strictEqual(r.hasClone, 'function')
2826
+ for (let [n, v] of Object.entries(r.brands)) {
2827
+ assert.deepStrictEqual(v, wantBrand, n)
2828
+ }
2829
+ let why = 'invalid find: an empty find must be an ordinary object at [find]'
2830
+ assert.strictEqual(r.proxyEmpty, why) //自檢通過, 故空Proxy被擋(與無structuredClone之領域不同)
2831
+ assert.strictEqual(r.proxyHiddenMap, why)
2832
+ assert.strictEqual(r.proxyNonEmpty, 'ACCEPT')
2833
+ assert.deepStrictEqual(r.plain, ['ACCEPT', 'ACCEPT', 'ACCEPT', 'ACCEPT']) //含真正之 {}: 不得因複本原型屬他領域而被誤拒
2834
+ })
2835
+
2836
+ it('should judge the recorded objects themselves: a later trap that swaps an earlier brand object out of the tree does not hide it', function() {
2837
+ let m = hijack(() => new Map(), null, { g: 'A' })
2838
+ let arr = [m]
2839
+ let p = new Proxy({ z: 1 }, {
2840
+ getOwnPropertyDescriptor(t, k) {
2841
+ arr[0] = { g: 'B' } //先前已讀並接受之Map被換掉
2842
+ arr[1] = { z: 2 } //連Proxy自己也換掉, 走訪後 find 可達之樹已無Proxy與Map
2843
+ return Reflect.getOwnPropertyDescriptor(t, k)
2844
+ }
2845
+ })
2846
+ arr.push(p)
2847
+ rej(() => ({ $and: arr }), PLAIN, 'find.$and[0]')
2848
+ //運算子物件位置
2849
+ let top = { a: hijack(() => new Set(), null, { $gt: 1 }) }
2850
+ top.b = new Proxy({ $gt: 2 }, {
2851
+ getOwnPropertyDescriptor(t, k) {
2852
+ top.a = { $gt: 1 } //先前讀過之Set被換掉
2853
+ top.b = { $gt: 2 } //Proxy自己也換掉
2854
+ return Reflect.getOwnPropertyDescriptor(t, k)
2855
+ }
2856
+ })
2857
+ rej(() => top, TYPE, 'find.a')
2858
+ })
2859
+
2860
+ it('should recognise detached and out-of-bounds DataViews and detached TypedArrays (their byteLength getters throw)', function() {
2861
+ if (typeof ArrayBuffer.prototype.transfer !== 'function') {
2862
+ this.skip()
2863
+ }
2864
+ let detachedDv = () => {
2865
+ let ab = new ArrayBuffer(8)
2866
+ let dv = new DataView(ab)
2867
+ ab.transfer()
2868
+ return dv
2869
+ }
2870
+ let outOfBoundsDv = () => {
2871
+ let rab = new ArrayBuffer(8, { maxByteLength: 16 })
2872
+ let dv = new DataView(rab, 4)
2873
+ rab.resize(2)
2874
+ return dv
2875
+ }
2876
+ let detachedTa = () => {
2877
+ let ab = new ArrayBuffer(8)
2878
+ let ta = new Uint8Array(ab)
2879
+ ab.transfer()
2880
+ return ta
2881
+ }
2882
+ for (let mk of [detachedDv, outOfBoundsDv, detachedTa]) {
2883
+ for (let [, p] of protos) {
2884
+ rej(() => hijack(mk, p, { g: 'A' }), PLAIN, 'find')
2885
+ rej(() => ({ $or: [hijack(mk, p, { g: 'A' })] }), PLAIN, 'find.$or[0]')
2886
+ rej(() => ({ a: hijack(mk, p, { $gt: 1 }) }), TYPE, 'find.a')
2887
+ rej(() => ({ $and: [new Proxy({ z: 1 }, {}), hijack(mk, p, { g: 'A' })] }), PLAIN, 'find.$and[1]') //精確探針路徑
2888
+ }
2889
+ }
2890
+ })
2891
+
2892
+ it('documents the limits: objects whose brand is not observable (private-field class or host instance cloned to {}; a Promise with an own scalar constructor key) are not recognised', function() {
2893
+ //含私有欄位之類別實例與宿主物件(AbortController)改原型為null後複製成空物件, 純JS無法與Object.create(null)區分: 頂層空視為「全部」
2894
+ class Priv {
2895
+ #p = 1
2896
+ get p() {
2897
+ return this.#p
2898
+ }
2899
+ }
2900
+ assert.deepStrictEqual(Reflect.ownKeys(cvOrmFind(Object.setPrototypeOf(new Priv(), null))), [])
2901
+ if (typeof AbortController === 'function') {
2902
+ //Node 24 之 AbortController 無自有鍵(實例狀態在私有欄位): 與 Priv 同形; 他版本若有自有 Symbol 鍵則被 reject, 兩者皆非回歸
2903
+ let ac = Object.setPrototypeOf(new AbortController(), null)
2904
+ let keys = Reflect.ownKeys(ac)
2905
+ if (keys.length === 0) {
2906
+ assert.deepStrictEqual(Reflect.ownKeys(cvOrmFind(ac)), [])
2907
+ }
2908
+ }
2909
+ //Promise.prototype.then 先查 constructor(物種建構子), 自有 constructor 為純量時拋錯而被探針當成非Promise;
2910
+ //constructor 為合法欄名, 亦不可據以拒收; 結構化複製使真Promise拋錯, 但無法指認是哪一個物件
2911
+ let p = Object.assign(Object.setPrototypeOf(Promise.resolve(1), null), { constructor: 0 })
2912
+ assert.deepStrictEqual(toPlain(cvOrmFind(p)), { constructor: 0 })
2913
+ assert.deepStrictEqual(toPlain(cvOrmFind({ $or: [p] })), { $or: [{ constructor: 0 }] })
2914
+ })
2915
+
2916
+ it('should clone the recorded objects once per call, and keep using the structuredClone captured at load (also after the global is replaced)', async function() {
2917
+ let real = globalThis.structuredClone
2918
+ let n = 0
2919
+ globalThis.structuredClone = (v, o) => {
2920
+ n++
2921
+ return real(v, o)
2922
+ }
2923
+ let mod
2924
+ try {
2925
+ mod = await import(`../src/cvOrmFind.mjs?count=${Date.now()}`) //新實例於載入時取得計數用包裝
2926
+ }
2927
+ finally {
2928
+ globalThis.structuredClone = real
2929
+ }
2930
+ let f = mod.default
2931
+ n = 0
2932
+ f({ g: 'A' })
2933
+ assert.strictEqual(n, 1)
2934
+ n = 0
2935
+ f({ $or: [{ a: 1 }, { b: { $gt: 2 } }], c: { $in: [1, 2] } })
2936
+ assert.strictEqual(n, 1)
2937
+ n = 0
2938
+ f({})
2939
+ assert.strictEqual(n, 1)
2940
+ n = 0
2941
+ f()
2942
+ assert.strictEqual(n, 0)
2943
+ //載入後替換全域不影響已取得者(退化成不拋錯之函數仍不放行品牌物件)
2944
+ n = 0
2945
+ globalThis.structuredClone = (v) => v
2946
+ try {
2947
+ assert.throws(() => f(hijack(() => new Map(), null, { g: 'A' })), /^Error: invalid find: condition must be a plain object, got \[object\] at \[find\]$/)
2948
+ assert.throws(() => f(new Proxy({}, {})), /an empty find must be an ordinary object/)
2949
+ }
2950
+ finally {
2951
+ globalThis.structuredClone = real
2952
+ }
2953
+ assert.strictEqual(n, 2)
2954
+ })
2955
+
2956
+ it('documents the cost bound: the internals of a rejected brand object are read once by the clone (a getter inside it is called at most once; maxNodes does not bound this)', function() {
2957
+ let calls = 0
2958
+ let entry = {}
2959
+ Object.defineProperty(entry, 'x', {
2960
+ get() {
2961
+ calls++
2962
+ return 1
2963
+ },
2964
+ enumerable: true
2965
+ })
2966
+ let m = hijack(() => new Map([['k', entry]]), null, { g: 'A' })
2967
+ rej(() => m, PLAIN, 'find', { maxNodes: 2 })
2968
+ assert.ok(calls <= 1, String(calls))
2969
+ })
2970
+
2971
+ it('should take the cheap structured-clone path for ordinary finds: a 50000-object tree and a 1000-element $or finish far below the exact-probe cost', function() {
2972
+ this.timeout(120000)
2973
+ let big = { $and: Array.from({ length: 1000 }, (_, i) => ({ $or: Array.from({ length: 49 }, (_, j) => ({ a: i * 100 + j })) })) }
2974
+ let wide = { $or: Array.from({ length: 1000 }, (_, i) => ({ g: `x${i}` })) }
2975
+ let wideDate = { $or: Array.from({ length: 1000 }, (_, i) => ({ d: { $gte: new Date(i) }, e: new Date(i) })) } //Date於結構化複製之複本中仍屬合法
2976
+ let min = (f, n) => {
2977
+ let best = Infinity
2978
+ for (let i = 0; i < n; i++) {
2979
+ let t0 = process.hrtime.bigint()
2980
+ f()
2981
+ best = Math.min(best, Number(process.hrtime.bigint() - t0) / 1e6)
2982
+ }
2983
+ return best
2984
+ }
2985
+ let msBig = min(() => cvOrmFind(big), 3) //逐物件探針實測約6600 ms
2986
+ let msWide = min(() => cvOrmFind(wide), 10) //逐物件探針實測約99 ms
2987
+ let msWideDate = min(() => cvOrmFind(wideDate), 10) //2000個條件物件, 逐物件探針約80 ms
2988
+ assert.ok(msBig < 2500, `${msBig} ms`)
2989
+ assert.ok(msWide < 40, `${msWide} ms`)
2990
+ assert.ok(msWideDate < 50, `${msWideDate} ms`)
2991
+ })
2992
+
2993
+ })
2994
+
2995
+
2996
+ describe('cvOrmFind 錯誤路徑含屬性鍵與元素索引(規則12)', function() {
2997
+
2998
+ let why = 'an exception was thrown while reading the condition'
2999
+ let gopd = (target, bad) => new Proxy(target, {
3000
+ getOwnPropertyDescriptor(t, k) {
3001
+ if (k === bad) {
3002
+ throw new Error('SECRET-gopd')
3003
+ }
3004
+ return Reflect.getOwnPropertyDescriptor(t, k)
3005
+ }
3006
+ })
3007
+
3008
+ it('should report the property key when the descriptor trap throws (document, operator object, $and / $or element)', function() {
3009
+ rej(() => gopd({ g: 'A' }, 'g'), why, 'find.g')
3010
+ rej(() => gopd({ a: 1, g: 'A' }, 'g'), why, 'find.g')
3011
+ rej(() => ({ a: gopd({ $gt: 1 }, '$gt') }), why, 'find.a.$gt')
3012
+ rej(() => ({ $or: [{ x: 1 }, gopd({ g: 'A' }, 'g')] }), why, 'find.$or[1].g')
3013
+ rej(() => ({ $and: [{ $or: [gopd({ g: 'A' }, 'g')] }] }), why, 'find.$and[0].$or[0].g')
3014
+ for (let mk of [() => gopd({ g: 'A' }, 'g'), () => ({ a: gopd({ $gt: 1 }, '$gt') })]) {
3015
+ assert.ok(!errOf(mk).message.includes('SECRET'))
3016
+ }
3017
+ })
3018
+
3019
+ it('should report the element index when the element descriptor trap throws ($in, $nin, $and, $or)', function() {
3020
+ rej(() => ({ a: { $in: gopd([1, 2, 3], '1') } }), why, 'find.a.$in[1]')
3021
+ rej(() => ({ a: { $nin: gopd([1, 2, 3], '2') } }), why, 'find.a.$nin[2]')
3022
+ rej(() => ({ $or: gopd([{ g: 'A' }, { g: 'B' }], '1') }), why, 'find.$or[1]')
3023
+ rej(() => ({ $and: gopd([{ g: 'A' }], '0') }), why, 'find.$and[0]')
3024
+ rej(() => ({ $or: [{ x: 1 }, { $and: gopd([{ g: 'A' }, { g: 'B' }], '1') }] }), why, 'find.$or[1].$and[1]')
3025
+ })
3026
+
3027
+ it('should report the property key for accessor and non-enumerable properties, without calling the getter', function() {
3028
+ let n = 0
3029
+ let acc = (key) => Object.defineProperty({}, key, {
3030
+ get() {
3031
+ n++
3032
+ return 1
3033
+ },
3034
+ enumerable: true,
3035
+ configurable: true
3036
+ })
3037
+ rej(() => acc('g'), 'accessor property is not allowed', 'find.g')
3038
+ rej(() => ({ a: acc('$gt') }), 'accessor property is not allowed', 'find.a.$gt')
3039
+ rej(() => ({ $or: [{ x: 1 }, acc('g')] }), 'accessor property is not allowed', 'find.$or[1].g')
3040
+ rej(() => ({ $or: [{ x: 1 }, hidden({}, 'g', 'A')] }), 'non-enumerable property is not allowed', 'find.$or[1].g')
3041
+ assert.strictEqual(n, 0)
3042
+ })
3043
+
3044
+ it('should keep the owner path for symbol keys and array holes (a symbol key cannot be displayed safely, a hole has no key)', function() {
3045
+ rej(() => ({ $or: [{ x: 1 }, { [Symbol('s')]: 1 }] }), 'symbol key is not allowed', 'find.$or[1]')
3046
+ rej(() => ({ a: { $in: [1, , 3] } }), 'sparse array hole at index 1', 'find.a.$in') // eslint-disable-line no-sparse-arrays
3047
+ })
3048
+
3049
+ })