wsemi 1.9.13 → 1.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/wsemi.umd.js +7 -7
- package/dist/wsemi.umd.js.map +1 -1
- package/docs/-_class.mjs.html +1 -1
- package/docs/-_const.mjs.html +1 -1
- package/docs/-_detector.mjs.html +1 -1
- package/docs/-_optNum.mjs.html +1 -1
- package/docs/ab2blob.mjs.html +1 -1
- package/docs/ab2u8arr.mjs.html +1 -1
- package/docs/addExcelWorksheetFromData.mjs.html +1 -1
- package/docs/addMonth.mjs.html +1 -1
- package/docs/aes2str.mjs.html +1 -1
- package/docs/alive.mjs.html +106 -5
- package/docs/arr2dt.mjs.html +1 -1
- package/docs/arrAccum.mjs.html +1 -1
- package/docs/arrAdd.mjs.html +1 -1
- package/docs/arrAt.mjs.html +1 -1
- package/docs/arrDiff.mjs.html +1 -1
- package/docs/arrFilterByKeywords.mjs.html +1 -1
- package/docs/arrFilterByNum.mjs.html +1 -1
- package/docs/arrFilterByPnum.mjs.html +1 -1
- package/docs/arrFilterByPnumAndToLog.mjs.html +1 -1
- package/docs/arrFind.mjs.html +1 -1
- package/docs/arrGroupByMaxmin.mjs.html +1 -1
- package/docs/arrHas.mjs.html +1 -1
- package/docs/arrInsert.mjs.html +1 -1
- package/docs/arrLookupByMaxmin.mjs.html +1 -1
- package/docs/arrMax.mjs.html +1 -1
- package/docs/arrMin.mjs.html +1 -1
- package/docs/arrMulti.mjs.html +1 -1
- package/docs/arrNorm.mjs.html +1 -1
- package/docs/arrProduct.mjs.html +1 -1
- package/docs/arrPull.mjs.html +1 -1
- package/docs/arrPullAt.mjs.html +1 -1
- package/docs/arrReduce.mjs.html +1 -1
- package/docs/arrReverse.mjs.html +1 -1
- package/docs/arrSep.mjs.html +1 -1
- package/docs/arrSort.mjs.html +1 -1
- package/docs/asind.mjs.html +1 -1
- package/docs/atan2d.mjs.html +1 -1
- package/docs/atand.mjs.html +1 -1
- package/docs/attstr.mjs.html +1 -1
- package/docs/b642obj.mjs.html +1 -1
- package/docs/b642str.mjs.html +1 -1
- package/docs/b642u16arr.mjs.html +1 -1
- package/docs/b642u8arr.mjs.html +1 -1
- package/docs/binstr.mjs.html +1 -1
- package/docs/blob2ab.mjs.html +1 -1
- package/docs/blob2b64.mjs.html +1 -1
- package/docs/blob2str.mjs.html +1 -1
- package/docs/blob2u8arr.mjs.html +1 -1
- package/docs/blobs2b64s.mjs.html +1 -1
- package/docs/blobs2u8arrs.mjs.html +1 -1
- package/docs/bodyLog.mjs.html +1 -1
- package/docs/browserView.mjs.html +1 -1
- package/docs/browserViewFromBlob.mjs.html +1 -1
- package/docs/browserViewFromText.mjs.html +1 -1
- package/docs/browserViewFromU8arr.mjs.html +1 -1
- package/docs/bs2u8arr.mjs.html +1 -1
- package/docs/bufRead.mjs.html +1 -1
- package/docs/bufReadDbl.mjs.html +1 -1
- package/docs/bufWrite.mjs.html +1 -1
- package/docs/bufWriteDbl.mjs.html +1 -1
- package/docs/cache.mjs.html +3 -3
- package/docs/cacheBd.mjs.html +1 -1
- package/docs/cacheSt.mjs.html +2 -2
- package/docs/cbol.mjs.html +1 -1
- package/docs/cdbl.mjs.html +1 -1
- package/docs/ceil.mjs.html +1 -1
- package/docs/cfilesize.mjs.html +1 -1
- package/docs/cfinancial.mjs.html +1 -1
- package/docs/cint.mjs.html +1 -1
- package/docs/clearXSS.mjs.html +1 -1
- package/docs/clearXSSDyn.mjs.html +1 -1
- package/docs/color.mjs.html +1 -1
- package/docs/composeToTree.mjs.html +1 -1
- package/docs/convertToTree.mjs.html +1 -1
- package/docs/copyStrToClipboard.mjs.html +1 -1
- package/docs/cosd.mjs.html +1 -1
- package/docs/cotd.mjs.html +1 -1
- package/docs/createExcelWorkbook.mjs.html +1 -1
- package/docs/cscd.mjs.html +1 -1
- package/docs/cstr.mjs.html +1 -1
- package/docs/day2cht.mjs.html +1 -1
- package/docs/debounce.mjs.html +1 -1
- package/docs/delay.mjs.html +1 -1
- package/docs/detectXSS.mjs.html +1 -1
- package/docs/detectXSSDyn.mjs.html +1 -1
- package/docs/dig.mjs.html +1 -1
- package/docs/digExp.mjs.html +1 -1
- package/docs/domAlert.mjs.html +1 -1
- package/docs/domAppend.mjs.html +1 -1
- package/docs/domCancelEvent.mjs.html +1 -1
- package/docs/domCompareText.mjs.html +1 -1
- package/docs/domConvertToPic.mjs.html +1 -1
- package/docs/domConvertToPicDyn.mjs.html +1 -1
- package/docs/domDetect.mjs.html +1 -1
- package/docs/domDrag.mjs.html +1 -1
- package/docs/domDragBarAndScroll.mjs.html +1 -1
- package/docs/domDragPointer.mjs.html +1 -1
- package/docs/domDragPreview.mjs.html +1 -1
- package/docs/domDropFiles.mjs.html +1 -1
- package/docs/domElementsFromPoint.mjs.html +1 -1
- package/docs/domFadeIn.mjs.html +1 -1
- package/docs/domFadeOut.mjs.html +1 -1
- package/docs/domFind.mjs.html +1 -1
- package/docs/domFinds.mjs.html +1 -1
- package/docs/domGetAttr.mjs.html +1 -1
- package/docs/domGetBoudRect.mjs.html +1 -1
- package/docs/domGetBoudRectRefSelf.mjs.html +1 -1
- package/docs/domGetFileAccept.mjs.html +1 -1
- package/docs/domGetFiles.mjs.html +1 -1
- package/docs/domGetParents.mjs.html +1 -1
- package/docs/domGetPointFromEvent.mjs.html +1 -1
- package/docs/domGetScrollableParent.mjs.html +1 -1
- package/docs/domGetWindowSize.mjs.html +1 -1
- package/docs/domIsClientXYIn.mjs.html +1 -1
- package/docs/domIsFocused.mjs.html +1 -1
- package/docs/domIsRendered.mjs.html +1 -1
- package/docs/domIsStable.mjs.html +1 -1
- package/docs/domIsVisible.mjs.html +1 -1
- package/docs/domPrepend.mjs.html +1 -1
- package/docs/domRemove.mjs.html +1 -1
- package/docs/domRenderMarkdownMind.mjs.html +1 -1
- package/docs/domRenderMarkdownMindDyn.mjs.html +1 -1
- package/docs/domRipple.mjs.html +1 -1
- package/docs/domScrollIntoView.mjs.html +1 -1
- package/docs/domScrollTo.mjs.html +1 -1
- package/docs/domSetAttr.mjs.html +1 -1
- package/docs/domShowImages.mjs.html +1 -1
- package/docs/domShowImagesDyn.mjs.html +1 -1
- package/docs/domShowInputAndGetFiles.mjs.html +1 -1
- package/docs/domShowInputAndGetFilesU8Arrs.mjs.html +1 -1
- package/docs/domShowInputDatatime.mjs.html +1 -1
- package/docs/domTooltip.mjs.html +1 -1
- package/docs/domTriggerEvent.mjs.html +1 -1
- package/docs/domVirtualCreate.mjs.html +1 -1
- package/docs/domVirtualCreateQueue.mjs.html +1 -1
- package/docs/downloadExcelFileFromData.mjs.html +1 -1
- package/docs/downloadExcelFileFromDataDyn.mjs.html +1 -1
- package/docs/downloadFileFromB64.mjs.html +1 -1
- package/docs/downloadFileFromBlob.mjs.html +1 -1
- package/docs/downloadFileFromText.mjs.html +1 -1
- package/docs/downloadFileFromU8Arr.mjs.html +1 -1
- package/docs/dtmapping.mjs.html +1 -1
- package/docs/dtpick.mjs.html +1 -1
- package/docs/estimateTicks.mjs.html +1 -1
- package/docs/evEmit.mjs.html +2 -2
- package/docs/evEmitDelay.mjs.html +1 -1
- package/docs/evem.mjs.html +1 -1
- package/docs/examples/ex-blob2b64.html +1 -1
- package/docs/examples/ex-clearXSS.html +1 -1
- package/docs/examples/ex-clearXSSDyn.html +1 -1
- package/docs/examples/ex-colorConverter.html +1 -1
- package/docs/examples/ex-detectXSS.html +1 -1
- package/docs/examples/ex-detectXSSDyn.html +1 -1
- package/docs/examples/ex-domCompareText.html +1 -1
- package/docs/examples/ex-domConvertToPic.html +1 -1
- package/docs/examples/ex-domDrag.html +1 -1
- package/docs/examples/ex-domDropFiles.html +1 -1
- package/docs/examples/ex-domIsVisible.html +1 -1
- package/docs/examples/ex-domRenderMarkdownMind.html +1 -1
- package/docs/examples/ex-domRenderMarkdownMindDyn.html +1 -1
- package/docs/examples/ex-domRipple.html +1 -1
- package/docs/examples/ex-domScrollIntoView.html +1 -1
- package/docs/examples/ex-domScrollTo.html +1 -1
- package/docs/examples/ex-domShowImages.html +1 -1
- package/docs/examples/ex-domShowImagesDyn.html +1 -1
- package/docs/examples/ex-domShowInputAndGetFiles.html +1 -1
- package/docs/examples/ex-domTooltip.html +1 -1
- package/docs/examples/ex-getFileHash.html +1 -1
- package/docs/examples/ex-getUserAgent.html +1 -1
- package/docs/examples/ex-getUserAgentDyn.html +1 -1
- package/docs/examples/ex-html2str.html +1 -1
- package/docs/examples/ex-obj2u8arr-u8arr2obj.html +1 -1
- package/docs/examples/ex-openLink.html +1 -1
- package/docs/examples/ex-str2aes-aes2str.html +1 -1
- package/docs/examples/ex-str2b64-b642str.html +1 -1
- package/docs/examples/ex-strFindFuzz.html +1 -1
- package/docs/examples/ex-timeTZ2expire-timeTZ2past.html +1 -1
- package/docs/examples/ex-xlsx.html +1 -1
- package/docs/execCli.mjs.html +67 -33
- package/docs/execCliCore.mjs.html +4 -1
- package/docs/execCliJsonRpc.mjs.html +3 -3
- package/docs/execCliSession.mjs.html +24 -12
- package/docs/execJsCode.mjs.html +1 -1
- package/docs/execProcess.mjs.html +58 -35
- package/docs/execProcessKillPid.mjs.html +5 -3
- package/docs/filepathToTree.mjs.html +1 -1
- package/docs/files2data.mjs.html +1 -1
- package/docs/flattenToConn.mjs.html +1 -1
- package/docs/flattenTree.mjs.html +1 -1
- package/docs/floor.mjs.html +1 -1
- package/docs/fsBuildReadStreamText.mjs.html +1 -1
- package/docs/fsBuildWriteStreamText.mjs.html +1 -1
- package/docs/fsCleanFolder.mjs.html +1 -1
- package/docs/fsCleanFolderCore.mjs.html +1 -1
- package/docs/fsCopyFile.mjs.html +1 -1
- package/docs/fsCopyFileCore.mjs.html +1 -1
- package/docs/fsCopyFolder.mjs.html +1 -1
- package/docs/fsCopyFolderCore.mjs.html +1 -1
- package/docs/fsCreateFile.mjs.html +1 -1
- package/docs/fsCreateFileCore.mjs.html +1 -1
- package/docs/fsCreateFolder.mjs.html +1 -1
- package/docs/fsCreateFolderCore.mjs.html +1 -1
- package/docs/fsDeleteFile.mjs.html +1 -1
- package/docs/fsDeleteFileCore.mjs.html +1 -1
- package/docs/fsDeleteFileSafe.mjs.html +1 -1
- package/docs/fsDeleteFileSafeCore.mjs.html +1 -1
- package/docs/fsDeleteFolder.mjs.html +1 -1
- package/docs/fsDeleteFolderCore.mjs.html +1 -1
- package/docs/fsDeleteFolderSafe.mjs.html +1 -1
- package/docs/fsDeleteFolderSafeCore.mjs.html +1 -1
- package/docs/fsDownloadFile.mjs.html +1 -1
- package/docs/fsDownloadFileCore.mjs.html +1 -1
- package/docs/fsEvem.mjs.html +1 -1
- package/docs/fsExists.mjs.html +1 -1
- package/docs/fsExistsCore.mjs.html +1 -1
- package/docs/fsGetFileBasicHash.mjs.html +1 -1
- package/docs/fsGetFileBasicHashCore.mjs.html +1 -1
- package/docs/fsGetFileHash.mjs.html +1 -1
- package/docs/fsGetFileHashCore.mjs.html +1 -1
- package/docs/fsGetFileXxHash.mjs.html +1 -1
- package/docs/fsGetFileXxHashCore.mjs.html +1 -1
- package/docs/fsGetFilesInFolder.mjs.html +1 -1
- package/docs/fsGetFilesInFolderCore.mjs.html +1 -1
- package/docs/fsGetFilesWithHashInFolder.mjs.html +1 -1
- package/docs/fsGetFilesWithHashInFolderCore.mjs.html +1 -1
- package/docs/fsGetFolderBasicHash.mjs.html +1 -1
- package/docs/fsGetFolderBasicHashCore.mjs.html +1 -1
- package/docs/fsGetFoldersInFolder.mjs.html +1 -1
- package/docs/fsGetFoldersInFolderCore.mjs.html +1 -1
- package/docs/fsIsFile.mjs.html +1 -1
- package/docs/fsIsFileCore.mjs.html +1 -1
- package/docs/fsIsFolder.mjs.html +1 -1
- package/docs/fsIsFolderCore.mjs.html +1 -1
- package/docs/fsMergeFiles.mjs.html +1 -1
- package/docs/fsMergeFilesCore.mjs.html +1 -1
- package/docs/fsReadJson.mjs.html +1 -1
- package/docs/fsReadText.mjs.html +1 -1
- package/docs/fsReadTextCore.mjs.html +1 -1
- package/docs/fsRenameFile.mjs.html +1 -1
- package/docs/fsRenameFileCore.mjs.html +1 -1
- package/docs/fsRenameFolder.mjs.html +1 -1
- package/docs/fsRenameFolderCore.mjs.html +1 -1
- package/docs/fsSrlog.mjs.html +1 -1
- package/docs/fsSyncFolder.mjs.html +1 -1
- package/docs/fsSyncFolderCore.mjs.html +1 -1
- package/docs/fsTask.mjs.html +1 -1
- package/docs/fsTaskCp.mjs.html +1 -1
- package/docs/fsTreeFolder.mjs.html +1 -1
- package/docs/fsTreeFolderCore.mjs.html +1 -1
- package/docs/fsTreeFolderWithHash.mjs.html +1 -1
- package/docs/fsTreeFolderWithHashCore.mjs.html +1 -1
- package/docs/fsWatchFile.mjs.html +1 -1
- package/docs/fsWatchFolder.mjs.html +1 -1
- package/docs/fsWriteJson.mjs.html +1 -1
- package/docs/fsWriteText.mjs.html +1 -1
- package/docs/fsWriteTextCore.mjs.html +1 -1
- package/docs/genGradientColor.mjs.html +1 -1
- package/docs/genID.mjs.html +1 -1
- package/docs/genIDSeq.mjs.html +1 -1
- package/docs/genPm.mjs.html +1 -1
- package/docs/getBufferSize.mjs.html +1 -1
- package/docs/getClearPathName.mjs.html +1 -1
- package/docs/getCsvStrFromData.mjs.html +1 -1
- package/docs/getDataFromExcelFileU8Arr.mjs.html +1 -1
- package/docs/getDataFromExcelFileU8ArrDyn.mjs.html +1 -1
- package/docs/getEnv.mjs.html +1 -1
- package/docs/getErrorMessage.mjs.html +1 -1
- package/docs/getExcelU8ArrFromData.mjs.html +1 -1
- package/docs/getExcelU8ArrFromWorkbook.mjs.html +1 -1
- package/docs/getExcelWorkbookFromData.mjs.html +1 -1
- package/docs/getExcelWorkbookFromWorksheet.mjs.html +1 -1
- package/docs/getExcelWorksheetFromData.mjs.html +1 -1
- package/docs/getFileAccept.mjs.html +1 -1
- package/docs/getFileBasicHash.mjs.html +1 -1
- package/docs/getFileHash.mjs.html +1 -1
- package/docs/getFileName.mjs.html +1 -1
- package/docs/getFileNameExt.mjs.html +1 -1
- package/docs/getFileTrueName.mjs.html +1 -1
- package/docs/getFileType.mjs.html +1 -1
- package/docs/getFileXxHash.mjs.html +1 -1
- package/docs/getGlobal.mjs.html +1 -1
- package/docs/getObjSize.mjs.html +1 -1
- package/docs/getPathParent.mjs.html +1 -1
- package/docs/getTimeBetween.mjs.html +1 -1
- package/docs/getTimeFromUnit.mjs.html +1 -1
- package/docs/getTimeHp.mjs.html +1 -1
- package/docs/getTimeObject.mjs.html +1 -1
- package/docs/getTimeRange.mjs.html +1 -1
- package/docs/getUserAgent.mjs.html +1 -1
- package/docs/getUserAgentDyn.mjs.html +1 -1
- package/docs/getltdtkeys.mjs.html +1 -1
- package/docs/global.html +7 -7
- package/docs/haskey.mjs.html +1 -1
- package/docs/html2str.mjs.html +1 -1
- package/docs/htmlDecode.mjs.html +1 -1
- package/docs/htmlEncode.mjs.html +1 -1
- package/docs/importResources.mjs.html +1 -1
- package/docs/index.html +1 -1
- package/docs/interp1.mjs.html +1 -1
- package/docs/isDate.mjs.html +1 -1
- package/docs/isDev.mjs.html +1 -1
- package/docs/isEle.mjs.html +1 -1
- package/docs/isEleExistByID.mjs.html +1 -1
- package/docs/isEmail.mjs.html +1 -1
- package/docs/isErr.mjs.html +1 -1
- package/docs/isIE.mjs.html +1 -1
- package/docs/isNarrow.mjs.html +1 -1
- package/docs/isStrHasCapital.mjs.html +1 -1
- package/docs/isStrHasLetter.mjs.html +1 -1
- package/docs/isStrHasLowerCase.mjs.html +1 -1
- package/docs/isStrHasNumber.mjs.html +1 -1
- package/docs/isStrHasSpace.mjs.html +1 -1
- package/docs/isStrHasSymbol.mjs.html +1 -1
- package/docs/isUserIdentify.mjs.html +1 -1
- package/docs/isUserName.mjs.html +1 -1
- package/docs/isUserPw.mjs.html +1 -1
- package/docs/isWindow.mjs.html +1 -1
- package/docs/isab.mjs.html +1 -1
- package/docs/isarr.mjs.html +1 -1
- package/docs/isarr0.mjs.html +1 -1
- package/docs/isarr1.mjs.html +1 -1
- package/docs/isbigint.mjs.html +1 -1
- package/docs/isblob.mjs.html +1 -1
- package/docs/isbol.mjs.html +1 -1
- package/docs/isday.mjs.html +1 -1
- package/docs/isearr.mjs.html +1 -1
- package/docs/iseobj.mjs.html +1 -1
- package/docs/iser.mjs.html +1 -1
- package/docs/isernot.mjs.html +1 -1
- package/docs/isestr.mjs.html +1 -1
- package/docs/isfile.mjs.html +1 -1
- package/docs/isfun.mjs.html +1 -1
- package/docs/isint.mjs.html +1 -1
- package/docs/ismonth.mjs.html +1 -1
- package/docs/isn0int.mjs.html +1 -1
- package/docs/isn0num.mjs.html +1 -1
- package/docs/isnan.mjs.html +1 -1
- package/docs/isnbr.mjs.html +1 -1
- package/docs/isnint.mjs.html +1 -1
- package/docs/isnnum.mjs.html +1 -1
- package/docs/isnull.mjs.html +1 -1
- package/docs/isnum.mjs.html +1 -1
- package/docs/isobj.mjs.html +1 -1
- package/docs/isobj0.mjs.html +1 -1
- package/docs/isp0int.mjs.html +1 -1
- package/docs/isp0num.mjs.html +1 -1
- package/docs/ispint.mjs.html +1 -1
- package/docs/ispm.mjs.html +1 -1
- package/docs/ispnum.mjs.html +1 -1
- package/docs/isstr.mjs.html +1 -1
- package/docs/isstr0.mjs.html +1 -1
- package/docs/istime.mjs.html +1 -1
- package/docs/istimeTZ.mjs.html +1 -1
- package/docs/istimems.mjs.html +1 -1
- package/docs/istimemsTZ.mjs.html +1 -1
- package/docs/isu16arr.mjs.html +1 -1
- package/docs/isu8arr.mjs.html +1 -1
- package/docs/isundefined.mjs.html +1 -1
- package/docs/j2o.mjs.html +1 -1
- package/docs/judge.mjs.html +1 -1
- package/docs/keysmat2ltdt.mjs.html +1 -1
- package/docs/ltdt2vrtsable.mjs.html +1 -1
- package/docs/ltdtDiff.mjs.html +1 -1
- package/docs/ltdtDiffByKey.mjs.html +1 -1
- package/docs/ltdtkeys2mat.mjs.html +1 -1
- package/docs/ltdtkeysheads2mat.mjs.html +1 -1
- package/docs/ltdtmapping.mjs.html +1 -1
- package/docs/ltdtmerge.mjs.html +1 -1
- package/docs/ltdtpick.mjs.html +1 -1
- package/docs/ltdtpull.mjs.html +1 -1
- package/docs/mat2ltdt.mjs.html +1 -1
- package/docs/matConcat.mjs.html +1 -1
- package/docs/matDiff.mjs.html +1 -1
- package/docs/matat.mjs.html +1 -1
- package/docs/now2str.mjs.html +1 -1
- package/docs/now2strp.mjs.html +1 -1
- package/docs/nowDay2str.mjs.html +1 -1
- package/docs/nowms2str.mjs.html +1 -1
- package/docs/nowms2strp.mjs.html +1 -1
- package/docs/o2j.mjs.html +1 -1
- package/docs/obj2b64.mjs.html +1 -1
- package/docs/obj2pb64.mjs.html +1 -1
- package/docs/obj2str.mjs.html +1 -1
- package/docs/obj2stru8arr.mjs.html +1 -1
- package/docs/obj2u8arr.mjs.html +1 -1
- package/docs/objSortBy.mjs.html +1 -1
- package/docs/objSortByKeys.mjs.html +1 -1
- package/docs/oo.mjs.html +1 -1
- package/docs/openLink.mjs.html +1 -1
- package/docs/pb642obj.mjs.html +1 -1
- package/docs/pmChain.mjs.html +1 -1
- package/docs/pmConvertResolve.mjs.html +1 -1
- package/docs/pmDebounce.mjs.html +1 -1
- package/docs/pmHook.mjs.html +1 -1
- package/docs/pmHookReject.mjs.html +1 -1
- package/docs/pmIni.mjs.html +1 -1
- package/docs/pmInvResolve.mjs.html +1 -1
- package/docs/pmKeyMutex.mjs.html +1 -1
- package/docs/pmMap.mjs.html +1 -1
- package/docs/pmQueue.mjs.html +1 -1
- package/docs/pmSeries.mjs.html +1 -1
- package/docs/pmThrottle.mjs.html +1 -1
- package/docs/pmTimeout.mjs.html +1 -1
- package/docs/preciseNum.mjs.html +1 -1
- package/docs/pseudoRandom.mjs.html +1 -1
- package/docs/pseudoRandomIntRange.mjs.html +1 -1
- package/docs/pseudoRandomIntsNdpRange.mjs.html +1 -1
- package/docs/pseudoRandomIntsRange.mjs.html +1 -1
- package/docs/pseudoRandomRange.mjs.html +1 -1
- package/docs/pseudoRandomsRange.mjs.html +1 -1
- package/docs/queue.mjs.html +1 -1
- package/docs/random.mjs.html +1 -1
- package/docs/randomIntRange.mjs.html +1 -1
- package/docs/randomIntsNdpRange.mjs.html +1 -1
- package/docs/randomIntsRange.mjs.html +1 -1
- package/docs/randomRange.mjs.html +1 -1
- package/docs/randomsRange.mjs.html +1 -1
- package/docs/rang.mjs.html +1 -1
- package/docs/rep.mjs.html +1 -1
- package/docs/repObj.mjs.html +1 -1
- package/docs/replace.mjs.html +1 -1
- package/docs/replaceObj.mjs.html +1 -1
- package/docs/replacePlus.mjs.html +1 -1
- package/docs/round.mjs.html +1 -1
- package/docs/secd.mjs.html +1 -1
- package/docs/sep.mjs.html +1 -1
- package/docs/sepInt.mjs.html +1 -1
- package/docs/setIntervalHp.mjs.html +1 -1
- package/docs/setTimeoutHp.mjs.html +1 -1
- package/docs/sind.mjs.html +1 -1
- package/docs/split.mjs.html +1 -1
- package/docs/str2aes.mjs.html +1 -1
- package/docs/str2b64.mjs.html +1 -1
- package/docs/str2hint.mjs.html +1 -1
- package/docs/str2md5.mjs.html +1 -1
- package/docs/str2obj.mjs.html +1 -1
- package/docs/str2sha.mjs.html +1 -1
- package/docs/str2sha512.mjs.html +1 -1
- package/docs/str2u8arr.mjs.html +1 -1
- package/docs/strCompare.mjs.html +1 -1
- package/docs/strCompareForVersion.mjs.html +1 -1
- package/docs/strDiff.mjs.html +1 -1
- package/docs/strDiffToHtml.mjs.html +1 -1
- package/docs/strFindFuzz.mjs.html +1 -1
- package/docs/strFindSimilar.mjs.html +1 -1
- package/docs/strTruncate.mjs.html +1 -1
- package/docs/strdelleft.mjs.html +1 -1
- package/docs/strdelright.mjs.html +1 -1
- package/docs/strleft.mjs.html +1 -1
- package/docs/strmid.mjs.html +1 -1
- package/docs/strright.mjs.html +1 -1
- package/docs/stru8arr2obj.mjs.html +1 -1
- package/docs/tand.mjs.html +1 -1
- package/docs/time2day.mjs.html +1 -1
- package/docs/time2hour.mjs.html +1 -1
- package/docs/time2min.mjs.html +1 -1
- package/docs/timeTZ2day.mjs.html +1 -1
- package/docs/timeTZ2expire.mjs.html +1 -1
- package/docs/timeTZ2hour.mjs.html +1 -1
- package/docs/timeTZ2min.mjs.html +1 -1
- package/docs/timeTZ2past.mjs.html +1 -1
- package/docs/timems2day.mjs.html +1 -1
- package/docs/timems2hour.mjs.html +1 -1
- package/docs/timems2min.mjs.html +1 -1
- package/docs/timems2time.mjs.html +1 -1
- package/docs/timemsTZ2day.mjs.html +1 -1
- package/docs/timemsTZ2expire.mjs.html +1 -1
- package/docs/timemsTZ2hour.mjs.html +1 -1
- package/docs/timemsTZ2min.mjs.html +1 -1
- package/docs/timemsTZ2past.mjs.html +1 -1
- package/docs/timemsTZ2time.mjs.html +1 -1
- package/docs/treeObj.mjs.html +1 -1
- package/docs/treeToFilepath.mjs.html +1 -1
- package/docs/trim.mjs.html +1 -1
- package/docs/u16arr2b64.mjs.html +1 -1
- package/docs/u16arr2u8arr.mjs.html +1 -1
- package/docs/u8arr2ab.mjs.html +1 -1
- package/docs/u8arr2b64.mjs.html +1 -1
- package/docs/u8arr2blob.mjs.html +1 -1
- package/docs/u8arr2bs.mjs.html +1 -1
- package/docs/u8arr2obj.mjs.html +1 -1
- package/docs/u8arr2str.mjs.html +1 -1
- package/docs/u8arr2u16arr.mjs.html +1 -1
- package/docs/urlParse.mjs.html +1 -1
- package/docs/verifyValue.mjs.html +1 -1
- package/docs/waitFun.mjs.html +1 -1
- package/docs/wsemi.html +24 -23
- package/package.json +2 -2
- package/script.txt +1 -1
- package/src/_execSpawnError.mjs +41 -0
- package/src/alive.mjs +105 -4
- package/src/cache.mjs +2 -2
- package/src/cacheSt.mjs +1 -1
- package/src/evEmit.mjs +1 -1
- package/src/execCli.mjs +66 -32
- package/src/execCliCore.mjs +3 -0
- package/src/execCliJsonRpc.mjs +2 -2
- package/src/execCliSession.mjs +23 -11
- package/src/execProcess.mjs +57 -34
- package/src/execProcessKillPid.mjs +4 -2
- package/test/_execSpawnError.test.mjs +81 -0
- package/test/alive.test.mjs +201 -0
- package/test/cache.test.mjs +26 -0
- package/test/cacheSt.test.mjs +40 -0
- package/test/execCli.test.mjs +95 -0
- package/test/execCliJsonRpc.test.mjs +27 -0
- package/test/execCliSession.test.mjs +82 -0
- package/test/execProcess.test.mjs +135 -3
- package/test/execProcessKillPid.test.mjs +34 -0
- package/test/tools/spawnEmfileRunner.mjs +55 -0
- package/toolfp/dist/wsemip.umd.js +1 -1
- package/toolparfor/dist/wpf.umd.js +1 -1
- package/toolparfor/dist/wpf_pmSeries.umd.js +1 -1
package/src/alive.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import get from 'lodash-es/get.js'
|
|
|
2
2
|
import each from 'lodash-es/each.js'
|
|
3
3
|
import cloneDeep from 'lodash-es/cloneDeep.js'
|
|
4
4
|
import size from 'lodash-es/size.js'
|
|
5
|
-
import cst from './_const.mjs'
|
|
5
|
+
import cst from './_const.mjs'
|
|
6
6
|
import evem from './evem.mjs'
|
|
7
7
|
import evEmit from './evEmit.mjs'
|
|
8
8
|
import ispint from './ispint.mjs'
|
|
@@ -19,7 +19,7 @@ import cint from './cint.mjs'
|
|
|
19
19
|
* @memberOf wsemi
|
|
20
20
|
* @param {Integer} [timeAlive=10000] 輸入判斷單元是否斷線之延時整數,單位為毫秒ms,預設為10000
|
|
21
21
|
* @param {Integer} [timeDetect=50] 輸入偵測佇列間隔時間整數,單位為毫秒ms,預設為50
|
|
22
|
-
* @returns {Object} 回傳事件物件,可呼叫函數on、trigger、get。trigger給予單元的唯一key字串與攜帶數據data物件,on為監聽事件,需自行監聽message事件取得單元進出事件。get事件可取得alive
|
|
22
|
+
* @returns {Object} 回傳事件物件,可呼叫函數on、trigger、get、clear。trigger給予單元的唯一key字串與攜帶數據data物件,on為監聽事件,需自行監聽message事件取得單元進出事件。get事件可取得alive內視為存活的單元清單(含剛trigger而尚未發出enter者)。clear為釋放:立即清除偵測計時器、取消尚未發出之enter、清空佇列,本身不發事件,其後不再發出任何事件、trigger無效、get回傳[],可重複呼叫(釋放為終態,需重新偵測者另建alive);clear回傳已發出enter且尚未leave之單元陣列[{ key, data }],供使用端補發離線通知,再次呼叫回傳[];監聽器於派發中呼叫clear亦安全,同一輪尚未處理之單元不再派發。事件物件為原生EventEmitter(eventemitter3)。本模組於派發處以try攔截監聽器之同步拋錯,故其不會使行程崩潰,會改以error事件回報{ fun: 'listener', name, msg, args },無error監聽者則console.error;惟依EventEmitter規範,同一次派發中先拋錯之監聽器會中止該次派發,其後之監聽器不再被呼叫。async監聽器之reject不被攔截(規範上emit不觀察監聽器回傳值),須由監聽器自行處理,否則為unhandledRejection
|
|
23
23
|
* @example
|
|
24
24
|
*
|
|
25
25
|
* async function topAsync() {
|
|
@@ -81,14 +81,47 @@ import cint from './cint.mjs'
|
|
|
81
81
|
* // 4544ms { eventName: 'leave', key: 'b', data: { data: '34.56' }, now: 0 }
|
|
82
82
|
* // ["enter|a","enter|b","leave|b","enter|b","leave|a","leave|b"]
|
|
83
83
|
*
|
|
84
|
+
* function test2() {
|
|
85
|
+
* return new Promise((resolve, reject) => {
|
|
86
|
+
* let ms = []
|
|
87
|
+
*
|
|
88
|
+
* let oAL = alive({ timeAlive: 1500 })
|
|
89
|
+
*
|
|
90
|
+
* oAL.on('message', function({ eventName, key }) {
|
|
91
|
+
* ms.push(eventName + '|' + key)
|
|
92
|
+
* })
|
|
93
|
+
*
|
|
94
|
+
* oAL.trigger('a', { data: 123 })
|
|
95
|
+
*
|
|
96
|
+
* setTimeout(() => {
|
|
97
|
+
* oAL.trigger('b', { data: '34.56' })
|
|
98
|
+
* let r = oAL.clear() //b剛trigger尚未發出enter, 故只回傳a, 且b之enter不再發出
|
|
99
|
+
* ms.push('clear|' + r.map((v) => v.key).join(','))
|
|
100
|
+
* }, 500)
|
|
101
|
+
*
|
|
102
|
+
* setTimeout(() => {
|
|
103
|
+
* resolve(ms)
|
|
104
|
+
* }, 2500)
|
|
105
|
+
*
|
|
106
|
+
* })
|
|
107
|
+
* }
|
|
108
|
+
* console.log('test2')
|
|
109
|
+
* let r2 = await test2()
|
|
110
|
+
* console.log(JSON.stringify(r2))
|
|
111
|
+
* // test2
|
|
112
|
+
* // ["enter|a","clear|a"]
|
|
113
|
+
*
|
|
84
114
|
* }
|
|
85
115
|
* topAsync().catch(() => {})
|
|
86
116
|
*
|
|
87
117
|
*/
|
|
88
118
|
function alive(opt = {}) {
|
|
89
119
|
let ev = evem() //message事件於timer內派發, 監聽器出錯不得殺行程, 故派發處以evEmit攔截
|
|
90
|
-
let q =
|
|
120
|
+
let q = Object.create(null) //queue, 以trigger之key為鍵; 用無原型物件使任意字串皆為一般鍵, 一般物件時__proto__會改掉佇列之原型、constructor等原型屬性名會被haskey誤判為已存在而不發enter
|
|
91
121
|
let t = null //timer
|
|
122
|
+
let tEnters = new Set() //尚未發出enter之計時器, 供clear取消
|
|
123
|
+
let entered = new Set() //已發出enter且尚未leave之單元key, 供clear回傳
|
|
124
|
+
let cleared = false //是否已clear, clear為終態
|
|
92
125
|
|
|
93
126
|
//timeAlive
|
|
94
127
|
let timeAlive = get(opt, 'timeAlive')
|
|
@@ -107,6 +140,9 @@ function alive(opt = {}) {
|
|
|
107
140
|
timeDetect = Math.min(timeDetect, cst.TIMER_TIME_MAX) //同上
|
|
108
141
|
|
|
109
142
|
function detect() {
|
|
143
|
+
if (cleared) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
110
146
|
if (t !== null) {
|
|
111
147
|
return
|
|
112
148
|
}
|
|
@@ -127,13 +163,25 @@ function alive(opt = {}) {
|
|
|
127
163
|
|
|
128
164
|
//刪除佇列內key紀錄
|
|
129
165
|
delete q[key]
|
|
166
|
+
entered.delete(key)
|
|
130
167
|
|
|
131
168
|
//emit leave
|
|
132
169
|
evEmit(ev, 'message', [{ eventName: 'leave', key, data: r.data, now: size(q) }], { tag: 'alive' })
|
|
133
170
|
|
|
171
|
+
//監聽器於派發中呼叫clear時須立即停止本輪, 不再處理或派發
|
|
172
|
+
// each走的是本輪開始時之鍵清單, 而clear已清空佇列, 續走會讀到不存在之單元而拋錯
|
|
173
|
+
if (cleared) {
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
|
|
134
177
|
}
|
|
135
178
|
})
|
|
136
179
|
|
|
180
|
+
//已clear者計時器已於clear內清除
|
|
181
|
+
if (cleared) {
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
137
185
|
//clear, 當無任何訊息存在
|
|
138
186
|
if (!iseobj(q)) {
|
|
139
187
|
clearInterval(t)
|
|
@@ -145,6 +193,11 @@ function alive(opt = {}) {
|
|
|
145
193
|
|
|
146
194
|
function trigger(key, data) {
|
|
147
195
|
|
|
196
|
+
//已clear為終態, 不建立計時器亦不發事件
|
|
197
|
+
if (cleared) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
148
201
|
//check
|
|
149
202
|
if (!isestr(key)) {
|
|
150
203
|
console.log('trigger need key')
|
|
@@ -153,12 +206,23 @@ function alive(opt = {}) {
|
|
|
153
206
|
|
|
154
207
|
//check
|
|
155
208
|
if (!haskey(q, key)) {
|
|
156
|
-
|
|
209
|
+
//因需判斷是否為新單元故需放於update前, 而emit內可能會被存取q, 故需要用setTimeout脫勾使q為被更新資訊, 才能正確取得當前單元數量
|
|
210
|
+
//不用evEmitDelay, 因其參數於呼叫時即求值, now會少計本單元; 計時器存入tEnters供clear取消
|
|
211
|
+
let te = setTimeout(() => {
|
|
212
|
+
|
|
213
|
+
//已發出, 移出待取消清單
|
|
214
|
+
tEnters.delete(te)
|
|
215
|
+
|
|
216
|
+
//派發前即標記已enter, 使監聽器於派發中呼叫clear時能回傳本單元, 以與其leave配對
|
|
217
|
+
if (haskey(q, key)) {
|
|
218
|
+
entered.add(key)
|
|
219
|
+
}
|
|
157
220
|
|
|
158
221
|
//emit enter
|
|
159
222
|
evEmit(ev, 'message', [{ eventName: 'enter', key, data, now: size(q) }], { tag: 'alive' })
|
|
160
223
|
|
|
161
224
|
}, 1)
|
|
225
|
+
tEnters.add(te)
|
|
162
226
|
}
|
|
163
227
|
|
|
164
228
|
//update
|
|
@@ -178,9 +242,46 @@ function alive(opt = {}) {
|
|
|
178
242
|
return rs
|
|
179
243
|
}
|
|
180
244
|
|
|
245
|
+
function clear() {
|
|
246
|
+
|
|
247
|
+
//已clear則回傳[], 可重複呼叫
|
|
248
|
+
if (cleared) {
|
|
249
|
+
return []
|
|
250
|
+
}
|
|
251
|
+
cleared = true
|
|
252
|
+
|
|
253
|
+
//清除偵測計時器
|
|
254
|
+
if (t !== null) {
|
|
255
|
+
clearInterval(t)
|
|
256
|
+
t = null
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//取消尚未發出之enter
|
|
260
|
+
for (let te of tEnters) {
|
|
261
|
+
clearTimeout(te)
|
|
262
|
+
}
|
|
263
|
+
tEnters.clear()
|
|
264
|
+
|
|
265
|
+
//已發出enter且尚未leave之單元, 形狀同get; get含尚未發出enter者, 使用端拿其配對leave會出現有leave無enter
|
|
266
|
+
let rs = []
|
|
267
|
+
each(q, (v, k) => {
|
|
268
|
+
if (entered.has(k)) {
|
|
269
|
+
rs.push({ key: k, data: v.data })
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
rs = cloneDeep(rs) //使用cloneDeep避免外部修改內部數據
|
|
273
|
+
|
|
274
|
+
//清空佇列, 同建立時用無原型物件
|
|
275
|
+
q = Object.create(null)
|
|
276
|
+
entered.clear()
|
|
277
|
+
|
|
278
|
+
return rs
|
|
279
|
+
}
|
|
280
|
+
|
|
181
281
|
//save
|
|
182
282
|
ev.trigger = trigger
|
|
183
283
|
ev.get = _get
|
|
284
|
+
ev.clear = clear
|
|
184
285
|
|
|
185
286
|
return ev
|
|
186
287
|
}
|
package/src/cache.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import loGet from 'lodash-es/get.js'
|
|
2
2
|
import cloneDeep from 'lodash-es/cloneDeep.js'
|
|
3
3
|
import evem from './evem.mjs'
|
|
4
|
-
import evEmitDelay from './evEmitDelay.mjs'
|
|
4
|
+
import evEmitDelay from './evEmitDelay.mjs'
|
|
5
5
|
import isfun from './isfun.mjs'
|
|
6
6
|
import isbol from './isbol.mjs'
|
|
7
7
|
import haskey from './haskey.mjs'
|
|
@@ -538,7 +538,7 @@ import cint from './cint.mjs'
|
|
|
538
538
|
*/
|
|
539
539
|
function cache() {
|
|
540
540
|
let ev = evem() //事件以timer脫勾派發, 監聽器出錯不得殺行程, 故派發處以evEmit攔截
|
|
541
|
-
let data =
|
|
541
|
+
let data = Object.create(null) //快取資料, 以呼叫端之key為鍵; 用無原型物件使任意字串皆為一般鍵, 一般物件時__proto__會改掉其原型、constructor等原型屬性名會被haskey誤判為已存在, 對其update會寫到Object本身
|
|
542
542
|
|
|
543
543
|
function emit(mode, data) {
|
|
544
544
|
evEmitDelay(ev, mode, [data], { tag: 'cache' }) //用timer脫勾, 並於該新堆疊內攔截監聽器之同步拋錯
|
package/src/cacheSt.mjs
CHANGED
|
@@ -112,7 +112,7 @@ function cacheSt(opt = {}) {
|
|
|
112
112
|
|
|
113
113
|
let ev = evem() //detect事件於setInterval內派發, 其餘於呼叫端同步堆疊或await之後派發且監聽器可能為async, 監聽器出錯不得殺行程, 故派發處以evEmit攔截
|
|
114
114
|
|
|
115
|
-
let _gs =
|
|
115
|
+
let _gs = Object.create(null) //以呼叫端之key為鍵; 用無原型物件使任意字串皆為一般鍵, 一般物件時__proto__會改掉其原型(且不會過期)、constructor等原型屬性名會被haskey誤判為已存在
|
|
116
116
|
|
|
117
117
|
let _set = async (key, value = true) => {
|
|
118
118
|
_gs[key] = {
|
package/src/evEmit.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import isfun from './isfun.mjs'
|
|
|
16
16
|
*
|
|
17
17
|
* setTimeout會開一個「沒有呼叫者」的堆疊,監聽器之同步拋錯於該處即為uncaughtException,
|
|
18
18
|
* 且呼叫端無論如何try都攔不到(此與監聽器是否為async無關,純同步監聽器亦然)。
|
|
19
|
-
* 於呼叫端之堆疊上派發,一個普通的try即可攔下。確實需要脫勾者請改用
|
|
19
|
+
* 於呼叫端之堆疊上派發,一個普通的try即可攔下。確實需要脫勾者請改用evEmitDelay,
|
|
20
20
|
* 其內部即以本函數派發,故仍有攔截。
|
|
21
21
|
*
|
|
22
22
|
* 【為何不處理async監聽器之reject】
|
package/src/execCli.mjs
CHANGED
|
@@ -16,12 +16,17 @@ import strdelleft from './strdelleft.mjs'
|
|
|
16
16
|
import strTruncate from './strTruncate.mjs'
|
|
17
17
|
import execProcessKillPid from './execProcessKillPid.mjs'
|
|
18
18
|
import execCliCore from './execCliCore.mjs'
|
|
19
|
+
import execSpawnError from './_execSpawnError.mjs'
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
//buildSpawnArgs, buildSpawnEnv, 共用核心已抽至execCliCore
|
|
22
23
|
let { buildSpawnArgs, buildSpawnEnv } = execCliCore
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
//kNoRetry, 無法啟動子進程之同步錯誤所產生之結果物件(確定性失敗, 重試無意義), 供execCli判定不重試, 不擴充對外之結果物件欄位
|
|
27
|
+
let kNoRetry = new WeakSet()
|
|
28
|
+
|
|
29
|
+
|
|
25
30
|
//optTruncate, 供strTruncate裁切失敗結果之stdout與stderr時, 於刪節號後標註原始總長度
|
|
26
31
|
let optTruncate = {
|
|
27
32
|
funWithMsg: (str) => `(truncated, total ${str.length} chars)`,
|
|
@@ -126,15 +131,33 @@ function execCliOnce(command, args = [], opt = {}) {
|
|
|
126
131
|
//每次attempt於此重新求值並僅作用於該次子進程, 不動本進程process.env, 故並行調用可各自帶不同值
|
|
127
132
|
let envSpawn = buildSpawnEnv(envExtra)
|
|
128
133
|
|
|
129
|
-
//spawn
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
//spawn, 同步拋錯(EPERM、UNKNOWN、ENAMETOOLONG、參數檢查錯誤等)以結果物件回報而不reject, 且屬確定性失敗不重試; buildSpawnArgs之拋錯亦同
|
|
135
|
+
let proc
|
|
136
|
+
try {
|
|
137
|
+
let winSpawn = buildSpawnArgs(command, args)
|
|
138
|
+
proc = spawn(winSpawn.file, winSpawn.args, {
|
|
139
|
+
cwd,
|
|
140
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
141
|
+
windowsHide: true, //執行的主程序若沒有主控台調用執行程序就不會有視窗, 但若通過pm2執行會有, 須設定windowsHide=true
|
|
142
|
+
env: envSpawn,
|
|
143
|
+
...winSpawn.opt,
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
let e = execSpawnError(err)
|
|
148
|
+
let r = {
|
|
149
|
+
ok: false,
|
|
150
|
+
stdout: '',
|
|
151
|
+
stderr: '',
|
|
152
|
+
code: null,
|
|
153
|
+
error: `${e.code}: ${e.message}`,
|
|
154
|
+
durationMs: Date.now() - startTime,
|
|
155
|
+
pid: undefined, //與'error'事件(如ENOENT)之結果一致
|
|
156
|
+
}
|
|
157
|
+
kNoRetry.add(r)
|
|
158
|
+
pm.resolve(r)
|
|
159
|
+
return pm
|
|
160
|
+
}
|
|
138
161
|
|
|
139
162
|
let stdout = ''
|
|
140
163
|
let stderr = ''
|
|
@@ -146,6 +169,32 @@ function execCliOnce(command, args = [], opt = {}) {
|
|
|
146
169
|
|
|
147
170
|
let settled = false
|
|
148
171
|
let timedOut = false
|
|
172
|
+
let timer = null
|
|
173
|
+
|
|
174
|
+
//error, 須於存取stdio前監聽: node對EMFILE、ENFILE不建立stdio即於nextTick發出'error', 先存取會拋TypeError且'error'無人監聽而拉倒程序
|
|
175
|
+
// 可能為'找不到可執行檔'(ENOENT), '權限不足', '系統資源用盡'等, 訊息經execSpawnError去除參數值
|
|
176
|
+
proc.on('error', (err) => {
|
|
177
|
+
if (settled) {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
settled = true
|
|
181
|
+
clearTimeout(timer)
|
|
182
|
+
let e = execSpawnError(err)
|
|
183
|
+
pm.resolve({
|
|
184
|
+
ok: false,
|
|
185
|
+
stdout: '',
|
|
186
|
+
stderr: '',
|
|
187
|
+
code: null,
|
|
188
|
+
error: `${e.code}: ${e.message}`,
|
|
189
|
+
durationMs: Date.now() - startTime,
|
|
190
|
+
pid: proc.pid,
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
//stdio, EMFILE、ENFILE時node未建立(undefined), 由上方error回報
|
|
195
|
+
if (!proc.stdout || !proc.stderr || !proc.stdin) {
|
|
196
|
+
return pm
|
|
197
|
+
}
|
|
149
198
|
|
|
150
199
|
//stdout data, 經TextDecoder跨chunk解碼
|
|
151
200
|
proc.stdout.on('data', (chunk) => {
|
|
@@ -183,7 +232,7 @@ function execCliOnce(command, args = [], opt = {}) {
|
|
|
183
232
|
proc.stdin.end()
|
|
184
233
|
|
|
185
234
|
//timer, 逾時觸發時調用execProcessKillPid殺整棵程序樹
|
|
186
|
-
|
|
235
|
+
timer = setTimeout(() => {
|
|
187
236
|
if (settled) {
|
|
188
237
|
return
|
|
189
238
|
}
|
|
@@ -213,24 +262,6 @@ function execCliOnce(command, args = [], opt = {}) {
|
|
|
213
262
|
|
|
214
263
|
}, timeoutMs)
|
|
215
264
|
|
|
216
|
-
//error, 監聽(例如執行spawn本身)錯誤訊息, 可能為'找不到可執行檔'(ENOENT), '權限不足', '系統資源用盡'等
|
|
217
|
-
proc.on('error', (err) => {
|
|
218
|
-
if (settled) {
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
settled = true
|
|
222
|
-
clearTimeout(timer)
|
|
223
|
-
pm.resolve({
|
|
224
|
-
ok: false,
|
|
225
|
-
stdout: '',
|
|
226
|
-
stderr: '',
|
|
227
|
-
code: null,
|
|
228
|
-
error: `${err.code || 'UNKNOWN'}: ${err.message}`,
|
|
229
|
-
durationMs: Date.now() - startTime,
|
|
230
|
-
pid: proc.pid,
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
|
|
234
265
|
//close
|
|
235
266
|
proc.on('close', (code, signal) => {
|
|
236
267
|
if (settled) {
|
|
@@ -314,7 +345,7 @@ function execCliOnce(command, args = [], opt = {}) {
|
|
|
314
345
|
* 通用CLI子程序調用,封裝逾時、進程樹清理、輸出驗證、結構化錯誤回報與自動重試
|
|
315
346
|
* 各參數原樣傳遞給目標CLI,支援中文
|
|
316
347
|
* Windows下npm全域安裝之命令為.cmd批次檔,Node.js spawn無法直接執行(CVE-2024-27980安全修正後回EINVAL),內部已參考cross-spawn自動解析.cmd入口並繞過cmd.exe,故可支援含多行文字之參數
|
|
317
|
-
* 本函數不throw,一律以結果物件之ok與error
|
|
348
|
+
* 本函數不throw,一律以結果物件之ok與error欄位回報成敗,error不含args與env之值(子程序自身之輸出另見stdout、stderr)
|
|
318
349
|
*
|
|
319
350
|
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/execCli.test.mjs Github}
|
|
320
351
|
* @memberOf wsemi
|
|
@@ -326,10 +357,10 @@ function execCliOnce(command, args = [], opt = {}) {
|
|
|
326
357
|
* @param {String} [opt.input=undefined] 輸入傳入子進程stdin之字串,預設undefined代表不寫入stdin
|
|
327
358
|
* @param {String|Function} [opt.validate=undefined] 輸入stdout驗證規則字串或自訂驗證函數,規則字串支援'nonempty'(非空)、'json'(可JSON.parse)、'min:100'(長度下限),多規則可用逗號串接例如'nonempty,json',自訂函數為(stdout)=>Boolean,預設undefined代表不驗證
|
|
328
359
|
* @param {Number} [opt.maxBuffer=10485760] 輸入stdout與stderr各自累積之最大字元數正整數,超過部分將不再累積,預設10485760(10MB)
|
|
329
|
-
* @param {Function} [opt.onStdout=undefined] 輸入stdout串流回調函數,格式為(chunk)=>{}
|
|
330
|
-
* @param {Function} [opt.onStderr=undefined] 輸入stderr串流回調函數,格式為(chunk)=>{},預設undefined
|
|
360
|
+
* @param {Function} [opt.onStdout=undefined] 輸入stdout串流回調函數,格式為(chunk)=>{},回調內拋出之錯誤不會被攔截,將成為未捕捉例外,請於回調內自行處理,預設undefined
|
|
361
|
+
* @param {Function} [opt.onStderr=undefined] 輸入stderr串流回調函數,格式為(chunk)=>{},行為同onStdout,預設undefined
|
|
331
362
|
* @param {Object} [opt.env=undefined] 輸入本次調用額外注入之環境變數物件,會併入process.env之後(同名以此為準,含PYTHONIOENCODING),僅作用於該次子進程而不影響本進程之process.env,故並行調用可各自帶不同值;值為undefined代表移除該變數(可遮蔽繼承值),值為null會成為字面字串'null'故建議勿傳;Windows下環境變數大小寫不敏感,同名不同大小寫(如Path與PATH)亦視為同名覆蓋,預設undefined代表不覆寫任何變數
|
|
332
|
-
* @param {Number} [opt.maxRetries=0] 輸入失敗後最大重試次數非負整數,遇ENOENT(命令不存在)
|
|
363
|
+
* @param {Number} [opt.maxRetries=0] 輸入失敗後最大重試次數非負整數,遇ENOENT(命令不存在)、exit code 2(參數錯誤)或無法啟動子進程之同步錯誤(如參數過長、權限不足、非有效執行檔、參數含空字元)視為不可重試而立即中止,預設0
|
|
333
364
|
* @param {Number} [opt.retryDelayMs=5000] 輸入重試間隔毫秒正整數,實際間隔為retryDelayMs乘以重試次數且上限15000ms,預設5000
|
|
334
365
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、pid(子進程pid)、attempts(實際嘗試次數)
|
|
335
366
|
* @example
|
|
@@ -508,6 +539,9 @@ async function execCli(command, args = [], opt = {}) {
|
|
|
508
539
|
}
|
|
509
540
|
|
|
510
541
|
//不可重試的錯誤
|
|
542
|
+
if (kNoRetry.has(lastResult)) {
|
|
543
|
+
break //無法啟動子進程之同步錯誤(參數過長、權限不足、非有效執行檔、參數檢查錯誤等), 重試無意義
|
|
544
|
+
}
|
|
511
545
|
if (lastResult.error.includes('ENOENT')) {
|
|
512
546
|
break //命令不存在, 重試無意義
|
|
513
547
|
}
|
package/src/execCliCore.mjs
CHANGED
package/src/execCliJsonRpc.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import execCliSession from './execCliSession.mjs'
|
|
|
12
12
|
/**
|
|
13
13
|
* 對stdio協定型CLI依序送出一行一則JSON之請求並收集回應(疊於execCliSession之上),適用codex app-server、MCP之stdio伺服器等以換行分隔JSON之協定(LSP為Content-Length分幀,不適用)
|
|
14
14
|
* 不內建任何協定知識,initialize/initialized等握手由呼叫端以請求陣列表達
|
|
15
|
-
* 本函數不throw,一律以結果物件之ok與error、errorType
|
|
15
|
+
* 本函數不throw,一律以結果物件之ok與error、errorType欄位回報成敗,error不含args與env之值
|
|
16
16
|
*
|
|
17
17
|
* 規則:
|
|
18
18
|
* - 請求物件含method者為請求,自動配id(自0遞增)並等待同id之回應;含notify者為通知,不配id,送出即繼續
|
|
@@ -34,7 +34,7 @@ import execCliSession from './execCliSession.mjs'
|
|
|
34
34
|
* @param {Boolean} [opt.continueOnError=false] 輸入回應含error時是否繼續送出後續請求布林值,預設false
|
|
35
35
|
* @param {Function} [opt.onNotify=undefined] 輸入伺服器通知(無id訊息)回調函數,格式為(msg)=>{},預設undefined
|
|
36
36
|
* @param {Function} [opt.onServerRequest=undefined] 輸入伺服器發給客戶端之請求(帶id且含method)回調函數,格式為(msg, session)=>{},可用session.writeLine回覆,預設undefined
|
|
37
|
-
* @param {Function} [opt.onStderr=undefined] 輸入stderr片段回調函數,格式為(chunk)=>{},預設undefined
|
|
37
|
+
* @param {Function} [opt.onStderr=undefined] 輸入stderr片段回調函數,格式為(chunk)=>{},預設undefined。onNotify、onServerRequest、onStderr內拋出之錯誤不會被攔截,將成為未捕捉例外,請於回調內自行處理
|
|
38
38
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件,內含ok(是否全部成功布林值)、results(以method為鍵之result物件,失敗者為null)、responses(依序之回應陣列,每項為{ id, method, result, error })、error(錯誤訊息字串,成功時為空字串)、errorType(錯誤類型字串: params(參數錯誤)、notfound(命令不存在)、timeout(逾時)、exit(子進程於回應前結束)、rpc(回應含error),成功時為空字串)、durationMs(耗時毫秒)、exitCode(子進程離開碼)
|
|
39
39
|
* @example
|
|
40
40
|
* //need test in nodejs
|
package/src/execCliSession.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import ispint from './ispint.mjs'
|
|
|
10
10
|
import isp0int from './isp0int.mjs'
|
|
11
11
|
import execProcessKillPid from './execProcessKillPid.mjs'
|
|
12
12
|
import execCliCore from './execCliCore.mjs'
|
|
13
|
+
import execSpawnError from './_execSpawnError.mjs'
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
//buildSpawnArgs, buildSpawnEnv, 共用核心
|
|
@@ -19,7 +20,7 @@ let { buildSpawnArgs, buildSpawnEnv } = execCliCore
|
|
|
19
20
|
/**
|
|
20
21
|
* 建立互動式stdio工作階段(session),用於stdio協定型之長駐CLI(如MCP之stdio伺服器、codex app-server等一行一則JSON之程式),與execCli「寫完input即關閉stdin並等待結束」之一次性契約不同,本函數維持stdin開啟,由呼叫端逐次write,直到呼叫stop()才關閉stdin並等待子進程自行退出,逾寬限期則以execProcessKillPid樹殺
|
|
21
22
|
* Windows下npm全域安裝之命令為.cmd批次檔,內部經execCliCore.buildSpawnArgs解析為以node執行其JS入口,不使用shell:true,故樹殺可確實涵蓋子孫程序
|
|
22
|
-
* 本函數不throw亦不reject,spawn失敗(如ENOENT)以onExit與stop()
|
|
23
|
+
* 本函數不throw亦不reject,spawn失敗(如ENOENT)以onExit與stop()之結果物件回報,其error不含args與env之值
|
|
23
24
|
*
|
|
24
25
|
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/execCliSession.test.mjs Github}
|
|
25
26
|
* @memberOf wsemi
|
|
@@ -35,7 +36,7 @@ let { buildSpawnArgs, buildSpawnEnv } = execCliCore
|
|
|
35
36
|
* @param {Function} [opt.onLine=undefined] 輸入stdout逐行回調函數,格式為(line)=>{},line不含換行(\\r\\n與\\n皆視為換行),子進程結束時殘餘之不完整行亦會發出,預設undefined
|
|
36
37
|
* @param {Function} [opt.onStdout=undefined] 輸入stdout原始片段回調函數,格式為(chunk)=>{},已經UTF-8跨chunk解碼,供非行式協定(如LSP之Content-Length分幀)自行分幀,預設undefined
|
|
37
38
|
* @param {Function} [opt.onStderr=undefined] 輸入stderr片段回調函數,格式為(chunk)=>{},預設undefined
|
|
38
|
-
* @param {Function} [opt.onExit=undefined] 輸入子進程結束回調函數,格式為(result)=>{},result同stop()之回傳,預設undefined
|
|
39
|
+
* @param {Function} [opt.onExit=undefined] 輸入子進程結束回調函數,格式為(result)=>{},result同stop()之回傳,預設undefined。onLine、onStdout、onStderr、onExit內拋出之錯誤不會被攔截,將成為未捕捉例外,請於回調內自行處理
|
|
39
40
|
* @returns {Object} 回傳工作階段物件s,內含pid(子進程pid,spawn失敗為null)、exited(是否已結束布林值)、write(str)(寫入stdin,回傳Promise,resolve為是否寫入成功布林值,子進程已結束時靜默resolve(false))、writeLine(str)(補換行後寫入)、stop()(關閉stdin並等待結束,回傳Promise,resolve回傳結果物件,內含code、signal、exited、timeout、killed、error、stderr、durationMs、pid;多次呼叫回傳同一Promise)
|
|
40
41
|
* @example
|
|
41
42
|
* //need test in nodejs
|
|
@@ -212,10 +213,10 @@ function execCliSession(command, args = [], opt = {}) {
|
|
|
212
213
|
return s
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
//spawn
|
|
216
|
-
let sp = buildSpawnArgs(command, args)
|
|
216
|
+
//spawn, buildSpawnArgs之拋錯亦於此回報
|
|
217
217
|
let proc
|
|
218
218
|
try {
|
|
219
|
+
let sp = buildSpawnArgs(command, args)
|
|
219
220
|
proc = spawn(sp.file, sp.args, {
|
|
220
221
|
cwd,
|
|
221
222
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -225,17 +226,33 @@ function execCliSession(command, args = [], opt = {}) {
|
|
|
225
226
|
})
|
|
226
227
|
}
|
|
227
228
|
catch (err) {
|
|
228
|
-
//spawn同步拋錯(
|
|
229
|
+
//spawn同步拋錯(如EPERM、ENAMETOOLONG、參數檢查錯誤), 訊息經execSpawnError去除參數值
|
|
230
|
+
let e = execSpawnError(err)
|
|
229
231
|
s.write = async () => false
|
|
230
232
|
s.writeLine = async () => false
|
|
231
233
|
s.stop = stopNoProc
|
|
232
234
|
setTimeout(() => {
|
|
233
|
-
settle({ error: `${
|
|
235
|
+
settle({ error: `${e.code}: ${e.message}` })
|
|
234
236
|
}, 1)
|
|
235
237
|
return s
|
|
236
238
|
}
|
|
237
239
|
s.pid = proc.pid || null
|
|
238
240
|
|
|
241
|
+
//error, 須於存取stdio前監聽: node對EMFILE、ENFILE不建立stdio即於nextTick發出'error', 先存取會拋TypeError且'error'無人監聽而拉倒程序
|
|
242
|
+
// 可能為'找不到可執行檔'(ENOENT), '權限不足', '系統資源用盡'等, 訊息經execSpawnError去除參數值
|
|
243
|
+
proc.on('error', (err) => {
|
|
244
|
+
let e = execSpawnError(err)
|
|
245
|
+
settle({ error: `${e.code}: ${e.message}` })
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
//stdio, EMFILE、ENFILE時node未建立(undefined), 由上方error回報
|
|
249
|
+
if (!proc.stdout || !proc.stderr || !proc.stdin) {
|
|
250
|
+
s.write = async () => false
|
|
251
|
+
s.writeLine = async () => false
|
|
252
|
+
s.stop = stopNoProc
|
|
253
|
+
return s
|
|
254
|
+
}
|
|
255
|
+
|
|
239
256
|
//TextDecoder, 跨chunk邊界正確解碼UTF-8, 避免多位元組中文字元被切成亂碼(U+FFFD)
|
|
240
257
|
let stdoutDecoder = new TextDecoder('utf-8')
|
|
241
258
|
let stderrDecoder = new TextDecoder('utf-8')
|
|
@@ -327,11 +344,6 @@ function execCliSession(command, args = [], opt = {}) {
|
|
|
327
344
|
}, timeoutMs)
|
|
328
345
|
}
|
|
329
346
|
|
|
330
|
-
//error, 監聽(例如執行spawn本身)錯誤訊息, 可能為'找不到可執行檔'(ENOENT), '權限不足', '系統資源用盡'等
|
|
331
|
-
proc.on('error', (err) => {
|
|
332
|
-
settle({ error: `${err.code || 'UNKNOWN'}: ${err.message}` }) //待確認用err.toString()還是err.message更佳 bbb
|
|
333
|
-
})
|
|
334
|
-
|
|
335
347
|
//close
|
|
336
348
|
proc.on('close', (code, signal) => {
|
|
337
349
|
if (settled) {
|