Global

Members

TIMER_TIME_MAX :Integer

Description:
  • 計時器(setTimeout、setInterval)之延遲毫秒上限

    值為 2^31 - 1 = 2147483647 毫秒, 約 24.8 天。

    【為何需要此上限 —— nodejs與瀏覽器之行為不同, 且皆為靜默失效】

    兩環境對超界值之處理機制不同, 實測(nodejs v24 / chromium)如下:

    輸入ms nodejs chromium


    2147483647 正常排程 正常排程 2^31 立即觸發(1ms)+警告 立即觸發(ToInt32為-2147483648) 2^32 - 5000 立即觸發(1ms)+警告 立即觸發(ToInt32為-5000) 2^32 + 5000 立即觸發(1ms)+警告 【約5秒後觸發】(ToInt32為5000) 2^32 + 100 立即觸發(1ms)+警告 【約100ms後觸發】(ToInt32為100) Infinity 立即觸發(1ms)+警告 立即觸發(ToInt32為0) NaN 立即觸發(1ms)+警告 立即觸發(ToInt32為0) -1 立即觸發(1ms)+警告 立即觸發

    nodejs為自家實作: 超過 2^31-1 一律夾為 1ms, 並以 process.on('warning') 發出 TimeoutOverflowWarning(NaN為TimeoutNaNWarning、負數為TimeoutNegativeWarning), 不拋錯。

    瀏覽器則依 WebIDL 之 long 型別轉換(即 ToInt32), 對超界值做 modulo 2^32 之【環繞】而非夾制, 且【完全沒有警告】。故 2^32 + 5000 於瀏覽器不是立即觸發, 而是 5 秒後觸發 —— 表面上看起來正常運作, 只是時間完全錯了(49.7天變成5秒), 比nodejs之立即觸發更難察覺。 環繞亦非單調: 輸入越大不代表延遲越長, 無法由行為反推輸入。

    wsemi為同構套件(前後端皆用), 故不可依賴runtime之行為, 必須於進入setTimeout之前自行夾制。 夾制後兩環境行為一致(皆為正常排程至上限), 環境差異消失。

    【為何是夾制而非退回預設值或拋錯】

    呼叫端給出超大值時, 其意圖顯然是「很久」或「幾乎不觸發」, 夾至 24.8 天最接近該意圖; 退回預設值(如50ms)會變成高頻輪詢, 與意圖完全相反且更危險; 拋錯則對既有呼叫端為破壞性變更。

    【與安全整數界線之區別 —— 兩者須分開檢核】

    ispint(v, { useLimitSafe: true }) 擋的是「非安全整數」(Infinity、1e300、超出 Number.MAX_SAFE_INTEGER 者), 但 2^31 本身【是】合法的安全整數, 只是超過計時器之32位元上限。 故型別與安全整數之檢核【擋不住】本上限, 兩層界線必須各自處理: 第一層 ispint / isp0int 等: 擋型別錯誤與非安全整數 第二層 本常數之夾制: 擋超過計時器上限者

    【用法】

    import cst from './_const.mjs' timeAlive = Math.min(timeAlive, cst.TIMER_TIME_MAX) //須用min, 用max會把正常值放大為上限

Source:

計時器(setTimeout、setInterval)之延遲毫秒上限

值為 2^31 - 1 = 2147483647 毫秒, 約 24.8 天。

【為何需要此上限 —— nodejs與瀏覽器之行為不同, 且皆為靜默失效】

兩環境對超界值之處理機制不同, 實測(nodejs v24 / chromium)如下:

輸入ms nodejs chromium


2147483647 正常排程 正常排程 2^31 立即觸發(1ms)+警告 立即觸發(ToInt32為-2147483648) 2^32 - 5000 立即觸發(1ms)+警告 立即觸發(ToInt32為-5000) 2^32 + 5000 立即觸發(1ms)+警告 【約5秒後觸發】(ToInt32為5000) 2^32 + 100 立即觸發(1ms)+警告 【約100ms後觸發】(ToInt32為100) Infinity 立即觸發(1ms)+警告 立即觸發(ToInt32為0) NaN 立即觸發(1ms)+警告 立即觸發(ToInt32為0) -1 立即觸發(1ms)+警告 立即觸發

