electron應用開發:命令npm install electron的執行邏輯

我們來徹底解析?npm install electron?這個命令背后的完整執行邏輯。這是一個非常精妙的過程,遠不止下載一個簡單的 JavaScript 包那么簡單。理解了它,你就能透徹地明白 Electron 開發環境的運作原理,并能輕松解決各種安裝問題。

npm install electron?的執行過程可以分為兩大階段:

  1. npm 的通用包安裝流程

  2. Electron 包特有的后安裝腳本流程

整個過程的核心流程如下圖所示,它揭示了一個關鍵點:npm install?只是下載了一個“安裝器”,而真正的 Electron 運行時是由這個“安裝器”再下載的。

第一階段:npm 的通用安裝流程

當你運行?npm install electron?時,npm(或 yarn/pnpm)首先會執行所有包的標準安裝步驟(即上圖“階段一”):

  1. 解析依賴和確定版本

    • npm 讀取你的?package.json?中的?"dependencies"?和?"devDependencies"

    • 它根據語義化版本規則(SemVer)和可能存在的?package-lock.json/yarn.lock?文件,確定要安裝的?electron?的具體版本(例如?28.0.0)。

  2. 獲取包信息

    • npm 會聯系配置的注冊表(默認是?https://registry.npmjs.org?)來獲取?electron?包的元數據(metadata)。

    • 這些元數據包含了包的所有版本信息、依賴關系、以及最重要的——分發標簽(dist-tags)?和?壓縮包地址(tarball)

  3. 獲取包壓縮包

    • npm 根據元數據中的?dist.tarball?URL 下載?electron?的壓縮包(一個?.tgz?文件)。

    • 這個壓縮包會被下載到?npm 的全局緩存目錄~/.npm?或由?npm_config_cache?環境變量指定)。

  4. 解壓到 node_modules

    • npm 將緩存中的?.tgz?文件解壓到你的項目?node_modules?目錄中。

    • 此時,你的?node_modules/electron?目錄結構看起來是這樣的:

      node_modules/electron/
      ├── package.json
      ├── index.js
      └── install.js    <--- 這是關鍵文件!
    • 注意:此時目錄里還沒有真正的 Electron 應用程序二進制文件。這個?electron?包只是一個“殼”或“安裝器”。

至此,一個普通的 JavaScript 包就已經安裝完畢了。但對于?electron?來說,這僅僅是開始。它的?package.json?中定義了一個?"scripts"?字段,其中包含?"install": "node install.js"。這觸發了第二階段。


第二階段:Electron 特有的后安裝腳本流程

npm 在解壓完包之后,會檢測到?package.json?中的?install?腳本,并自動執行?node install.js(即上圖“階段二”)。這才是下載真正 Electron 可執行文件的核心環節。