nodejs為自家實作: 超過 2^31-1 一律夾為 1ms, 並以 process.on('warning') 發出 TimeoutOverflowWarning(NaN為TimeoutNaNWarning、負數為TimeoutNegativeWarning), 不拋錯。

瀏覽器則依 WebIDL 之 long 型別轉換(即 ToInt32), 對超界值做 modulo 2^32 之【環繞】而非夾制, 且【完全沒有警告】。故 2^32 + 5000 於瀏覽器不是立即觸發, 而是 5 秒後觸發 —— 表面上看起來正常運作, 只是時間完全錯了(49.7天變成5秒), 比nodejs之立即觸發更難察覺。 環繞亦非單調: 輸入越大不代表延遲越長, 無法由行為反推輸入。

wsemi為同構套件(前後端皆用), 故不可依賴runtime之行為, 必須於進入setTimeout之前自行夾制。 夾制後兩環境行為一致(皆為正常排程至上限), 環境差異消失。

【為何是夾制而非退回預設值或拋錯】

呼叫端給出超大值時, 其意圖顯然是「很久」或「幾乎不觸發」, 夾至 24.8 天最接近該意圖; 退回預設值(如50ms)會變成高頻輪詢, 與意圖完全相反且更危險; 拋錯則對既有呼叫端為破壞性變更。

【與安全整數界線之區別 —— 兩者須分開檢核】

ispint(v, { useLimitSafe: true }) 擋的是「非安全整數」(Infinity、1e300、超出 Number.MAX_SAFE_INTEGER 者), 但 2^31 本身【是】合法的安全整數, 只是超過計時器之32位元上限。 故型別與安全整數之檢核【擋不住】本上限, 兩層界線必須各自處理: 第一層 ispint / isp0int 等: 擋型別錯誤與非安全整數 第二層 本常數之夾制: 擋超過計時器上限者

【用法】

import cst from './_const.mjs' timeAlive = Math.min(timeAlive, cst.TIMER_TIME_MAX) //須用min, 用max會把正常值放大為上限

Type:
  • Integer

Methods

buildSpawnArgs(command, argsopt) → {Object}

Description:
  • 將command與args轉為可安全交給child_process.spawn之參數, 主要處理Windows下npm全域命令為.cmd批次檔而spawn無法直接執行之問題 策略優先順序:

    1. .exe → 直接spawn
    2. .cmd → 解析JS入口, 用node直接執行(繞過cmd.exe, 支援多行參數)
    3. .cmd但無法解析JS入口 → 透過cmd.exe /d /s /c執行(fallback, 不支援多行參數) 非Windows平台原樣回傳 回傳物件三個分支一律帶opt鍵(無額外設定時為{}), 呼叫端可直接展開至spawn之選項物件
Source:
Parameters:
Name Type Attributes Default Description
command String

輸入執行檔名稱字串

args Array <optional>
[]

輸入參數陣列, 預設[]

Returns:

回傳spawn參數物件, 內含file(執行檔), args(參數陣列), opt(spawn額外選項物件)

Type
Object

buildSpawnEnv(envExtraopt) → {Object}

Description:
  • 建立子進程之環境變數物件: 以process.env為底, 加入PYTHONIOENCODING=utf-8, 再併入envExtra(同名以envExtra為準) 每次呼叫重新求值且不動本進程process.env, 故並行調用可各自帶不同值 PYTHONIOENCODING對一般python直譯器有效(sys.stdout.encoding變utf-8); 對PyInstaller打包之exe無效, 其以隔離模式嵌入直譯器且文件明載刻意忽略PYTHONIOENCODING與PYTHONUTF8, 該類程式須於打包時以-X utf8_mode=1指定, 或由本進程以execProcess之codeCmd('auto'或系統字碼頁)解碼其輸出 Windows下環境變數大小寫不敏感但JS物件鍵敏感, 故併入前先移除大小寫不同之同名既有鍵, 否則呼叫端傳Path而process.env為PATH時兩鍵並存且Windows取原值, 注入靜默失效 此刪除僅限win32: POSIX環境變數大小寫敏感, Path與PATH為兩個獨立變數, 誤刪即退化

Source:
Parameters:
Name Type Attributes Description
envExtra Object <optional>

輸入額外注入之環境變數物件, 值為undefined代表移除該變數, 預設undefined代表不覆寫任何變數

Returns:

回傳可交給spawn之env物件

Type
Object

buildValidator(rule) → {function|null}

Description:
  • 建立驗證函式 支援'nonempty', 'json', 'min:100'或自訂函式, 多規則可用逗號串接

Source:
Parameters:
Name Type Description
rule String | function

輸入驗證規則字串或自訂函式

Returns:

回傳驗證函式, 無有效規則回傳null

Type
function | null

detectorEvent(start, optopt) → {Object}

Description:
  • 建立event模式之偵測器, 回傳EventEmitter並掛create與dispose

    create於microtask啟動: 晚於本次同步流程(例如Vue 2指令於bind時create, 元素於其後才插入), 早於任何計時器; create後同步才掛之監聽亦收得到首次事件 start(emit, addCleanup)於啟動時呼叫一次: 每建立一項資源(觀察器、計時器等)即以addCleanup登記其釋放函數, 使建立途中拋錯時已建立者仍可釋放 emit(name, value)經evEmit派發: 監聽器拋錯時有error監聽者則emit('error'), 否則console.error, 不中斷偵測亦不被吞掉; 已釋放後之emit不發出 start為null代表無法偵測(元素無效或環境不支援), 此時create與dispose皆可呼叫但永不觸發, error為原因 生命週期: 未開始 → 已create待啟動 → 偵測中 → 已釋放; create只於未開始時有效(重複呼叫不重複偵測), dispose後create無效, create後同一輪dispose則永不啟動; dispose可重複呼叫且回傳true start拋錯時視同無法偵測: error為所拋之值, 已登記之資源立即釋放, 轉為已釋放 釋放依登記之相反順序逐一呼叫, 單一釋放函數拋錯不影響其他; 已釋放後才登記者立即釋放

Source:
Parameters:
Name Type Attributes Default Description
start function | null

輸入開始偵測之函數

opt Object <optional>
{}

輸入設定物件

Properties
Name Type Attributes Default Description
error * <optional>
null

輸入無法偵測之原因

tag String <optional>
'detector'

輸入監聽器拋錯時console.error之標記

Returns:

回傳EventEmitter, 另有create、dispose函數與error屬性(可偵測時為null)

Type
Object

detectorFail(mode, reason) → {Promise|Object}

Description:
  • 無法偵測時之回報: promise模式回傳被拒絕之Promise, event模式回傳永不觸發之偵測器(error為原因)

Source:
Parameters:
Name Type Description
mode String

輸入模式

reason *

輸入原因

Returns:

回傳Promise或偵測器

Type
Promise | Object

escapeWinArg(arg) → {String}

Description:
  • 轉義cmd.exe的單一參數(cross-spawn escapeArgument邏輯) 參考: https://qntm.org/cmd

Source:
Parameters:
Name Type Description
arg String

輸入參數字串

Returns:

回傳轉義後參數字串

Type
String

escapeWinCmd(cmd) → {String}

Description:
  • 轉義cmd.exe的命令部分

Source:
Parameters:
Name Type Description
cmd String

輸入命令字串

Returns:

回傳轉義後命令字串

Type
String

execCliOnce(command, argsopt, optopt) → {Promise}

Description:
  • 單次非同步呼叫(內部使用, 不含重試邏輯)