install.js?腳本(由?@electron/get?包提供)的內部邏輯如下:

  1. 環境檢測

    • 腳本首先檢測當前的操作系統(process.platform)和架構(process.arch),例如?linux-x64,?win32-ia32,?darwin-arm64

  2. 確定下載版本

    • 讀取?node_modules/electron/package.json?中的?version?字段,確定需要下載的 Electron 二進制文件版本。

  3. 計算緩存路徑

    • 腳本會計算一個緩存目錄。默認是:

      • Linux:?~/.cache/electron/

      • macOS:?~/Library/Caches/electron/

      • Windows:?%LOCALAPPDATA%/electron/Cache/

    • 你也可以用?ELECTRON_CACHE?環境變量覆蓋這個路徑。

  4. 檢查緩存

    • 腳本會檢查緩存目錄中是否已經存在對應版本和平臺的 Electron ZIP 壓縮包(例如?electron-v28.0.0-linux-x64.zip)以及其 SHA256 校驗和文件。

    • 如果存在且校驗通過,則跳過下載,直接使用緩存文件。這是離線安裝能成功的核心。

  5. 下載(如果緩存不存在)

    • 如果緩存中沒有有效的文件,腳本會從網絡下載。默認的下載源是 GitHub Releases(https://github.com/electron/electron/releases/download/v${version}/)。

    • 你可以通過?ELECTRON_MIRROR?環境變量來設置鏡像源,加速下載,例如?https://npmmirror.com/mirrors/electron/

  6. 校驗完整性

    • 下載完成后,腳本會計算下載文件的 SHA256 哈希值,并與從網上下載的?SHASUMS256.txt?文件中的官方值進行比對,確保文件完整無誤。

  7. 保存到緩存

    • 下載并驗證通過的 ZIP 文件會被保存到緩存目錄中,供未來使用。

  8. 解壓并創建符號鏈接

    • 這是最后一步,也是最巧妙的一步。腳本會將緩存中的 ZIP 文件解壓到緩存目錄的一個子目錄中(例如?~/.cache/electron/28.0.0)。

    • 然后,它會在你的項目?node_modules/electron?目錄中創建一個名為?dist?的符號鏈接(Symbolic Link),指向剛才解壓出來的目錄。

    • 同時,它會在?node_modules/electron?目錄下創建一個?path.txt?文件,文件內容只有一行:dist。這個文件告訴?electron?這個 npm 包:“真正的可執行文件在?dist?這個子目錄里”。

最終,你的?node_modules/electron?目錄結構看起來是這樣的:

node_modules/electron/
├── package.json
├── index.js
├── install.js
├── path.txt          # 內容為 "dist"
└── dist -> /home/username/.cache/electron/28.0.0/  # 一個符號鏈接

當你通過?require('electron')?或?npm start?啟動應用時,index.js?會讀取?path.txt,找到?dist?目錄,最終啟動的是?dist/electron(或?dist/electron.exe)這個真正的 Electron 可執行文件。

這個流程的核心是?@electron/get?這個庫。它負責處理所有繁重的工作:平臺檢測、URL 組裝、下載、校驗和緩存管理。

📍 默認緩存路徑

Electron 的緩存目錄因操作系統而異。下表列出了各系統常見的默認緩存位置:

操作系統默認緩存路徑備注
Linux$XDG_CACHE_HOME/electron/如果?$XDG_CACHE_HOME?未設置,則使用?~/.cache/electron
~/.cache/electron/
macOS~/Library/Caches/electron/
Windows%LOCALAPPDATA%/electron/Cache/通常為?C:\Users\[用戶名]\AppData\Local\electron\Cache
~/AppData/Local/electron/Cache/

💡 提示

  • 對于舊版本的 Electron,緩存也可能出現在?~/.electron/?目錄中。

  • 緩存目錄通常包含 Electron 二進制包的 ZIP 文件(如?electron-v1.7.9-darwin-x64.zip)和對應的校驗和文件(如?SHASUMS256.txt-1.7.9)。

Electron 下載緩存目錄(如?~/.cache/electron)里那些由一長串 SHA256 哈希值命名的子目錄,主要用于存儲和管理從網絡下載的 Electron 二進制文件(如 ZIP 壓縮包)及其校驗文件,目的是確保下載文件的完整性、提供緩存機制以加速后續安裝,并支持離線安裝。

🔍 目錄命名與工作原理

這些長哈希值子目錄的名稱,并非其內部 Electron 壓縮包內容的哈希,而是由下載 Electron 二進制文件的 URL 計算得出的 SHA256 哈希值

  • 命名來源:例如,你要下載 Electron v25.3.2 的發布包,其下載 URL 可能是?https://github.com/electron/electron/releases/download/v25.3.2/。對此 URL 進行 SHA256 哈希計算,便會得到對應的目錄名(如?8094ad17c1b056c796ee8402115143ac99458e895da15291d7aaa6ba8359b20c)。

如下:

echo -n 'https://github.com/electron/electron/releases/download/v25.3.2' | shasum -a 256 | awk '{print $1}'
# 結果:8094ad17c1b056c796ee8402115143ac99458e895da15291d7aaa6ba8359b20c
  • 目錄內容:在這些哈希值命名的子目錄下,你通常會找到兩個文件:

    1. Electron 二進制壓縮包?(如?electron-v25.3.2-win32-x64.zip):這就是下載的 Electron 本體。

    2. 校驗和文件:通常是?SHASUMS256.txt,該文件包含了 Electron 官方針對此次發布的所有二進制文件的正確 SHA256 哈希值。

?? 工作流程與重要性

@electron/get?這個庫(負責下載 Electron)會遵循以下流程:

  1. 接收下載請求:當執行?npm install electron?或項目需要特定版本 Electron 時,@electron/get?會開始工作。

  2. 計算目錄名:根據目標 Electron 版本的下載 URL 計算 SHA256 哈希值,并確定對應的緩存子目錄路徑。

  3. 檢查緩存:首先檢查該目錄是否已存在所需的 ZIP 包和?SHASUMS256.txt?文件。

  4. 下載與驗證(如需要):若緩存中沒有,則從網絡下載。下載完成后,會使用?SHASUMS256.txt?中的官方哈希值校驗下載的 ZIP 文件是否完整、未被篡改。

  5. 使用緩存:若緩存中已有且校驗通過,或網絡下載校驗通過后,就會直接使用緩存的文件,從而避免重復下載。

🛠? 離線安裝與手動緩存

理解這個機制對離線環境安裝 Electron 至關重要

  1. 手動準備緩存:在一臺有網絡的機器上,下載所需版本的 Electron ZIP 包和對應的?SHASUMS256.txt?文件。

  2. 計算目錄名(關鍵步驟):你需要根據下載 URL 計算 SHA256 哈希來確定目錄名稱。例如,使用 Node.js 腳本:

    const crypto = require('crypto');
    const url = 'https://github.com/electron/electron/releases/download/v25.3.2'; // 替換為你需要的版本,注意版本后不要加'/'符號結尾
    const hash = crypto.createHash('sha256').update(url).digest('hex');
    console.log(hash); // 輸出即為所需的目錄名
  3. 創建目錄并放置文件:在緩存目錄(如?~/.cache/electron)下,創建一個以剛才計算出的哈希值為名的子目錄,然后將下載好的?electron-v25.3.2-平臺-架構.zip?和?SHASUMS256.txt?文件放入其中。

  4. 離線安裝:在離線機器上執行?npm install electron?時,@electron/get?會計算出相同的哈希值目錄,并在其中找到文件,從而完成離線安裝。

?? 自定義緩存路徑

通過設置?ELECTRON_CACHE?環境變量,可以覆蓋默認的緩存位置。這在你想使用更大容量磁盤或統一緩存位置時很有用。

# 在 Linux/macOS 上設置
export ELECTRON_CACHE="/path/to/your/custom/cache"
# 在 Windows 上設置
set ELECTRON_CACHE=D:\path\to\your\custom\cache
緩存策略

@electron/get?具有智能的緩存策略:

  1. 優先使用緩存:在下載前,它總是先檢查緩存目錄中是否已存在相同版本的 ZIP 文件和解壓后的目錄。

  2. 校驗完整性:即使文件存在,它也會使用從 GitHub 下載的?SHASUMS256.txt?文件來驗證緩存文件的 SHA256 哈希值,確保文件沒有損壞或被篡改。

  3. 避免重復解壓:如果緩存中已存在解壓后的目錄,它會直接創建符號鏈接,節省時間和 CPU 資源。

環境變量的影響

整個流程可以通過環境變量進行定制和優化,這也是離線開發的關鍵:

  • ELECTRON_MIRROR: 覆蓋默認的 GitHub Releases 下載基地址。例如,設置為?"https://npmmirror.com/mirrors/electron/"?來使用國內鏡像加速。

  • ELECTRON_CUSTOM_DIR?和?ELECTRON_CUSTOM_FILENAME: 自定義鏡像的路徑結構和文件名。

  • ELECTRON_CACHE: 覆蓋默認的緩存目錄(如上表所示)。

  • ELECTRON_SKIP_BINARY_DOWNLOAD: 如果設置為?1install.js?腳本會跳過所有下載步驟。這在只需要?electron?的 Node.js 依賴(例如在 CI 中運行 lint)時非常有用。

  • ELECTRON_OVERRIDE_DIST_PATH:?離線開發神器。如果設置了這個變量,腳本會完全跳過下載和緩存邏輯,直接使用該路徑下的已有二進制文件。

下載url

當你運行?npm install electron?時,Electron 的預編譯二進制文件默認會從 GitHub Releases 下載。這個過程由?@electron/get?模塊處理。下面的表格匯總了關鍵信息,幫助你快速了解:

事項說明
默認下載基址https://github.com/electron/electron/releases/download/v$VERSION/
完整URL示例https://github.com/electron/electron/releases/download/v28.0.0/electron-v28.0.0-win32-x64.zip
加速下載(國內鏡像)設置環境變量?ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
自定義版本和平臺使用?--arch?和?--platform?標志,如?npm install --arch=ia32 electron
緩存位置默認在?~/.cache/electron/?(Linux),?~/Library/Caches/electron/?(macOS),?%LOCALAPPDATA%/electron/Cache?(Windows)
🌐 加速下載與自定義鏡像

如果你從 GitHub 下載速度較慢或遇到網絡問題,可以使用國內的鏡像源。通過設置?ELECTRON_MIRROR?環境變量,可以改變下載的基礎 URL。例如,使用 npmmirror.com(原淘寶鏡像):

# 在 Linux/macOS 上設置
export ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
# 在 Windows 上設置
set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/

設置之后,再次運行?npm install electron,下載速度通常會顯著提升。

你還可以通過環境變量?ELECTRON_CUSTOM_DIR?和?ELECTRON_CUSTOM_FILENAME?來進一步自定義下載路徑和文件名。例如:

ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
ELECTRON_CUSTOM_DIR="{{ version }}"  # 指定目錄格式

這樣,最終的下載 URL 會變為類似于?https://npmmirror.com/mirrors/electron/28.0.0/electron-v28.0.0-linux-x64.zip?的形式。

💾 緩存機制

@electron/get?模塊會將下載的 Electron 二進制文件緩存在本地目錄中。主要的緩存路徑如下:

  • Linux: "$XDG_CACHE_HOME~/.cache" + "/electron/"

  • macOS:?~/Library/Caches/electron/

  • Windows:?%LOCALAPPDATA%/electron/Cache?或?~/AppData/Local/electron/Cache/

緩存目錄通常包含 Electron 版本的官方 zip 文件及其校驗和文件緩存的命名結構是?[checksum]/[filename]。你可以通過設置?ELECTRON_CACHE?環境變量來覆蓋本地緩存的位置。

?? 其他相關配置
  • 跳過二進制包下載:如果只想安裝?electron?的 npm 包而不下載二進制文件(例如在 CI 中運行測試),可以設置環境變量?ELECTRON_SKIP_BINARY_DOWNLOAD=1

  • 使用代理:如果你需要使用 HTTP 代理,可以設置?ELECTRON_GET_USE_PROXY?環境變量,并根據你的系統 Node 版本配置其他相關的代理環境變量。

  • 故障排查:如果安裝遇到網絡問題(如 ELIFECYCLE、EAI_AGAIN、ECONNRESET、ETIMEDOUT 等錯誤),可以嘗試使用?--verbose?標志顯示詳細下載進度,或切換網絡環境。必要時,也可手動從 GitHub Releases 下載對應的二進制文件并清除緩存重試。

總結與關鍵點

  • 兩步過程npm install electron?實際上是安裝了一個?“安裝器”(Node.js 包),然后這個“安裝器”再下載真正的?“運行時”?(平臺相關的二進制文件)。

  • 緩存是關鍵:默認情況下,二進制文件會被緩存,第二次安裝會快很多,并且是離線安裝的基礎。

  • 環境變量控制行為:你可以通過?ELECTRON_MIRROR,?ELECTRON_CACHE,?ELECTRON_SKIP_BINARY_DOWNLOAD?等環境變量精細控制整個下載過程。

  • 離線安裝的原理:就是將在線環境下已經下載好的?~/.cache/electron?和?~/.npm?目錄完整地復制到離線機器上,并確保環境變量配置正確,讓安裝腳本能命中緩存。

理解了這個邏輯,你就能從容應對各種網絡問題、鏡像配置和離線開發場景了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94540.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94540.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94540.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Visual Studio 2022不同項目設置不同背景圖

ClaudiaIDE Visual Studio 地址&#xff1a;https://marketplace.visualstudio.com/items?itemNamekbuchi.ClaudiaIDE&ssrfalse#overviewgithub 地址&#xff1a;https://github.com/buchizo/ClaudiaIDE/ 這是一個Visual Studio擴展&#xff0c;可以讓你設置自定義背景圖…

React頁面使用ant design Spin加載遮罩指示符自定義成進度條的形式

React頁面使用ant design Spin加載遮罩指示符自定義成進度條的形式具體實現&#xff1a;import React, { useState, useEffect, } from react; import { Spin, Progress, } from antd; import styles from ./style.less;const App () > {// 全局加載狀態const [globalLoadi…

TCP并發服務器構建

TCP并發服務器構建&#xff1a; 單循環服務器&#xff1a;服務端同一時刻只能處理單個客戶端的任務 并發服務器&#xff1a;服務端同一時刻能夠處理多個客戶端的任務 產生多個套接字可建立多個連接&#xff1a;TCP服務端并發模型&#xff1a; 1&#xff1a;使用多進程 頭文件&a…

優選算法-常見位運算總結

1.基礎位運算&#xff1a; >> :右移運算符&#xff1a; 邏輯右移&#xff08;無符號數&#xff09;&#xff1a;高位補 0&#xff0c;低位直接丟棄。 示例&#xff1a;8 >> 2&#xff08;二進制 1000 右移 2 位&#xff09;結果為 0010&#xff08;十進制 2&#…

記一次MySQL數據庫的操作練習

數據庫基礎使用數據庫的操作&#xff1a;1.使用命令行連接數據庫。在命令行鍵入”mysql -u root -p”命令。2.列出MySQL數據庫管理系統的數據庫列表。在命令行鍵入”show databases;”命令。3.創建數據庫。在命令行鍵入”create database database_name;”命令。使用”show dat…

C++STL-list 底層實現

目錄 一、實現框架 二、list_node節點類的模擬實現 節點構造函數 三、list_iterator迭代器的模擬實現 迭代器類的模板參數說明 構造函數 *運算符重載 運算符的重載 --運算符的重載 運算符的重載 !運算符的重載 list的模擬實現 默認成員函數 構造函數 拷貝構造函…

解決網站圖片加載慢:從架構原理到實踐

在當前的數字商業環境中&#xff0c;用戶的在線體驗至關重要。當一個潛在客戶訪問企業網站或電商平臺時&#xff0c;如果頁面加載過程遲緩&#xff0c;特別是圖片和視頻內容無法快速顯示&#xff0c;用戶的耐心會迅速耗盡。研究數據表明&#xff0c;網站加載時間與用戶跳出率和…

windows注冊表:開機自啟動程序配置

目錄 一、注冊表位置 系統范圍的開機自啟動程序 當前用戶的開機自啟動程序 二、配置步驟 三、注意事項 四、其他方法 任務計劃程序 啟動文件夾 1. 創建程序快捷方式 2. 打開 Startup 文件夾 3. 將快捷方式移動到 Startup 文件夾 4. 驗證程序是否自動啟動 注意事項 …

(11)用于無GPS導航的制圖師SLAM(一)

文章目錄 前言 1 安裝 RPLidar 和 Pixhawk 2 檢查 RPLidar 的串行端口 3 安裝更多軟件包 4 創建Catkin工作空間 5 安裝 RPLidar 節點 6 安裝 Google Cartographer 前言 本頁展示了如何使用 RPLidarA2 激光雷達(RPLidarA2 lidar)設置 ROS 和 Google Cartographer SLAM&a…

車載診斷架構 --- 基于整車功能的正向診斷需求開發

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

字帖生成器怎么用?電腦手機雙端操作指南

字帖生成器是一款支持電腦端和手機端的免費練字工具&#xff0c;可一鍵生成PDF格式字帖并直接打印使用。本文基于官方公開版本&#xff0c;提供無廣告、無營銷的實測操作指南。 工具基礎信息 軟件名稱&#xff1a;字帖生成器適用設備&#xff1a;Windows、安卓/鴻蒙核心功能&…

pycharm 遠程連接服務器報錯

配置遠程鏈接的時候出現報錯 Command finished with exit code 139 Execution was killed due to timeout Failed to execute command Rsync command ‘rsync’ was not found neither in local PATH nor as full executable path Starting introspection for Python… 放假前好…

局域網共享文件夾

準備工作&#xff1a; A電腦&#xff08;共享端&#xff09; B電腦&#xff08;本機&#xff09;在A電腦&#xff0c;選好要共享的目錄&#xff0c;然后右鍵屬性 > 高級共享 > 共享此文件夾 > 權限(全開)然后找到此電腦&#xff0c;右鍵&#xff0c;打開屬性&#xff…

時序數據庫全景指南:從場景選型到內核拆解

1. 什么是時序數據 時序數據&#xff08;Time-Series Data&#xff09; 是在時間上連續產生、且帶有時間戳的觀測值序列&#xff0c;典型特征&#xff1a;維度描述高并發寫百萬點/秒&#xff0c;追加為主寫多讀少90 % 查詢是降采樣或聚合時效性越新越熱&#xff0c;舊數據價值遞…

深入解析 Oracle 內存架構:駕馭 SGA 與 PGA 的性能藝術

引言&#xff1a;數據庫的心臟與大腦如果說磁盤上的數據文件是 Oracle 數據庫的“身體”&#xff0c;是永久存儲的基石&#xff0c;那么內存結構就是其“心臟與大腦”。它負責所有計算活動的發生&#xff0c;決定了數據泵送的速度與效率。一個配置得當、運行順暢的內存體系&…

竣工驗收備案識別技術:通過AI和OCR實現智能化文檔處理,提升效率與準確性,推動建筑行業數字化轉型。

竣工驗收備案是建設工程項目投入使用的最終法定程序&#xff0c;是確保工程符合規劃、質量、消防、環保等各項要求的核心關口。傳統的備案流程依賴大量紙質文檔和人工審核&#xff0c;效率低下且易出錯。隨著人工智能與大數據技術的崛起&#xff0c;竣工驗收備案識別技術應運而…

76 最小覆蓋子串

76 最小覆蓋子串 文章目錄76 最小覆蓋子串1 題目2 解答1 題目 給你一個字符串 s 、一個字符串 t 。返回 s 中涵蓋 t 所有字符的最小子串。如果 s 中不存在涵蓋 t 所有字符的子串&#xff0c;則返回空字符串 "" 。 注意&#xff1a; 對于 t 中重復字符&#xff0c;…

趣味學Rust基礎篇(變量與可變性)

這篇文章將用通俗的比喻和清晰的邏輯&#xff0c;帶你深入理解 Rust 變量背后的核心思想&#xff0c;讓你不僅“會用”&#xff0c;更能“明白為什么”。 Rust 的“盒子哲學”&#xff1a;變量、可變性、常量與隱藏 想象一下&#xff0c;Rust 里的變量就像一個個盒子。你把值&a…

2025年- H100-Lc208--912.排序數組(快速選擇排序)--Java版

1.題目2.思路 快速選擇排序的平均時間復雜度是O&#xff08;nlogn&#xff09;&#xff0c;最壞時間復雜度是O&#xff08;n^2&#xff09;&#xff0c;最好的時間復雜度是O&#xff08;nlogn&#xff09;&#xff0c;空間復雜度是O&#xff08;nlogn&#xff09;。 排序算法中…

解決 pdf.mjs 因 MIME 類型錯誤導致的模塊加載失敗問題

Mozilla PDF.js V4 開始&#xff0c;它官方分發確實只提供了 ESM 模塊&#xff08;.mjs&#xff09;&#xff0c;沒有以前的 pdf.js、pdf.worker.js UMD 版本了。 這個問題本質上是 瀏覽器要求以 application/javascript MIME 類型加載 ES Module&#xff0c;而你引入的 pdf.mj…