Source:
Parameters:
Name Type Attributes Default Description
command String

輸入執行檔名稱字串

args Array <optional>
[]

輸入參數陣列

opt Object <optional>
{}

輸入設定物件

Returns:

回傳Promise, resolve回傳結果物件

Type
Promise

getMode(opt) → {String}

Description:
  • 取偵測模式, 'event'以外皆為'promise'

Source:
Parameters:
Name Type Description
opt Object

輸入設定物件

Returns:

回傳'promise'或'event'

Type
String

optNum(opt, key, def, ruleopt) → {Number}

Description:
  • 取設定物件之數值選項, 無效時回傳預設值; 供各函數之數值選項共用同一套規則, 不各自手寫檢查

    數字與數字字串皆可(isnum), 字串先轉為數字再判定, 例如'12.34'為12.34、'5'為5、' 7 '為7; 空字串、純空白字串、非數字字串、NaN、null、布林等皆非isnum, 一律用預設 以Number轉換後判定, 不用cdbl: cdbl把Infinity轉為有限之Number.MAX_VALUE, 會使有限與否之判定失效 低於下界(min, minOpen為true時須大於min)者用預設, below為'clamp'時改夾至下界 +Infinity依意圖處置: timer之計時器毫秒夾至計時器上限(同delay, 見_const.mjs), inf為'keep'者保留(例如容許誤差之無限大即任何變化皆容許), 其餘用預設; NaN用預設, -Infinity視為低於下界 int為true時須為整數(Infinity依上一條處置) timer為true時有效值再以Math.min夾至計時器上限: 超大值之意圖為「很久」, 夾至上限最接近其意圖, 不視為無效

Source:
Parameters:
Name Type Attributes Default Description
opt Object

輸入設定物件

key String

輸入選項鍵名

def Number

輸入無效時之預設值

rule Object <optional>
{}

輸入規則物件

Properties
Name Type Attributes Default Description
min Number <optional>
-Infinity

輸入下限

minOpen Boolean <optional>
false

輸入是否不含下限(須大於min)

below String <optional>
'def'

輸入低於下限時之處置, 'def'為用預設, 'clamp'為夾至下限(minOpen為true時仍用預設)

int Boolean <optional>
false

輸入是否須為整數

timer Boolean <optional>
false

輸入是否為計時器毫秒(夾至上限)

inf String <optional>
'def'

輸入非計時器之+Infinity之處置, 'def'為用預設, 'keep'為保留

Returns:

回傳數字

Type
Number

parseJsEntryFromCmd(cmdPath) → {String|null}

Description:
  • 從.cmd shim中解析出實際入口檔案路徑(可能為JS, 亦可能為原生.exe) npm全域安裝的.cmd格式固定, 末行為: ... "%_prog%" "%dp0%\node_modules...\entry" %* 入口可能為.js / .cjs / .mjs / 無副檔名 / .exe(如opencode的bin/opencode.exe), 故一律抓引號內node_modules後的相對路徑, 再以fsIsFile驗證實體檔存在 (只匹配.js會讓無副檔名入口落入cmd.exe fallback, 破壞多行prompt) 回傳後由buildSpawnArgs依副檔名決定: .exe直接spawn, 其餘交給node

Source:
Parameters:
Name Type Description
cmdPath String

輸入.cmd檔案路徑字串

Returns:

回傳入口檔案路徑字串, 無法解析回傳null

Type
String | null

parseVersion(v) → {Array|null}

Description:
  • 自字串解析版本號(取第一個「數字.數字.數字」)

Source:
Parameters:
Name Type Description
v String

輸入字串

Returns:

回傳三段整數陣列, 無法解析回傳null

Type
Array | null

resolveCommand(cmd) → {String}

Description:
  • 用where指令找到命令的實際路徑(.cmd / .exe)

Source:
Parameters:
Name Type Description
cmd String

輸入命令字串

Returns:

回傳解析後之命令路徑字串

Type
String