เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชธเชพเชฎเชพเชจเซเชฏ "เช•เซเชฒเชพเชฏเชจเซเชŸ-เชธเชฐเซเชตเชฐ" เช†เชฐเซเช•เชฟเชŸเซ‡เช•เซเชšเชฐเชฅเซ€ เชตเชฟเชชเชฐเซ€เชค, เชตเชฟเช•เซ‡เชจเซเชฆเซเชฐเชฟเชค เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเซ‹ เชฒเชพเช•เซเชทเชฃเชฟเช•เชคเชพ เชงเชฐเชพเชตเซ‡ เช›เซ‡:

  • เชฏเซเชเชฐ เชฒเซ‹เช—เซ€เชจ เช…เชจเซ‡ เชชเชพเชธเชตเชฐเซเชก เชธเชพเชฅเซ‡ เชกเซ‡เชŸเชพเชฌเซ‡เช เชธเซเชŸเซ‹เชฐ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชจเชฅเซ€. เชเช•เซเชธเซ‡เชธ เชฎเชพเชนเชฟเชคเซ€ เชซเช•เซเชค เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเช“ เชฆเซเชตเชพเชฐเชพ เชœ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เช…เชจเซ‡ เชคเซ‡เชฎเชจเซ€ เช…เชงเชฟเช•เซƒเชคเชคเชพเชจเซ€ เชชเซเชทเซเชŸเชฟ เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒ เชธเซเชคเชฐเซ‡ เชฅเชพเชฏ เช›เซ‡.
  • เชธเชฐเซเชตเชฐเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชจเชฅเซ€. เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชฒเซ‹เชœเชฟเช• เชฌเซเชฒเซ‹เช•เชšเซ‡เชจ เชจเซ‡เชŸเชตเชฐเซเช• เชชเชฐ เชเช•เซเชเชฟเช•เซเชฏเซเชŸ เช•เชฐเซ€ เชถเช•เชพเชฏ เช›เซ‡, เชœเซเชฏเชพเช‚ เชœเชฐเซ‚เชฐเซ€ เชฎเชพเชคเซเชฐเชพเชฎเชพเช‚ เชกเซ‡เชŸเชพ เชธเซเชŸเซ‹เชฐ เช•เชฐเชตเชพเชจเซเช‚ เชถเช•เซเชฏ เช›เซ‡.

เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพ เช•เซ€ เชฎเชพเชŸเซ‡ 2 เชชเซเชฐเชฎเชพเชฃเชฎเชพเช‚ เชธเชฒเชพเชฎเชค เชธเซเชŸเซ‹เชฐเซ‡เชœ เช›เซ‡ - เชนเชพเชฐเซเชกเชตเซ‡เชฐ เชตเซ‹เชฒเซ‡เชŸเซเชธ เช…เชจเซ‡ เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เชจเซเชถเชจเซเชธ. เชนเชพเชฐเซเชกเชตเซ‡เชฐ เชตเซ‹เชฒเซ‡เชŸเซเชธ เชฎเซ‹เชŸเชพเชญเชพเช—เซ‡ เช…เชคเซเชฏเช‚เชค เชธเซเชฐเช•เซเชทเชฟเชค เช›เซ‡, เชชเชฐเช‚เชคเซ เชตเชพเชชเชฐเชตเชพ เชฎเชพเชŸเซ‡ เชฎเซเชถเซเช•เซ‡เชฒ เช›เซ‡ เช…เชจเซ‡ เชฎเชซเชคเชฅเซ€ เชฆเซ‚เชฐ เช›เซ‡, เชชเชฐเช‚เชคเซ เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เชจเซเชถเชจเซเชธ เชธเชฒเชพเชฎเชคเซ€ เช…เชจเซ‡ เช‰เชชเชฏเซ‹เช—เชฎเชพเช‚ เชธเชฐเชณเชคเชพเชจเซเช‚ เชธเช‚เชชเซ‚เชฐเซเชฃ เชธเช‚เชฏเซ‹เชœเชจ เช›เซ‡, เช…เชจเซ‡ เช…เช‚เชคเชฟเชฎ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเช“ เชฎเชพเชŸเซ‡ เชธเช‚เชชเซ‚เชฐเซเชฃเชชเชฃเซ‡ เชฎเชซเชค เชชเชฃ เชนเซ‹เชˆ เชถเช•เซ‡ เช›เซ‡.

เช† เชฌเชงเชพเชจเซ‡ เชงเซเชฏเชพเชจเชฎเชพเช‚ เชฐเชพเช–เซ€เชจเซ‡, เช…เชฎเซ‡ เชตเซเชฏเชตเชนเชพเชฐเซ‹ เช…เชจเซ‡ เชนเชธเซเชคเชพเช•เซเชทเชฐ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชเช• เชธเชฐเชณ API เชชเซเชฐเชฆเชพเชจ เช•เชฐเซ€เชจเซ‡ เชตเชฟเช•เซ‡เชจเซเชฆเซเชฐเชฟเชค เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเชพ เชตเชฟเช•เชพเชธเชจเซ‡ เชธเชฐเชณ เชฌเชจเชพเชตเซ‡ เชคเซ‡เชตเซเช‚ เชธเซŒเชฅเซ€ เชธเซเชฐเช•เซเชทเชฟเชค เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฌเชจเชพเชตเชตเชพ เชฎเชพเช—เซ€เช เช›เซ€เช.
เช…เชฎเซ‡ เชคเชฎเชจเซ‡ เชจเซ€เชšเซ‡ เช† เช…เชจเซเชญเชต เชตเชฟเชถเซ‡ เชœเชฃเชพเชตเซ€เชถเซเช‚.

เชฒเซ‡เช–เชฎเชพเช‚ เช•เซ‹เชก เช‰เชฆเชพเชนเชฐเชฃเซ‹ เช…เชจเซ‡ เชธเซเช•เซเชฐเซ€เชจเชถเซ‰เชŸเซเชธ เชธเชพเชฅเซ‡, เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชฒเช–เชตเซเช‚ เชคเซ‡เชจเชพ เชชเชฐ เชชเช—เชฒเซเช‚-เชฆเชฐ-เชชเช—เชฒเชพเช‚ เชธเซ‚เชšเชจเซ‹ เชนเชถเซ‡. เชคเชฎเซ‡ เชฌเชงเชพ เช•เซ‹เชก เชถเซ‹เชงเซ€ เชถเช•เซ‹ เช›เซ‹ เชญเช‚เชกเชพเชฐ. เชฆเชฐเซ‡เช• เชชเซเชฐเชคเชฟเชฌเชฆเซเชงเชคเชพ เชคเชพเชฐเซเช•เชฟเช• เชฐเซ€เชคเซ‡ เช† เชฒเซ‡เช–เชจเชพ เชตเชฟเชญเชพเช—เชจเซ‡ เช…เชจเซเชฐเซ‚เชช เช›เซ‡.

เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เชจเซเชถเชจเซเชธเชจเซ‹ เชธเช‚เช•เซเชทเชฟเชชเซเชค เช‡เชคเชฟเชนเชพเชธ

เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเชพเช‚เชฌเชพ เชธเชฎเชฏเชฅเซ€ เช†เชธเชชเชพเชธ เช›เซ‡. เชคเซ‡เช“ 1999เชฎเชพเช‚ เชˆเชจเซเชŸเชฐเชจเซ‡เชŸ เชเช•เซเชธเชชเซเชฒเซ‹เชฐเชฐเชฎเชพเช‚, 2004เชฎเชพเช‚ เชซเชพเชฏเชฐเชซเซ‹เช•เซเชธเชฎเชพเช‚ เชฆเซ‡เช–เชพเชฏเชพ เชนเชคเชพ. เชœเซ‹ เช•เซ‡, เช–เซ‚เชฌ เชฒเชพเช‚เชฌเชพ เชธเชฎเชฏเชฅเซ€ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฎเชพเชŸเซ‡ เช•เซ‹เชˆ เชเช• เชงเซ‹เชฐเชฃ เชจเชนเซ‹เชคเซเช‚.

เช…เชฎเซ‡ เช•เชนเซ€ เชถเช•เซ€เช เช•เซ‡ เชคเซ‡ Google Chrome เชจเชพ เชšเซ‹เชฅเชพ เชธเช‚เชธเซเช•เชฐเชฃเชฎเชพเช‚ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเชพเชฅเซ‡ เชฆเซ‡เช–เชพเชฏเซ‹. เช…เชฒเชฌเชคเซเชค, เชคเซเชฏเชพเชฐเซ‡ เช•เซ‹เชˆ เชธเซเชชเชทเซเชŸเซ€เช•เชฐเชฃ เชจเชนเซ‹เชคเซเช‚, เชชเชฐเช‚เชคเซ เชคเซ‡ เช•เซเชฐเซ‹เชฎ API เชนเชคเซเช‚ เชœเซ‡ เชคเซ‡เชจเซ‹ เช†เชงเชพเชฐ เชฌเชจเซเชฏเซ‹: เชฎเซ‹เชŸเชพเชญเชพเช—เชจเชพ เชฌเซเชฐเชพเช‰เชเชฐ เชฎเชพเชฐเซเช•เซ‡เชŸ เชชเชฐ เชตเชฟเชœเชฏ เชฎเซ‡เชณเชตเซเชฏเซ‹ เช…เชจเซ‡ เชฌเชฟเชฒเซเชŸ-เช‡เชจ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชธเซเชŸเซ‹เชฐ เชนเซ‹เชตเชพเชจเซ‡ เช•เชพเชฐเชฃเซ‡, เช•เซเชฐเซ‹เชฎเซ‡ เชตเชพเชธเซเชคเชตเชฎเชพเช‚ เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฎเชพเชŸเซ‡ เชชเซเชฐเชฎเชพเชฃเชญเซ‚เชค เชธเซ‡เชŸ เช•เชฐเซเชฏเซเช‚.

เชฎเซ‹เชเชฟเชฒเชพเชจเซเช‚ เชชเซ‹เชคเชพเชจเซเช‚ เชธเซเชŸเชพเชจเซเชกเชฐเซเชก เชนเชคเซเช‚, เชชเชฐเช‚เชคเซ เช•เซเชฐเซ‹เชฎ เชเช•เซเชธเชŸเซ‡เชจเซเชถเชจเชจเซ€ เชฒเซ‹เช•เชชเซเชฐเชฟเชฏเชคเชพ เชœเซ‹เชˆเชจเซ‡ เช•เช‚เชชเชจเซ€เช เชธเซเชธเช‚เช—เชค API เชฌเชจเชพเชตเชตเชพเชจเซเช‚ เชจเช•เซเช•เซ€ เช•เชฐเซเชฏเซเช‚. 2015 เชฎเชพเช‚, เชฎเซ‹เชเชฟเชฒเชพเชจเซ€ เชชเชนเซ‡เชฒ เชชเชฐ, เช•เซเชฐเซ‹เชธ-เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชตเชฟเชถเชฟเชทเซเชŸเชคเชพเช“ เชชเชฐ เช•เชพเชฎ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชตเชฐเซเชฒเซเชก เชตเชพเช‡เชก เชตเซ‡เชฌ เช•เชจเซเชธเซ‹เชฐเซเชŸเชฟเชฏเชฎ (W3C) เชจเซ€ เช…เช‚เชฆเชฐ เชเช• เชตเชฟเชถเซ‡เชท เชœเซ‚เชฅ เชฌเชจเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เชนเชคเซเช‚.

Chrome เชฎเชพเชŸเซ‡ เชนเชพเชฒเชจเชพ API เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเชจเซ‡ เช†เชงเชพเชฐ เชคเชฐเซ€เช•เซ‡ เชฒเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเชพ เชนเชคเชพ. เชฎเชพเช‡เช•เซเชฐเซ‹เชธเซ‹เชซเซเชŸเชจเชพ เชธเชฎเชฐเซเชฅเชจ เชธเชพเชฅเซ‡ เช•เชพเชฐเซเชฏ เชนเชพเชฅ เชงเชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เชนเชคเซเช‚ (เช—เซ‚เช—เชฒเซ‡ เชงเซ‹เชฐเชฃเชจเชพ เชตเชฟเช•เชพเชธเชฎเชพเช‚ เชญเชพเช— เชฒเซ‡เชตเชพเชจเซ‹ เช‡เชจเช•เชพเชฐ เช•เชฐเซเชฏเซ‹ เชนเชคเซ‹), เช…เชจเซ‡ เชชเชฐเชฟเชฃเชพเชฎเซ‡ เชเช• เชกเซเชฐเชพเชซเซเชŸ เชฆเซ‡เช–เชพเชฏเซ‹. เชธเซเชชเชทเซเชŸเซ€เช•เชฐเชฃเซ‹.

เช”เชชเชšเชพเชฐเชฟเช• เชฐเซ€เชคเซ‡, เชธเซเชชเชทเซเชŸเซ€เช•เชฐเชฃ เชเชœ, เชซเชพเชฏเชฐเชซเซ‹เช•เซเชธ เช…เชจเซ‡ เช“เชชเซ‡เชฐเชพ เชฆเซเชตเชพเชฐเชพ เชธเชฎเชฐเซเชฅเชฟเชค เช›เซ‡ (เชจเซ‹เช‚เชง เช•เชฐเซ‹ เช•เซ‡ เช•เซเชฐเซ‹เชฎ เช† เชธเซ‚เชšเชฟเชฎเชพเช‚ เชจเชฅเซ€). เชชเชฐเช‚เชคเซ เชนเช•เซ€เช•เชคเชฎเชพเช‚, เชชเซเชฐเชฎเชพเชฃเชญเซ‚เชค เชฎเซ‹เชŸเชพเชญเชพเช—เซ‡ เช•เซเชฐเซ‹เชฎ เชธเชพเชฅเซ‡ เชธเซเชธเช‚เช—เชค เช›เซ‡, เช•เชพเชฐเชฃ เช•เซ‡ เชคเซ‡ เชตเชพเชธเซเชคเชตเชฎเชพเช‚ เชคเซ‡เชจเชพ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเชจเชพ เช†เชงเชพเชฐเซ‡ เชฒเช–เชพเชฏเซ‡เชฒเซเช‚ เช›เซ‡. เชคเชฎเซ‡ WebExtensions API เชตเชฟเชถเซ‡ เชตเชงเซ เชตเชพเช‚เชšเซ€ เชถเช•เซ‹ เช›เซ‹ เช…เชนเซ€เช‚.

เชตเชฟเชธเซเชคเชฐเชฃ เชฎเชพเชณเช–เซเช‚

เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฎเชพเชŸเซ‡ เชœเชฐเซ‚เชฐเซ€ เช›เซ‡ เชคเซ‡ เชเช•เชฎเชพเชคเซเชฐ เชซเชพเช‡เชฒ เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸ เช›เซ‡ (manifest.json). เชคเซ‡ เชตเชฟเชธเซเชคเชฐเชฃ เชฎเชพเชŸเซ‡ "เชชเซเชฐเชตเซ‡เชถ เชฌเชฟเช‚เชฆเซ" เชชเชฃ เช›เซ‡.

เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸ

เชธเซเชชเชทเซเชŸเซ€เช•เชฐเชฃ เชฎเซเชœเชฌ, เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸ เชซเชพเช‡เชฒ เชฎเชพเชจเซเชฏ JSON เชซเชพเช‡เชฒ เช›เซ‡. เช•เชฏเชพ เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚ เชœเซ‹เชˆ เชถเช•เชพเชฏ เช›เซ‡ เชคเซ‡เชฎเชพเช‚ เช•เชˆ เช•เซ€เช เชธเชชเซ‹เชฐเซเชŸเซ‡เชก เช›เซ‡ เชคเซ‡เชจเซ€ เชฎเชพเชนเชฟเชคเซ€ เชธเชพเชฅเซ‡ เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸ เช•เซ€เชจเซเช‚ เชธเช‚เชชเซ‚เชฐเซเชฃ เชตเชฐเซเชฃเชจ เช…เชนเซ€เช‚.

เชธเซเชชเชทเซเชŸเซ€เช•เชฐเชฃเชฎเชพเช‚ เชจ เชนเซ‹เชฏ เชคเซ‡เชตเซ€ เช•เซ€เชจเซ‡ เช…เชตเช—เชฃเชตเชพเชฎเชพเช‚ เช†เชตเซ€ เชถเช•เซ‡ เช›เซ‡ (Chrome เช…เชจเซ‡ Firefox เชฌเช‚เชจเซ‡ เชญเซ‚เชฒเซ‹เชจเซ€ เชœเชพเชฃ เช•เชฐเซ‡ เช›เซ‡, เชชเชฐเช‚เชคเซ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช•เชพเชฎ เช•เชฐเชตเชพเชจเซเช‚ เชšเชพเชฒเซ เชฐเชพเช–เซ‡ เช›เซ‡).

เช…เชจเซ‡ เชนเซเช‚ เช•เซ‡เชŸเชฒเชพเช• เชฎเซเชฆเซเชฆเชพเช“ เชชเชฐ เชงเซเชฏเชพเชจ เชฆเซ‹เชฐเชตเชพ เชฎเชพเช‚เช—เซ เช›เซเช‚.

  1. เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ โ€” เชเช• เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชœเซ‡เชฎเชพเช‚ เชจเซ€เชšเซ‡เชจเชพ เช•เซเชทเซ‡เชคเซเชฐเซ‹ เชถเชพเชฎเซ‡เชฒ เช›เซ‡:
    1. เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธ โ€” เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธเชจเซ€ เชถเซเชฐเซ‡เชฃเซ€ เช•เซ‡ เชœเซ‡ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชธเช‚เชฆเชฐเซเชญเชฎเชพเช‚ เชšเชฒเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡ (เช†เชชเชฃเซ‡ เช† เชตเชฟเชถเซ‡ เชฅเซ‹เชกเซ€ เชตเชพเชฐ เชชเช›เซ€ เชตเชพเชค เช•เชฐเซ€เชถเซเช‚);
    2. เชชเซƒเชทเซเช  - เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธเชจเซ‡ เชฌเชฆเชฒเซ‡ เช•เซ‡ เชœเซ‡ เช–เชพเชฒเซ€ เชชเซƒเชทเซเช เชฎเชพเช‚ เชšเชฒเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡, เชคเชฎเซ‡ เชธเชพเชฎเช—เซเชฐเซ€ เชธเชพเชฅเซ‡ html เชจเซ‹ เช‰เชฒเซเชฒเซ‡เช– เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹. เช† เช•เชฟเชธเซเชธเชพเชฎเชพเช‚, เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชซเซ€เชฒเซเชกเชจเซ‡ เช…เชตเช—เชฃเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡, เช…เชจเซ‡ เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธเชจเซ‡ เชธเชพเชฎเช—เซเชฐเซ€ เชชเซƒเชทเซเช เชฎเชพเช‚ เชฆเชพเช–เชฒ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡;
    3. เชšเชพเชฒเซ โ€” เชฆเซเชตเชฟเชธเช‚เช—เซ€ เชงเซเชตเชœ, เชœเซ‹ เช‰เชฒเซเชฒเซ‡เช–เชฟเชค เชจ เชนเซ‹เชฏ, เชคเซ‹ เชฌเซเชฐเชพเช‰เชเชฐ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพเชจเซ‡ "เชฎเชพเชฐเซ€ เชจเชพเช–เชถเซ‡" เชœเซเชฏเชพเชฐเซ‡ เชคเซ‡ เชฎเชพเชจเซ‡ เช›เซ‡ เช•เซ‡ เชคเซ‡ เช•เช‚เชˆ เช•เชฐเซ€ เชฐเชนเซเชฏเซเช‚ เชจเชฅเซ€, เช…เชจเซ‡ เชœเซ‹ เชœเชฐเซ‚เชฐเซ€ เชนเซ‹เชฏ เชคเซ‹ เชคเซ‡เชจเซ‡ เชชเซเชจเชƒเชชเซเชฐเชพเชฐเช‚เชญ เช•เชฐเชถเซ‡. เชจเชนเชฟเช‚เชคเชฐ, เชœเซเชฏเชพเชฐเซ‡ เชฌเซเชฐเชพเช‰เชเชฐ เชฌเช‚เชง เชนเซ‹เชฏ เชคเซเชฏเชพเชฐเซ‡ เชœ เชชเซƒเชทเซเช เชจเซ‡ เช…เชจเชฒเซ‹เชก เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡. เชซเชพเชฏเชฐเชซเซ‹เช•เซเชธเชฎเชพเช‚ เชธเชชเซ‹เชฐเซเชŸเซ‡เชก เชจเชฅเซ€.
  2. เชธเชพเชฎเช—เซเชฐเซ€_เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธ โ€” เช‘เชฌเซเชœเซ‡เช•เซเชŸเซเชธเชจเซ€ เชเชฐเซ‡ เชœเซ‡ เชคเชฎเชจเซ‡ เชตเชฟเชตเชฟเชง เชตเซ‡เชฌ เชชเซƒเชทเซเช เซ‹ เชชเชฐ เชตเชฟเชตเชฟเชง เชธเซเช•เซเชฐเชฟเชชเซเชŸเซ‹ เชฒเซ‹เชก เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡. เชฆเชฐเซ‡เช• เช‘เชฌเซเชœเซ‡เช•เซเชŸเชฎเชพเช‚ เชจเซ€เชšเซ‡เชจเชพ เชฎเชนเชคเซเชตเชชเซ‚เชฐเซเชฃ เช•เซเชทเซ‡เชคเซเชฐเซ‹ เชถเชพเชฎเซ‡เชฒ เช›เซ‡:
    1. เชฎเซ‡เชšเซ‹ - เชชเซ‡เชŸเชฐเซเชจ url, เชœเซ‡ เชจเช•เซเช•เซ€ เช•เชฐเซ‡ เช›เซ‡ เช•เซ‡ เช•เซ‹เชˆ เชšเซ‹เช•เซเช•เชธ เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸเชจเซ‹ เชธเชฎเชพเชตเซ‡เชถ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡ เช•เซ‡ เชจเชนเซ€เช‚.
    2. js โ€” เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธเชจเซ€ เชธเซ‚เชšเชฟ เช•เซ‡ เชœเซ‡ เช† เชฎเซ‡เชšเชฎเชพเช‚ เชฒเซ‹เชก เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡;
    3. เชฌเชพเช•เชพเชค_เชฎเซ‡เชš - เช•เซเชทเซ‡เชคเซเชฐเชฎเชพเช‚เชฅเซ€ เชฌเชพเช•เชพเชค match เช† เชซเซ€เชฒเซเชก เชธเชพเชฅเซ‡ เชฎเซ‡เชณ เช–เชพเชคเชพ URL.
  3. เชชเซƒเชทเซเช _เช•เซเชฐเชฟเชฏเชพ - เชตเชพเชธเซเชคเชตเชฎเชพเช‚ เชเช• เช‘เชฌเซเชœเซ‡เช•เซเชŸ เช›เซ‡ เชœเซ‡ เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚ เชเชกเซเชฐเซ‡เชธ เชฌเชพเชฐเชจเซ€ เชฌเชพเชœเซเชฎเชพเช‚ เชชเซเชฐเชฆเชฐเซเชถเชฟเชค เชฅเชคเชพ เช†เช‡เช•เชจ เช…เชจเซ‡ เชคเซ‡เชจเซ€ เชธเชพเชฅเซ‡ เช•เซเชฐเชฟเชฏเชพเชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เชฎเชพเชŸเซ‡ เชœเชตเชพเชฌเชฆเชพเชฐ เช›เซ‡. เชคเซ‡ เชคเชฎเชจเซ‡ เชชเซ‹เชชเช…เชช เชตเชฟเชจเซเชกเซ‹ เชฆเชฐเซเชถเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เชชเชฃ เชชเชฐเชตเชพเชจเช—เซ€ เช†เชชเซ‡ เช›เซ‡, เชœเซ‡ เชคเชฎเชพเชฐเชพ เชชเซ‹เชคเชพเชจเชพ HTML, CSS เช…เชจเซ‡ JS เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡.
    1. เชกเชฟเชซเซ‹เชฒเซเชŸ_เชชเซ‹เชชเช…เชช - เชชเซ‹เชชเช…เชช เชˆเชจเซเชŸเชฐเชซเซ‡เชธ เชธเชพเชฅเซ‡ HTML เชซเชพเชˆเชฒเชจเซ‹ เชชเชพเชฅ, CSS เช…เชจเซ‡ JS เชธเชฎเชพเชตเซ€ เชถเช•เซ‡ เช›เซ‡.
  4. เชชเชฐเชตเชพเชจเช—เซ€เช“ - เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช…เชงเชฟเช•เชพเชฐเซ‹เชจเซเช‚ เชธเช‚เชšเชพเชฒเชจ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชเช• เชเชฐเซ‡. เชคเซเชฏเชพเช‚ 3 เชชเซเชฐเช•เชพเชฐเชจเชพ เช…เชงเชฟเช•เชพเชฐเซ‹ เช›เซ‡, เชœเซ‡เชจเซเช‚ เชตเชฟเช—เชคเชตเชพเชฐ เชตเชฐเซเชฃเชจ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เช›เซ‡ เช…เชนเซ€เช‚
  5. เชตเซ‡เชฌ_เชเช•เซเชธเซ‡เชธเชฟเชฌเชฒ_เชธเช‚เชธเชพเชงเชจเซ‹ โ€” เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเช‚เชธเชพเชงเชจเซ‹ เช•เซ‡ เชœเซ‡ เชตเซ‡เชฌ เชชเซƒเชทเซเช  เชตเชฟเชจเช‚เชคเซ€ เช•เชฐเซ€ เชถเช•เซ‡ เช›เซ‡, เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เช›เชฌเซ€เช“, JS, CSS, HTML เชซเชพเช‡เชฒเซ‹.
  6. เชฌเชพเชนเซเชฏ เชฐเซ€เชคเซ‡_เช•เชจเซ‡เช•เซเชŸเซ‡เชฌเชฒ โ€” เช…เชนเซ€เช‚ เชคเชฎเซ‡ เช…เชจเซเชฏ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช…เชจเซ‡ เชตเซ‡เชฌ เชชเซ‡เชœเชจเชพ เชกเซ‹เชฎเซ‡เชจเซเชธ เช•เซ‡ เชœเซ‡เชฎเชพเช‚เชฅเซ€ เชคเชฎเซ‡ เช•เชจเซ‡เช•เซเชŸ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹ เชคเซ‡เชจเชพ ID เชจเซ‡ เชธเซเชชเชทเซเชŸเชชเชฃเซ‡ เชธเซเชชเชทเซเชŸ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹. เชกเซ‹เชฎเซ‡เชจ เชฌเซ€เชœเชพ เชธเซเชคเชฐ เช…เชฅเชตเชพ เช‰เชšเซเชš เชนเซ‹เชˆ เชถเช•เซ‡ เช›เซ‡. เชซเชพเชฏเชฐเชซเซ‹เช•เซเชธเชฎเชพเช‚ เช•เชพเชฎ เช•เชฐเชคเซเช‚ เชจเชฅเซ€.

เชเช•เซเชเซ‡เช•เซเชฏเซเชถเชจ เชธเช‚เชฆเชฐเซเชญ

เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเชฎเชพเช‚ เชคเซเชฐเชฃ เช•เซ‹เชก เชเช•เซเชเซ‡เช•เซเชฏเซเชถเชจ เชธเช‚เชฆเชฐเซเชญเซ‹ เช›เซ‡, เชเชŸเชฒเซ‡ เช•เซ‡, เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชฎเชพเช‚ เชฌเซเชฐเชพเช‰เชเชฐ API เชจเซ€ เชเช•เซเชธเซ‡เชธเชจเชพ เชตเชฟเชตเชฟเชง เชธเซเชคเชฐเซ‹ เชธเชพเชฅเซ‡ เชคเซเชฐเชฃ เชญเชพเช—เซ‹เชจเซ‹ เชธเชฎเชพเชตเซ‡เชถ เชฅเชพเชฏ เช›เซ‡.

เชตเชฟเชธเซเชคเชฐเชฃ เชธเช‚เชฆเชฐเซเชญ

เชฎเซ‹เชŸเชพเชญเชพเช—เชจเชพ API เช…เชนเซ€เช‚ เช‰เชชเชฒเชฌเซเชง เช›เซ‡. เช† เชธเช‚เชฆเชฐเซเชญเชฎเชพเช‚ เชคเซ‡เช“ "เชœเซ€เชตเช‚เชค" เช›เซ‡:

  1. เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชชเซƒเชทเซเช  - เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเชจเซ‹ "เชฌเซ‡เช•เชเชจเซเชก" เชญเชพเช—. เชซเชพเช‡เชฒ "เชฌเซ‡เช•เช—เซเชฐเชพเช‰เชจเซเชก" เช•เซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸเชฎเชพเช‚ เช‰เชฒเซเชฒเซ‡เช–เชฟเชค เช›เซ‡.
  2. เชชเซ‹เชชเช…เชช เชชเซƒเชทเซเช  - เชœเซเชฏเชพเชฐเซ‡ เชคเชฎเซ‡ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช†เชฏเช•เชจ เชชเชฐ เช•เซเชฒเชฟเช• เช•เชฐเซ‹ เช›เซ‹ เชคเซเชฏเชพเชฐเซ‡ เชเช• เชชเซ‹เชชเช…เชช เชชเซ‡เชœ เชฆเซ‡เช–เชพเชฏ เช›เซ‡. เชœเชพเชนเซ‡เชฐเชจเชพเชฎเชพเชฎเชพเช‚ browser_action -> default_popup.
  3. เช•เชธเซเชŸเชฎ เชชเซƒเชทเซเช  - เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชชเซƒเชทเซเช , เชฆเซƒเชถเซเชฏเชจเชพ เช…เชฒเช— เชŸเซ‡เชฌเชฎเชพเช‚ "เชœเซ€เชตเช‚เชค". chrome-extension://<id_ั€ะฐััˆะธั€ะตะฝะธั>/customPage.html.

เช† เชธเช‚เชฆเชฐเซเชญ เชฌเซเชฐเชพเช‰เชเชฐ เชตเชฟเชจเซเชกเซ‹ เช…เชจเซ‡ เชŸเซ…เชฌเซเชธเชฅเซ€ เชธเซเชตเชคเช‚เชคเซเชฐ เชฐเซ€เชคเซ‡ เช…เชธเซเชคเชฟเชคเซเชตเชฎเชพเช‚ เช›เซ‡. เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชชเซƒเชทเซเช  เชเช• เชจเช•เชฒเชฎเชพเช‚ เช…เชธเซเชคเชฟเชคเซเชตเชฎเชพเช‚ เช›เซ‡ เช…เชจเซ‡ เชนเช‚เชฎเซ‡เชถเชพ เช•เชพเชฐเซเชฏ เช•เชฐเซ‡ เช›เซ‡ (เช…เชชเชตเชพเชฆ เช เช‡เชตเซ‡เชจเซเชŸ เชชเซƒเชทเซเช  เช›เซ‡, เชœเซเชฏเชพเชฐเซ‡ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เช‡เชตเซ‡เชจเซเชŸ เชฆเซเชตเชพเชฐเชพ เชฒเซ‹เชจเซเชš เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ เช…เชจเซ‡ เชคเซ‡เชจเชพ เช…เชฎเชฒ เชชเช›เซ€ "เชฎเซƒเชคเซเชฏเซ เชชเชพเชฎเซ‡ เช›เซ‡). เชชเซ‹เชชเช…เชช เชชเซƒเชทเซเช  เชœเซเชฏเชพเชฐเซ‡ เชชเซ‹เชชเช…เชช เชตเชฟเชจเซเชกเซ‹ เช–เซเชฒเซเชฒเซ€ เชนเซ‹เชฏ เชคเซเชฏเชพเชฐเซ‡ เช…เชธเซเชคเชฟเชคเซเชตเชฎเชพเช‚ เช›เซ‡, เช…เชจเซ‡ เช•เชธเซเชŸเชฎ เชชเซƒเชทเซเช  โ€” เชœเซเชฏเชพเชฐเซ‡ เชคเซ‡เชจเซ€ เชธเชพเชฅเซ‡เชจเซ€ เชŸเซ‡เชฌ เช–เซเชฒเซเชฒเซ€ เชนเซ‹เชฏ. เช† เชธเช‚เชฆเชฐเซเชญเชฎเชพเช‚เชฅเซ€ เช…เชจเซเชฏ เชŸเซ‡เชฌเซเชธ เช…เชจเซ‡ เชคเซ‡เชฎเชจเชพ เชธเชฎเชพเชตเชฟเชทเซเชŸเซ‹เชจเซ€ เช•เซ‹เชˆ เชเช•เซเชธเซ‡เชธ เชจเชฅเซ€.

เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชธเช‚เชฆเชฐเซเชญ

เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชซเชพเช‡เชฒ เชฆเชฐเซ‡เช• เชฌเซเชฐเชพเช‰เชเชฐ เชŸเซ‡เชฌ เชธเชพเชฅเซ‡ เชฒเซ‹เชจเซเชš เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชคเซ‡เชจเซ€ เชชเชพเชธเซ‡ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเชจเชพ API เชจเชพ เชญเชพเช— เช…เชจเซ‡ เชตเซ‡เชฌ เชชเซƒเชทเซเช เชจเชพ DOM เชŸเซเชฐเซ€เชจเซ€ เชเช•เซเชธเซ‡เชธ เช›เซ‡. เชคเซ‡ เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸเซ‹ เช›เซ‡ เชœเซ‡ เชชเซƒเชทเซเช  เชธเชพเชฅเซ‡ เช•เซเชฐเชฟเชฏเชพเชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เชฎเชพเชŸเซ‡ เชœเชตเชพเชฌเชฆเชพเชฐ เช›เซ‡. เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเซเชธ เช•เซ‡ เชœเซ‡ DOM เชŸเซเชฐเซ€เชจเซ‡ เชฎเซ‡เชจเชฟเชชเซเชฏเซเชฒเซ‡เชŸ เช•เชฐเซ‡ เช›เซ‡ เชคเซ‡ เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸเซเชธเชฎเชพเช‚ เช† เช•เชฐเซ‡ เช›เซ‡ - เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เชเชก เชฌเซเชฒเซ‹เช•เชฐเซเชธ เช…เชฅเชตเชพ เช…เชจเซเชตเชพเชฆเช•เซ‹. เช‰เชชเชฐเชพเช‚เชค, เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชชเซเชฐเชฎเชพเชฃเชญเซ‚เชค เชฆเซเชตเชพเชฐเชพ เชชเซƒเชทเซเช  เชธเชพเชฅเซ‡ เชตเชพเชคเชšเซ€เชค เช•เชฐเซ€ เชถเช•เซ‡ เช›เซ‡ postMessage.

เชตเซ‡เชฌ เชชเซƒเชทเซเช  เชธเช‚เชฆเชฐเซเชญ

เช† เชชเซ‹เชคเซ‡ เชœ เชตเชพเชธเซเชคเชตเชฟเช• เชตเซ‡เชฌ เชชเซ‡เชœ เช›เซ‡. เชคเซ‡เชจเซ‡ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเชพเชฅเซ‡ เช•เซ‹เชˆ เชฒเซ‡เชตเชพเชฆเซ‡เชตเชพ เชจเชฅเซ€ เช…เชจเซ‡ เชคเซ‡เชจเซ€ เชชเชพเชธเซ‡ เชคเซเชฏเชพเช‚ เชเช•เซเชธเซ‡เชธ เชจเชฅเซ€, เชธเชฟเชตเชพเชฏ เช•เซ‡ เช† เชชเซƒเชทเซเช เชจเซเช‚ เชกเซ‹เชฎเซ‡เชจ เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸเชฎเชพเช‚ เชธเซเชชเชทเซเชŸเชชเชฃเซ‡ เชฆเชฐเซเชถเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เชจเชฅเซ€ (เชจเซ€เชšเซ‡ เช†เชจเชพ เชชเชฐ เชตเชงเซ).

เชธเช‚เชฆเซ‡เชถ เชตเชฟเชจเชฟเชฎเชฏ

เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเชพ เชตเชฟเชตเชฟเชง เชญเชพเช—เซ‹เช เชเช•เชฌเซ€เชœเชพ เชธเชพเชฅเซ‡ เชธเช‚เชฆเซ‡เชถเชพเช“เชจเซ€ เช†เชชเชฒเซ‡ เช•เชฐเชตเซ€ เช†เชตเชถเซเชฏเช• เช›เซ‡. เช† เชฎเชพเชŸเซ‡ เชเช• API เช›เซ‡ runtime.sendMessage เชธเช‚เชฆเซ‡เชถ เชฎเซ‹เช•เชฒเชตเชพ เชฎเชพเชŸเซ‡ background ะธ tabs.sendMessage เชชเซƒเชทเซเช  เชชเชฐ เชธเช‚เชฆเซ‡เชถ เชฎเซ‹เช•เชฒเชตเชพ เชฎเชพเชŸเซ‡ (เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸ, เชชเซ‹เชชเช…เชช เช…เชฅเชตเชพ เชตเซ‡เชฌ เชชเซƒเชทเซเช  เชœเซ‹ เช‰เชชเชฒเชฌเซเชง เชนเซ‹เชฏ เชคเซ‹ externally_connectable). Chrome API เชจเซ‡ เชเช•เซเชธเซ‡เชธ เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ เชจเซ€เชšเซ‡ เชเช• เช‰เชฆเชพเชนเชฐเชฃ เช›เซ‡.

// ะกะพะพะฑั‰ะตะฝะธะตะผ ะผะพะถะตั‚ ะฑั‹ั‚ัŒ ะปัŽะฑะพะน JSON ัะตั€ะธะฐะปะธะทัƒะตะผั‹ะน ะพะฑัŠะตะบั‚
const msg = {a: 'foo', b: 'bar'};

// extensionId ะผะพะถะฝะพ ะฝะต ัƒะบะฐะทั‹ะฒะฐั‚ัŒ, ะตัะปะธ ะผั‹ ั…ะพั‚ะธะผ ะฟะพัะปะฐั‚ัŒ ัะพะพะฑั‰ะตะฝะธะต 'ัะฒะพะตะผัƒ' ั€ะฐััˆะธั€ะตะฝะธัŽ (ะธะท ui ะธะปะธ ะบะพะฝั‚ะตะฝั‚ ัะบั€ะธะฟั‚ะฐ)
chrome.runtime.sendMessage(extensionId, msg);

// ะขะฐะบ ะฒั‹ะณะปัะดะธั‚ ะพะฑั€ะฐะฑะพั‚ั‡ะธะบ
chrome.runtime.onMessage.addListener((msg) => console.log(msg))

// ะœะพะถะฝะพ ัะปะฐั‚ัŒ ัะพะพะฑั‰ะตะฝะธั ะฒะบะปะฐะดะบะฐะผ ะทะฝะฐั ะธั… id
chrome.tabs.sendMessage(tabId, msg)

// ะŸะพะปัƒั‡ะธั‚ัŒ ะบ ะฒะบะปะฐะดะบะฐะผ ะธ ะธั… id ะผะพะถะฝะพ, ะฝะฐะฟั€ะธะผะตั€, ะฒะพั‚ ั‚ะฐะบ
chrome.tabs.query(
    {currentWindow: true, active : true},
    function(tabArray){
      tabArray.forEach(tab => console.log(tab.id))
    }
)

เชธเช‚เชชเซ‚เชฐเซเชฃ เชธเช‚เชšเชพเชฐ เชฎเชพเชŸเซ‡, เชคเชฎเซ‡ เชฎเชพเชฐเชซเชคเซ‡ เชœเซ‹เชกเชพเชฃเซ‹ เชฌเชจเชพเชตเซ€ เชถเช•เซ‹ เช›เซ‹ runtime.connect. เชœเชตเชพเชฌเชฎเชพเช‚ เช…เชฎเซ‡ เชชเซเชฐเชพเชชเซเชค เช•เชฐเซ€เชถเซเช‚ runtime.Port, เชœเซ‡เชจเชพ เชชเชฐ, เชœเซเชฏเชพเชฐเซ‡ เชคเซ‡ เช–เซเชฒเซเชฒเซเช‚ เชนเซ‹เชฏ, เชคเซเชฏเชพเชฐเซ‡ เชคเชฎเซ‡ เช—เชฎเซ‡ เชคเซ‡เชŸเชฒเชพ เชธเช‚เชฆเซ‡เชถเชพเช“ เชฎเซ‹เช•เชฒเซ€ เชถเช•เซ‹ เช›เซ‹. เช•เซเชฒเชพเชฏเช‚เชŸ เชฌเชพเชœเซ เชชเชฐ, เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, contentscript, เชคเซ‡ เช†เชจเชพ เชœเซ‡เชตเซเช‚ เชฆเซ‡เช–เชพเชฏ เช›เซ‡:

// ะžะฟัั‚ัŒ ะถะต extensionId ะผะพะถะฝะพ ะฝะต ัƒะบะฐะทั‹ะฒะฐั‚ัŒ ะฟั€ะธ ะบะพะผะผัƒะฝะธะบะฐั†ะธะธ ะฒะฝัƒั‚ั€ะธ ะพะดะฝะพะณะพ ั€ะฐััˆะธั€ะตะฝะธั. ะŸะพะดะบะปัŽั‡ะตะฝะธะต ะผะพะถะฝะพ ะธะผะตะฝะพะฒะฐั‚ัŒ
const port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
    if (msg.question === "Who's there?")
        port.postMessage({answer: "Madame"});
    else if (msg.question === "Madame who?")
        port.postMessage({answer: "Madame... Bovary"});

เชธเชฐเซเชตเชฐ เช…เชฅเชตเชพ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ:

// ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ะดะปั ะฟะพะดะบะปัŽั‡ะตะฝะธั 'ัะฒะพะธั…' ะฒะบะปะฐะดะพะบ. ะšะพะฝั‚ะตะฝั‚ ัะบั€ะธะฟั‚ะพะฒ, popup ะธะปะธ ัั‚ั€ะฐะฝะธั† ั€ะฐััˆะธั€ะตะฝะธั
chrome.runtime.onConnect.addListener(function(port) {
    console.assert(port.name === "knockknock");
    port.onMessage.addListener(function(msg) {
        if (msg.joke === "Knock knock")
            port.postMessage({question: "Who's there?"});
        else if (msg.answer === "Madame")
            port.postMessage({question: "Madame who?"});
        else if (msg.answer === "Madame... Bovary")
            port.postMessage({question: "I don't get it."});
    });
});

// ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ะดะปั ะฟะพะดะบะปัŽั‡ะตะฝะธั ะฒะฝะตัˆะฝะธั… ะฒะบะปะฐะดะพะบ. ะ”ั€ัƒะณะธั… ั€ะฐััˆะธั€ะตะฝะธะน ะธะปะธ ะฒะตะฑ ัั‚ั€ะฐะฝะธั†, ะบะพั‚ะพั€ั‹ะผ ั€ะฐะทั€ะตัˆะตะฝ ะดะพัั‚ัƒะฟ ะฒ ะผะฐะฝะธั„ะตัั‚ะต
chrome.runtime.onConnectExternal.addListener(function(port) {
    ...
});

เชเช• เชชเซเชฐเชธเช‚เช— เชชเชฃ เช›เซ‡ onDisconnect เช…เชจเซ‡ เชชเชฆเซเชงเชคเชฟ disconnect.

เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชกเชพเชฏเชพเช—เซเชฐเชพเชฎ

เชšเชพเชฒเซ‹ เชเช• เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฌเชจเชพเชตเซ€เช เชœเซ‡ เช–เชพเชจเช—เซ€ เช•เซ€เชจเซ‡ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเซ‡ เช›เซ‡, เชธเชพเชฐเซเชตเชœเชจเชฟเช• เชฎเชพเชนเชฟเชคเซ€เชจเซ€ เชเช•เซเชธเซ‡เชธ เชชเซเชฐเชฆเชพเชจ เช•เชฐเซ‡ เช›เซ‡ (เชธเชฐเชจเชพเชฎเซเช‚, เชธเชพเชฐเซเชตเชœเชจเชฟเช• เช•เซ€ เชชเซƒเชทเซเช  เชธเชพเชฅเซ‡ เชตเชพเชคเชšเซ€เชค เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชคเซƒเชคเซ€เชฏ-เชชเช•เซเชท เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเซ‹เชจเซ‡ เชตเซเชฏเชตเชนเชพเชฐเซ‹ เชฎเชพเชŸเซ‡ เชธเชนเซ€ เชฎเชพเชŸเซ‡ เชตเชฟเชจเช‚เชคเซ€ เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡.

เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชตเชฟเช•เชพเชธ

เช…เชฎเชพเชฐเซ€ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชฌเช‚เชจเซ‡เช เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพ เชธเชพเชฅเซ‡ เช•เซเชฐเชฟเชฏเชพเชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เช•เชฐเชตเซ€ เชœเซ‹เชˆเช เช…เชจเซ‡ เช•เซ‰เชฒ เชชเชฆเซเชงเชคเชฟเช“ (เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เชตเซเชฏเชตเชนเชพเชฐเซ‹ เชชเชฐ เชนเชธเซเชคเชพเช•เซเชทเชฐ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡) เชฎเชพเชŸเซ‡ API เชธเชพเชฅเซ‡ เชชเซƒเชทเซเช  เชชเซเชฐเชฆเชพเชจ เช•เชฐเชตเซเช‚ เชœเซ‹เชˆเช. เชฎเชพเชคเซเชฐ เชเช• เชธเชพเชฅเซ‡ เช•เชฐเซ‹ contentscript เช•เชพเชฎ เช•เชฐเชถเซ‡ เชจเชนเซ€เช‚, เช•เชพเชฐเชฃ เช•เซ‡ เชคเซ‡เชจเซ€ เชชเชพเชธเซ‡ เชซเช•เซเชค DOM เชจเซ‹ เชœ เชเช•เซเชธเซ‡เชธ เช›เซ‡, เชชเชฐเช‚เชคเซ เชชเซ‡เชœเชจเชพ JS เชฎเชพเชŸเซ‡ เชจเชนเซ€เช‚. เชฆเซเชตเชพเชฐเชพ เช•เชจเซ‡เช•เซเชŸ เช•เชฐเซ‹ runtime.connect เช…เชฎเซ‡ เช•เชฐเซ€ เชถเช•เชคเชพ เชจเชฅเซ€, เช•เชพเชฐเชฃ เช•เซ‡ API เชฌเชงเชพ เชกเซ‹เชฎเซ‡เชจเซเชธ เชชเชฐ เชœเชฐเซ‚เชฐเซ€ เช›เซ‡ เช…เชจเซ‡ เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸเชฎเชพเช‚ เชฎเชพเชคเซเชฐ เชšเซ‹เช•เซเช•เชธ เชœ เช‰เชฒเซเชฒเซ‡เช–เชฟเชค เช•เชฐเซ€ เชถเช•เชพเชฏ เช›เซ‡. เชชเชฐเชฟเชฃเชพเชฎเซ‡, เชกเชพเชฏเชพเช—เซเชฐเชพเชฎ เช†เชจเชพ เชœเซ‡เชตเซ‹ เชฆเซ‡เช–เชพเชถเซ‡:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชฌเซ€เชœเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชนเชถเซ‡ - inpage, เชœเซ‡ เช…เชฎเซ‡ เชชเซƒเชทเซเช เชฎเชพเช‚ เชฆเชพเช–เชฒ เช•เชฐเซ€เชถเซเช‚. เชคเซ‡ เชคเซ‡เชจเชพ เชธเช‚เชฆเชฐเซเชญเชฎเชพเช‚ เชšเชพเชฒเชถเซ‡ เช…เชจเซ‡ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ API เชชเซเชฐเชฆเชพเชจ เช•เชฐเชถเซ‡.

เชถเชฐเซ‚เช†เชค

เชฌเชงเชพ เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช•เซ‹เชก เชชเชฐ เช‰เชชเชฒเชฌเซเชง เช›เซ‡ GitHub. เชตเชฐเซเชฃเชจ เชฆเชฐเชฎเชฟเชฏเชพเชจ เช•เชฎเชฟเชŸเซเชธเชจเซ€ เชฒเชฟเช‚เช•เซเชธ เชนเชถเซ‡.

เชšเชพเชฒเซ‹ เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸเซ‹ เชธเชพเชฅเซ‡ เชชเซเชฐเชพเชฐเช‚เชญ เช•เชฐเซ€เช:

{
  // ะ˜ะผั ะธ ะพะฟะธัะฐะฝะธะต, ะฒะตั€ัะธั. ะ’ัะต ัั‚ะพ ะฑัƒะดะตั‚ ะฒะธะดะฝะพ ะฒ ะฑั€ะฐัƒะทะตั€ะต ะฒ chrome://extensions/?id=<id ั€ะฐััˆะธั€ะตะฝะธั>
  "name": "Signer",
  "description": "Extension demo",
  "version": "0.0.1",
  "manifest_version": 2,

  // ะกะบั€ะธะฟั‚ั‹, ะบะพั‚ะพั€ั‹ะต ะฑัƒะดัƒั‚ ะธัะฟะพะปะฝัั‚ัั ะฒ background, ะธั… ะผะพะถะตั‚ ะฑั‹ั‚ัŒ ะฝะตัะบะพะปัŒะบะพ
  "background": {
    "scripts": ["background.js"]
  },

  // ะšะฐะบะพะน html ะธัะฟะพะปัŒะทะพะฒะฐั‚ัŒ ะดะปั popup
  "browser_action": {
    "default_title": "My Extension",
    "default_popup": "popup.html"
  },

  // ะšะพะฝั‚ะตะฝั‚ ัะบั€ะธะฟั‚ั‹.
  // ะฃ ะฝะฐั ะพะดะธะฝ ะพะฑัŠะตะบั‚: ะดะปั ะฒัะตั… url ะฝะฐั‡ะธะฝะฐัŽั‰ะธั…ัั ั http ะธะปะธ https ะผั‹ ะทะฐะฟัƒัะบะฐะตะผ
  // contenscript context ัะพ ัะบั€ะธะฟั‚ะพะผ contentscript.js. ะ—ะฐะฟัƒัะบะฐั‚ัŒ ัั€ะฐะทัƒ ะฟะพ ะฟะพะปัƒั‡ะตะฝะธะธ ะดะพะบัƒะผะตะฝั‚ะฐ ะดะปั ะฒัะตั… ั„ั€ะตะนะผะพะฒ
  "content_scripts": [
    {
      "matches": [
        "http://*/*",
        "https://*/*"
      ],
      "js": [
        "contentscript.js"
      ],
      "run_at": "document_start",
      "all_frames": true
    }
  ],
  // ะ ะฐะทั€ะตัˆะตะฝ ะดะพัั‚ัƒะฟ ะบ localStorage ะธ idle api
  "permissions": [
    "storage",
    // "unlimitedStorage",
    //"clipboardWrite",
    "idle"
    //"activeTab",
    //"webRequest",
    //"notifications",
    //"tabs"
  ],
  // ะ—ะดะตััŒ ัƒะบะฐะทั‹ะฒะฐัŽั‚ัั ั€ะตััƒั€ัั‹, ะบ ะบะพั‚ะพั€ั‹ะผ ะฑัƒะดะตั‚ ะธะผะตั‚ัŒ ะดะพัั‚ัƒะฟ ะฒะตะฑ ัั‚ั€ะฐะฝะธั†ะฐ. ะขะพะตัั‚ัŒ ะธั… ะผะพะถะฝะพ ะฑัƒะดะตั‚ ะทะฐะฟั€ะฐัˆะธะฒะฐั‚ัŒ fetche'ะผ ะธะปะธ ะฟั€ะพัั‚ะพ xhr
  "web_accessible_resources": ["inpage.js"]
}

เช–เชพเชฒเซ€ background.js, popup.js, inpage.js เช…เชจเซ‡ contentscript.js เชฌเชจเชพเชตเซ‹. เช…เชฎเซ‡ popup.html เช‰เชฎเซ‡เชฐเซ€เช เช›เซ€เช - เช…เชจเซ‡ เช…เชฎเชพเชฐเซ€ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชชเชนเซ‡เชฒเซ‡เชฅเซ€ เชœ Google Chrome เชฎเชพเช‚ เชฒเซ‹เชก เชฅเชˆ เชถเช•เซ‡ เช›เซ‡ เช…เชจเซ‡ เช–เชพเชคเชฐเซ€ เช•เชฐเซ‹ เช•เซ‡ เชคเซ‡ เช•เชพเชฐเซเชฏ เช•เชฐเซ‡ เช›เซ‡.

เช†เชจเซ‡ เชšเช•เชพเชธเชตเชพ เชฎเชพเชŸเซ‡, เชคเชฎเซ‡ เช•เซ‹เชก เชฒเชˆ เชถเช•เซ‹ เช›เซ‹ เช…เชนเซ€เช‚เชฅเซ€. เช…เชฎเซ‡ เชœเซ‡ เช•เชฐเซเชฏเซเช‚ เชคเซ‡ เช‰เชชเชฐเชพเช‚เชค, เชฒเชฟเช‚เช• เชตเซ‡เชฌเชชเซ‡เช•เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชชเซเชฐเซ‹เชœเซ‡เช•เซเชŸเชจเซ€ เชเชธเซ‡เชฎเซเชฌเชฒเซ€เชจเซ‡ เช—เซ‹เช เชตเซ‡ เช›เซ‡. เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช‰เชฎเซ‡เชฐเชตเชพ เชฎเชพเชŸเซ‡, chrome://extensions เชฎเชพเช‚ เชคเชฎเชพเชฐเซ‡ เชฒเซ‹เชก เช…เชจเชชเซ‡เช•เซเชก เช…เชจเซ‡ เช…เชจเซเชฐเซ‚เชช เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเชพเชฅเซ‡ เชซเซ‹เชฒเซเชกเชฐ เชชเชธเช‚เชฆ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ - เช…เชฎเชพเชฐเชพ เช•เชฟเชธเซเชธเชพเชฎเชพเช‚ เชœเซ€.

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชนเชตเซ‡ เช…เชฎเชพเชฐเซเช‚ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช‡เชจเซเชธเซเชŸเซ‹เชฒ เชฅเชˆ เช—เชฏเซเช‚ เช›เซ‡ เช…เชจเซ‡ เช•เชพเชฎ เช•เชฐเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡. เชคเชฎเซ‡ เชจเซ€เชšเซ‡ เชชเซเชฐเชฎเชพเชฃเซ‡ เชตเชฟเชตเชฟเชง เชธเช‚เชฆเชฐเซเชญเซ‹ เชฎเชพเชŸเซ‡ เชตเชฟเช•เชพเชธเช•เชฐเซเชคเชพ เชธเชพเชงเชจเซ‹ เชšเชฒเชพเชตเซ€ เชถเช•เซ‹ เช›เซ‹:

เชชเซ‹เชชเช…เชช ->

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸ เช•เชจเซเชธเซ‹เชฒเชจเซ€ เชเช•เซเชธเซ‡เชธ เชคเซ‡ เชชเซƒเชทเซเช เชจเชพ เชœ เช•เชจเซเชธเซ‹เชฒ เชฆเซเชตเชพเชฐเชพ เชนเชพเชฅ เชงเชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ เชœเซ‡เชจเชพ เชชเชฐ เชคเซ‡ เชฒเซ‹เชจเซเชš เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เช›เซ‡.เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชธเช‚เชฆเซ‡เชถ เชตเชฟเชจเชฟเชฎเชฏ

เชคเซ‡เชฅเซ€, เช†เชชเชฃเซ‡ เชฌเซ‡ เชธเช‚เชšเชพเชฐ เชšเซ‡เชจเชฒเซ‹ เชธเซเชฅเชพเชชเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡: inpage <-> เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เช…เชจเซ‡ เชชเซ‹เชชเช…เชช <-> เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ. เชคเชฎเซ‡, เช…เชฒเชฌเชคเซเชค, เชซเช•เซเชค เชชเซ‹เชฐเซเชŸ เชชเชฐ เชธเช‚เชฆเซ‡เชถเชพ เชฎเซ‹เช•เชฒเซ€ เชถเช•เซ‹ เช›เซ‹ เช…เชจเซ‡ เชคเชฎเชพเชฐเชพ เชชเซ‹เชคเชพเชจเชพ เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒเชจเซ€ เชถเซ‹เชง เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹, เชชเชฐเช‚เชคเซ เชนเซเช‚ เชฎเซ‡เชŸเชพเชฎเชพเชธเซเช• เช“เชชเชจ เชธเซ‹เชฐเซเชธ เชชเซเชฐเซ‹เชœเซ‡เช•เซเชŸเชฎเชพเช‚ เชœเซ‹เชฏเซ‡เชฒเชพ เช…เชญเชฟเช—เชฎเชจเซ‡ เชชเชธเช‚เชฆ เช•เชฐเซเช‚ เช›เซเช‚.

เช† Ethereum เชจเซ‡เชŸเชตเชฐเซเช• เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชเช• เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช›เซ‡. เชคเซ‡เชฎเชพเช‚, เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเชพ เชตเชฟเชตเชฟเชง เชญเชพเช—เซ‹ dnode เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ RPC เชฆเซเชตเชพเชฐเชพ เชตเชพเชคเชšเซ€เชค เช•เชฐเซ‡ เช›เซ‡. เชœเซ‹ เชคเชฎเซ‡ เชคเซ‡เชจเซ‡ เชชเชฐเชฟเชตเชนเชจ เชคเชฐเซ€เช•เซ‡ เชจเซ‹เชกเชœ เชธเซเชŸเซเชฐเซ€เชฎ เชธเชพเชฅเซ‡ เชชเซเชฐเชฆเชพเชจ เช•เชฐเซ‹ เช›เซ‹ เชคเซ‹ เชคเซ‡ เชคเชฎเชจเซ‡ เช–เซ‚เชฌ เชœ เชเชกเชชเชฅเซ€ เช…เชจเซ‡ เชธเช—เชตเชกเชคเชพเชชเซ‚เชฐเซเชตเช• เชเช•เซเชธเชšเซ‡เชจเซเชœเชจเซเช‚ เช†เชฏเซ‹เชœเชจ เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡ (เชเชŸเชฒเซ‡ โ€‹โ€‹เช•เซ‡ เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชœเซ‡ เชธเชฎเชพเชจ เช‡เชจเซเชŸเชฐเชซเซ‡เชธเชจเซ‡ เชฒเชพเช—เซ เช•เชฐเซ‡ เช›เซ‡):

import Dnode from "dnode/browser";

// ะ’ ัั‚ะพะผ ะฟั€ะธะผะตั€ะต ัƒัะปะพะฒะธะผัั ั‡ั‚ะพ ะบะปะธะตะฝั‚ ัƒะดะฐะปะตะฝะฝะพ ะฒั‹ะทั‹ะฒะฐะตั‚ ั„ัƒะฝะบั†ะธะธ ะฝะฐ ัะตั€ะฒะตั€ะต, ั…ะพั‚ั ะฝะธั‡ะตะณะพ ะฝะฐะผ ะฝะต ะผะตัˆะฐะตั‚ ัะดะตะปะฐั‚ัŒ ัั‚ะพ ะดะฒัƒะฝะฐะฟั€ะฐะฒะปะตะฝะฝั‹ะผ

// Cะตั€ะฒะตั€
// API, ะบะพั‚ะพั€ะพะต ะผั‹ ั…ะพั‚ะธะผ ะฟั€ะตะดะพัั‚ะฐะฒะธั‚ัŒ
const dnode = Dnode({
    hello: (cb) => cb(null, "world")
})
// ะขั€ะฐะฝัะฟะพั€ั‚, ะฟะพะฒะตั€ั… ะบะพั‚ะพั€ะพะณะพ ะฑัƒะดะตั‚ ั€ะฐะฑะพั‚ะฐั‚ัŒ dnode. ะ›ัŽะฑะพะน nodejs ัั‚ั€ะธะผ. ะ’ ะฑั€ะฐัƒะทะตั€ะต ะตัั‚ัŒ ะฑะธะฑะธะปะธะพั‚ะตะบะฐ 'readable-stream'
connectionStream.pipe(dnode).pipe(connectionStream)

// ะšะปะธะตะฝั‚
const dnodeClient = Dnode() // ะ’ั‹ะทะพะฒ ะฑะตะท ะฐะณั€ัƒะผะตะฝั‚ะฐ ะทะฝะฐั‡ะธั‚ ั‡ั‚ะพ ะผั‹ ะฝะต ะฟั€ะตะดะพัั‚ะฐะฒะปัะตะผ API ะฝะฐ ะดั€ัƒะณะพะน ัั‚ะพั€ะพะฝะต

// ะ’ั‹ะฒะตะดะตั‚ ะฒ ะบะพะฝัะพะปัŒ world
dnodeClient.once('remote', remote => {
    remote.hello(((err, value) => console.log(value)))
})

เชนเชตเซ‡ เช†เชชเชฃเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช•เซเชฒเชพเชธ เชฌเชจเชพเชตเซ€เชถเซเช‚. เชคเซ‡ เชชเซ‹เชชเช…เชช เช…เชจเซ‡ เชตเซ‡เชฌ เชชเซ‡เชœ เชฎเชพเชŸเซ‡ API เช“เชฌเซเชœเซ‡เช•เซเชŸ เชฌเชจเชพเชตเชถเซ‡ เช…เชจเซ‡ เชคเซ‡เชฎเชจเชพ เชฎเชพเชŸเซ‡ เชกเซ€เชจเซ‹เชก เชฌเชจเชพเชตเชถเซ‡:

import Dnode from 'dnode/browser';

export class SignerApp {

    // ะ’ะพะทะฒั€ะฐั‰ะฐะตั‚ ะพะฑัŠะตะบั‚ API ะดะปั ui
    popupApi(){
        return {
            hello: cb => cb(null, 'world')
        }
    }

    // ะ’ะพะทะฒั€ะฐั‰ะฐะตั‚ ะพะฑัŠะตั‚ API ะดะปั ัั‚ั€ะฐะฝะธั†ั‹
    pageApi(){
        return {
            hello: cb => cb(null, 'world')
        }
    }

    // ะŸะพะดะบะปัŽั‡ะฐะตั‚ popup ui
    connectPopup(connectionStream){
        const api = this.popupApi();
        const dnode = Dnode(api);

        connectionStream.pipe(dnode).pipe(connectionStream);

        dnode.on('remote', (remote) => {
            console.log(remote)
        })
    }

    // ะŸะพะดะบะปัŽั‡ะฐะตั‚ ัั‚ั€ะฐะฝะธั†ัƒ
    connectPage(connectionStream, origin){
        const api = this.popupApi();
        const dnode = Dnode(api);

        connectionStream.pipe(dnode).pipe(connectionStream);

        dnode.on('remote', (remote) => {
            console.log(origin);
            console.log(remote)
        })
    }
}

เช…เชนเซ€เช‚ เช…เชจเซ‡ เชจเซ€เชšเซ‡, เชตเซˆเชถเซเชตเชฟเช• เช•เซเชฐเซ‹เชฎ เช‘เชฌเซเชœเซ‡เช•เซเชŸเชจเซ‡ เชฌเชฆเชฒเซ‡, เช…เชฎเซ‡ เชเช•เซเชธเชŸเซ‡เชจเซเชถเชจ เชเชชเซ€เช†เชˆเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช, เชœเซ‡ Google เชจเชพ เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚ เช•เซเชฐเซ‹เชฎ เช…เชจเซ‡ เช…เชจเซเชฏเชฎเชพเช‚ เชฌเซเชฐเชพเช‰เชเชฐเชจเซ‡ เชเช•เซเชธเซ‡เชธ เช•เชฐเซ‡ เช›เซ‡. เช† เช•เซเชฐเซ‹เชธ-เชฌเซเชฐเชพเช‰เชเชฐ เชธเซเชธเช‚เช—เชคเชคเชพ เชฎเชพเชŸเซ‡ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชชเชฐเช‚เชคเซ เช† เชฒเซ‡เช–เชจเชพ เชนเซ‡เชคเซเช“ เชฎเชพเชŸเซ‡ เช•เซ‹เชˆ เชซเช•เซเชค 'chrome.runtime.connect' เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชถเช•เซ‡ เช›เซ‡.

เชšเชพเชฒเซ‹ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชธเซเช•เซเชฐเชฟเชชเซเชŸเชฎเชพเช‚ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชฆเชพเช–เชฒเซ‹ เชฌเชจเชพเชตเซ€เช:

import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";

const app = new SignerApp();

// onConnect ัั€ะฐะฑะฐั‚ั‹ะฒะฐะตั‚ ะฟั€ะธ ะฟะพะดะบะปัŽั‡ะตะฝะธะธ 'ะฟั€ะพั†ะตััะพะฒ' (contentscript, popup, ะธะปะธ ัั‚ั€ะฐะฝะธั†ะฐ ั€ะฐััˆะธั€ะตะฝะธั)
extensionApi.runtime.onConnect.addListener(connectRemote);

function connectRemote(remotePort) {
    const processName = remotePort.name;
    const portStream = new PortStream(remotePort);
    // ะŸั€ะธ ัƒัั‚ะฐะฝะพะฒะบะต ัะพะตะดะธะฝะตะฝะธั ะผะพะถะฝะพ ัƒะบะฐะทั‹ะฒะฐั‚ัŒ ะธะผั, ะฟะพ ัั‚ะพะผัƒ ะธะผะตะฝะธ ะผั‹ ะธ ะพะฟะฟั€ะตะดะตะปัะตะผ ะบั‚ะพ ะบ ะฝะฐะผ ะฟะพะดะปัŽั‡ะธะปัั, ะบะพะฝั‚ะตะฝั‚ัะบั€ะธะฟั‚ ะธะปะธ ui
    if (processName === 'contentscript'){
        const origin = remotePort.sender.url
        app.connectPage(portStream, origin)
    }else{
        app.connectPopup(portStream)
    }
}

เช•เชพเชฐเชฃ เช•เซ‡ dnode เชธเซเชŸเซเชฐเซ€เชฎเซเชธ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเซ‡ เช›เซ‡, เช…เชจเซ‡ เช…เชฎเชจเซ‡ เชชเซ‹เชฐเซเชŸ เชชเซเชฐเชพเชชเซเชค เชฅเชพเชฏ เช›เซ‡, เชเชกเซ‡เชชเซเชŸเชฐ เชตเชฐเซเช—เชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เชคเซ‡ เชตเชพเช‚เชšเซ€ เชถเช•เชพเชฏ เชคเซ‡เชตเซ€-เชธเซเชŸเซเชฐเซ€เชฎ เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชฌเชจเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชœเซ‡ เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚ เชจเซ‹เชกเชœ เชธเซเชŸเซเชฐเซ€เชฎเซเชธ เชฒเชพเช—เซ เช•เชฐเซ‡ เช›เซ‡:

import {Duplex} from 'readable-stream';

export class PortStream extends Duplex{
    constructor(port){
        super({objectMode: true});
        this._port = port;
        port.onMessage.addListener(this._onMessage.bind(this));
        port.onDisconnect.addListener(this._onDisconnect.bind(this))
    }

    _onMessage(msg) {
        if (Buffer.isBuffer(msg)) {
            delete msg._isBuffer;
            const data = new Buffer(msg);
            this.push(data)
        } else {
            this.push(msg)
        }
    }

    _onDisconnect() {
        this.destroy()
    }

    _read(){}

    _write(msg, encoding, cb) {
        try {
            if (Buffer.isBuffer(msg)) {
                const data = msg.toJSON();
                data._isBuffer = true;
                this._port.postMessage(data)
            } else {
                this._port.postMessage(msg)
            }
        } catch (err) {
            return cb(new Error('PortStream - disconnected'))
        }
        cb()
    }
}

เชนเชตเซ‡ เชšเชพเชฒเซ‹ UI เชฎเชพเช‚ เช•เชจเซ‡เช•เซเชถเชจ เชฌเชจเชพเชตเซ€เช:

import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import Dnode from 'dnode/browser';

const DEV_MODE = process.env.NODE_ENV !== 'production';

setupUi().catch(console.error);

async function setupUi(){
    // ะขะฐะบะถะต, ะบะฐะบ ะธ ะฒ ะบะปะฐััะต ะฟั€ะธะปะพะถะตะฝะธั ัะพะทะดะฐะตะผ ะฟะพั€ั‚, ะพะฑะพั€ะฐั‡ะธะฒะฐะตะผ ะฒ stream, ะดะตะปะฐะตะผ  dnode
    const backgroundPort = extensionApi.runtime.connect({name: 'popup'});
    const connectionStream = new PortStream(backgroundPort);

    const dnode = Dnode();

    connectionStream.pipe(dnode).pipe(connectionStream);

    const background = await new Promise(resolve => {
        dnode.once('remote', api => {
            resolve(api)
        })
    });

    // ะ”ะตะปะฐะตะผ ะพะฑัŠะตะบั‚ API ะดะพัั‚ัƒะฟะฝั‹ะผ ะธะท ะบะพะฝัะพะปะธ
    if (DEV_MODE){
        global.background = background;
    }
}

เชชเช›เซ€ เช…เชฎเซ‡ เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸเชฎเชพเช‚ เช•เชจเซ‡เช•เซเชถเชจ เชฌเชจเชพเชตเซ€เช เช›เซ€เช:

import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import PostMessageStream from 'post-message-stream';

setupConnection();
injectScript();

function setupConnection(){
    const backgroundPort = extensionApi.runtime.connect({name: 'contentscript'});
    const backgroundStream = new PortStream(backgroundPort);

    const pageStream = new PostMessageStream({
        name: 'content',
        target: 'page',
    });

    pageStream.pipe(backgroundStream).pipe(pageStream);
}

function injectScript(){
    try {
        // inject in-page script
        let script = document.createElement('script');
        script.src = extensionApi.extension.getURL('inpage.js');
        const container = document.head || document.documentElement;
        container.insertBefore(script, container.children[0]);
        script.onload = () => script.remove();
    } catch (e) {
        console.error('Injection failed.', e);
    }
}

เช…เชฎเชจเซ‡ API เชจเซ€ เชœเชฐเซ‚เชฐ เชธเชพเชฎเช—เซเชฐเซ€ เชธเซเช•เซเชฐเชฟเชชเซเชŸเชฎเชพเช‚ เชจเชฅเซ€, เชชเชฐเช‚เชคเซ เชธเซ€เชงเชพ เชชเซƒเชทเซเช  เชชเชฐ เชนเซ‹เชตเชพเชฅเซ€, เช…เชฎเซ‡ เชฌเซ‡ เชตเชธเซเชคเซเช“ เช•เชฐเซ€เช เช›เซ€เช:

  1. เช…เชฎเซ‡ เชฌเซ‡ เชชเซเชฐเชตเชพเชนเซ‹ เชฌเชจเชพเชตเซ€เช เช›เซ€เช. เชเช• - เชชเซƒเชทเซเช  เชคเชฐเชซ, เชชเซ‹เชธเซเชŸเชฎเซ‡เชธเซ‡เชœเชจเซ€ เชŸเซ‹เชš เชชเชฐ. เช† เชฎเชพเชŸเซ‡ เช…เชฎเซ‡ เช†เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช เช† เชชเซ‡เช•เซ‡เชœ เชฎเซ‡เชŸเชพเชฎเชพเชธเซเช•เชจเชพ เชจเชฟเชฐเซเชฎเชพเชคเชพเช“ เชคเชฐเชซเชฅเซ€. เชฌเซ€เชœเซ€ เชธเซเชŸเซเชฐเซ€เชฎ เชชเซ‹เชฐเซเชŸ เชชเชฐเชฅเซ€ เชฎเชณเซ‡เชฒ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชชเชฐ เช›เซ‡ runtime.connect. เชšเชพเชฒเซ‹ เชคเซ‡เชฎเชจเซ‡ เช–เชฐเซ€เชฆเซ€เช. เชนเชตเซ‡ เชชเซƒเชทเซเช  เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชชเชฐ เชเช• เชธเซเชŸเซเชฐเซ€เชฎ เชนเชถเซ‡.
  2. เชธเซเช•เซเชฐเชฟเชชเซเชŸเชจเซ‡ DOM เชฎเชพเช‚ เชฆเชพเช–เชฒ เช•เชฐเซ‹. เชธเซเช•เซเชฐเชฟเชชเซเชŸ เชกเชพเช‰เชจเชฒเซ‹เชก เช•เชฐเซ‹ (เชฎเซ‡เชจเชฟเชซเซ‡เชธเซเชŸเชฎเชพเช‚ เชคเซ‡เชจเซ€ เชเช•เซเชธเซ‡เชธเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เชนเชคเซ€) เช…เชจเซ‡ เชŸเซ‡เช— เชฌเชจเชพเชตเซ‹ script เชคเซ‡เชจเซ€ เช…เช‚เชฆเชฐเชจเซ€ เชธเชพเชฎเช—เซเชฐเซ€ เชธเชพเชฅเซ‡:

import PostMessageStream from 'post-message-stream';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";

setupConnection();
injectScript();

function setupConnection(){
    // ะกั‚ั€ะธะผ ะบ ะฑะตะบะณั€ะฐัƒะฝะดัƒ
    const backgroundPort = extensionApi.runtime.connect({name: 'contentscript'});
    const backgroundStream = new PortStream(backgroundPort);

    // ะกั‚ั€ะธะผ ะบ ัั‚ั€ะฐะฝะธั†ะต
    const pageStream = new PostMessageStream({
        name: 'content',
        target: 'page',
    });

    pageStream.pipe(backgroundStream).pipe(pageStream);
}

function injectScript(){
    try {
        // inject in-page script
        let script = document.createElement('script');
        script.src = extensionApi.extension.getURL('inpage.js');
        const container = document.head || document.documentElement;
        container.insertBefore(script, container.children[0]);
        script.onload = () => script.remove();
    } catch (e) {
        console.error('Injection failed.', e);
    }
}

เชนเชตเซ‡ เช…เชฎเซ‡ inpage เชฎเชพเช‚ api เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชฌเชจเชพเชตเซ€เช เช›เซ€เช เช…เชจเซ‡ เชคเซ‡เชจเซ‡ เชตเซˆเชถเซเชตเชฟเช• เชชเชฐ เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช:

import PostMessageStream from 'post-message-stream';
import Dnode from 'dnode/browser';

setupInpageApi().catch(console.error);

async function setupInpageApi() {
    // ะกั‚ั€ะธะผ ะบ ะบะพะฝั‚ะตะฝั‚ัะบั€ะธะฟั‚ัƒ
    const connectionStream = new PostMessageStream({
        name: 'page',
        target: 'content',
    });

    const dnode = Dnode();

    connectionStream.pipe(dnode).pipe(connectionStream);

    // ะŸะพะปัƒั‡ะฐะตะผ ะพะฑัŠะตะบั‚ API
    const pageApi = await new Promise(resolve => {
        dnode.once('remote', api => {
            resolve(api)
        })
    });

    // ะ”ะพัั‚ัƒะฟ ั‡ะตั€ะตะท window
    global.SignerApp = pageApi;
}

เช…เชฎเซ‡ เชคเซˆเชฏเชพเชฐ เช›เซ€เช เชชเซƒเชทเซเช  เช…เชจเซ‡ UI เชฎเชพเชŸเซ‡ เช…เชฒเช— API เชธเชพเชฅเซ‡ เชฐเชฟเชฎเซ‹เชŸ เชชเซเชฐเซ‹เชธเชฟเชœเชฐ เช•เซ‰เชฒ (RPC).. เชจเชตเชพ เชชเซƒเชทเซเช เชจเซ‡ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชธเชพเชฅเซ‡ เช•เชจเซ‡เช•เซเชŸ เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ เช†เชชเชฃเซ‡ เช† เชœเซ‹เชˆ เชถเช•เซ€เช เช›เซ€เช:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เช–เชพเชฒเซ€ API เช…เชจเซ‡ เชฎเซ‚เชณ. เชชเซƒเชทเซเช  เชฌเชพเชœเซ เชชเชฐ, เช†เชชเชฃเซ‡ เชนเซ‡เชฒเซ‹ เชซเช‚เช•เซเชถเชจเชจเซ‡ เช†เชจเชพ เชœเซ‡เชตเซเช‚ เช•เชนเซ€ เชถเช•เซ€เช:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เช†เชงเซเชจเชฟเช• JS เชฎเชพเช‚ เช•เซ‰เชฒเชฌเซ‡เช• เชซเช‚เช•เซเชถเชจเซเชธ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเซเช‚ เช เช–เชฐเชพเชฌ เชฐเซ€เชคเชญเชพเชค เช›เซ‡, เชคเซ‡เชฅเซ€ เชšเชพเชฒเซ‹ เชเช• dnode เชฌเชจเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เชเช• เชจเชพเชจเซ‹ เชธเชนเชพเชฏเช• เชฒเช–เซ€เช เชœเซ‡ เชคเชฎเชจเซ‡ API เช‘เชฌเซเชœเซ‡เช•เซเชŸเชจเซ‡ utils เชชเชฐ เชชเชธเชพเชฐ เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡.

API เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชนเชตเซ‡ เช†เชจเชพ เชœเซ‡เชตเซ‹ เชฆเซ‡เช–เชพเชถเซ‡:

export class SignerApp {

    popupApi() {
        return {
            hello: async () => "world"
        }
    }

...

}

เชฐเชฟเชฎเซ‹เชŸเชฎเชพเช‚เชฅเซ€ เช† เชฐเซ€เชคเซ‡ เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชฎเซ‡เชณเชตเชตเซเช‚:

import {cbToPromise, transformMethods} from "../../src/utils/setupDnode";

const pageApi = await new Promise(resolve => {
    dnode.once('remote', remoteApi => {
        // ะก ะฟะพะผะพั‰ัŒัŽ ัƒั‚ะธะปะธั‚ ะผะตะฝัะตะผ ะฒัะต callback ะฝะฐ promise
        resolve(transformMethods(cbToPromise, remoteApi))
    })
});

เช…เชจเซ‡ เช•เซ‰เชฒเชฟเช‚เช— เช•เชพเชฐเซเชฏเซ‹ เชตเชšเชจ เช†เชชเซ‡ เช›เซ‡:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เช…เชธเซเชฎเซ‡เชณ เช•เชพเชฐเซเชฏเซ‹ เชธเชพเชฅเซ‡เชจเซเช‚ เชธเช‚เชธเซเช•เชฐเชฃ เช‰เชชเชฒเชฌเซเชง เช›เซ‡ เช…เชนเซ€เช‚.

เชเช•เช‚เชฆเชฐเซ‡, เช†เชฐเชชเซ€เชธเซ€ เช…เชจเซ‡ เชธเซเชŸเซเชฐเซ€เชฎ เช…เชญเชฟเช—เชฎ เชเช•เชฆเชฎ เชฒเชตเชšเซ€เช• เชฒเชพเช—เซ‡ เช›เซ‡: เช…เชฎเซ‡ เชธเซเชŸเซ€เชฎ เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เช•เซเชธเชฟเช‚เช—เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชถเช•เซ€เช เช›เซ€เช เช…เชจเซ‡ เชตเชฟเชตเชฟเชง เช•เชพเชฐเซเชฏเซ‹ เชฎเชพเชŸเซ‡ เชตเชฟเชตเชฟเชง API เชฌเชจเชพเชตเซ€ เชถเช•เซ€เช เช›เซ€เช. เชธเซˆเชฆเซเชงเชพเช‚เชคเชฟเช• เชฐเซ€เชคเซ‡, เชกเซ€เชจเซ‹เชกเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช—เชฎเซ‡ เชคเซเชฏเชพเช‚ เชฅเชˆ เชถเช•เซ‡ เช›เซ‡, เชฎเซเช–เซเชฏ เชตเชธเซเชคเซ เช เชจเซ‹เชกเชœ เชธเซเชŸเซเชฐเซ€เชฎเชจเชพ เชธเซเชตเชฐเซ‚เชชเชฎเชพเช‚ เชชเชฐเชฟเชตเชนเชจเชจเซ‡ เชฒเชชเซ‡เชŸเซ€ เช›เซ‡.

เชตเซˆเช•เชฒเซเชชเชฟเช• JSON เชซเซ‹เชฐเซเชฎเซ‡เชŸ เช›เซ‡, เชœเซ‡ JSON RPC 2 เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒเชจเซ‹ เช…เชฎเชฒ เช•เชฐเซ‡ เช›เซ‡. เชœเซ‹ เช•เซ‡, เชคเซ‡ เชšเซ‹เช•เซเช•เชธ เชชเชฐเชฟเชตเชนเชจ (TCP เช…เชจเซ‡ HTTP(S)) เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเซ‡ เช›เซ‡, เชœเซ‡ เช…เชฎเชพเชฐเชพ เช•เชฟเชธเซเชธเชพเชฎเชพเช‚ เชฒเชพเช—เซ เชชเชกเชคเซเช‚ เชจเชฅเซ€.

เช†เช‚เชคเชฐเชฟเช• เชฐเชพเชœเซเชฏ เช…เชจเซ‡ เชธเซเชฅเชพเชจเชฟเช• เชธเช‚เช—เซเชฐเชน

เช…เชฎเชพเชฐเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเซ€ เช†เช‚เชคเชฐเชฟเช• เชธเซเชฅเชฟเชคเชฟ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡ - เช“เช›เชพเชฎเชพเช‚ เช“เช›เซ€ เชธเชพเช‡เชจเชฟเช‚เช— เช•เซ€. เช…เชฎเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชฎเชพเช‚ เชธเซเชฅเชฟเชคเชฟ เช…เชจเซ‡ เชคเซ‡เชจเซ‡ เชชเซ‹เชชเช…เชช API เชฎเชพเช‚ เชฌเชฆเชฒเชตเชพ เชฎเชพเชŸเซ‡เชจเซ€ เชชเชฆเซเชงเชคเชฟเช“ เชธเชฐเชณเชคเชพเชฅเซ€ เช‰เชฎเซ‡เชฐเซ€ เชถเช•เซ€เช เช›เซ€เช:

import {setupDnode} from "./utils/setupDnode";

export class SignerApp {

    constructor(){
        this.store = {
            keys: [],
        };
    }

    addKey(key){
        this.store.keys.push(key)
    }

    removeKey(index){
        this.store.keys.splice(index,1)
    }

    popupApi(){
        return {
            addKey: async (key) => this.addKey(key),
            removeKey: async (index) => this.removeKey(index)
        }
    }

    ...

} 

เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟเชฎเชพเช‚, เช…เชฎเซ‡ เชฆเชฐเซ‡เช• เชตเชธเซเชคเซเชจเซ‡ เชซเช‚เช•เซเชถเชจเชฎเชพเช‚ เชฒเชชเซ‡เชŸเซ€เชถเซเช‚ เช…เชจเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช‘เชฌเซเชœเซ‡เช•เซเชŸเชจเซ‡ เชตเชฟเช‚เชกเซ‹เชฎเชพเช‚ เชฒเช–เซ€เชถเซเช‚ เชœเซ‡เชฅเซ€ เช…เชฎเซ‡ เช•เชจเซเชธเซ‹เชฒเชฅเซ€ เชคเซ‡เชจเซ€ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเซ€ เชถเช•เซ€เช:

import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";

const DEV_MODE = process.env.NODE_ENV !== 'production';

setupApp();

function setupApp() {
    const app = new SignerApp();

    if (DEV_MODE) {
        global.app = app;
    }

    extensionApi.runtime.onConnect.addListener(connectRemote);

    function connectRemote(remotePort) {
        const processName = remotePort.name;
        const portStream = new PortStream(remotePort);
        if (processName === 'contentscript') {
            const origin = remotePort.sender.url;
            app.connectPage(portStream, origin)
        } else {
            app.connectPopup(portStream)
        }
    }
}

เชšเชพเชฒเซ‹ UI เช•เชจเซเชธเซ‹เชฒเชฎเชพเช‚เชฅเซ€ เชฅเซ‹เชกเซ€ เช•เซ€ เช‰เชฎเซ‡เชฐเซ€เช เช…เชจเซ‡ เชœเซ‹เชˆเช เช•เซ‡ เชฐเชพเชœเซเชฏ เชธเชพเชฅเซ‡ เชถเซเช‚ เชฅเชพเชฏ เช›เซ‡:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชฐเชพเชœเซเชฏเชจเซ‡ เชธเชคเชค เชฌเชจเชพเชตเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ เชœเซ‡เชฅเซ€ เชชเซเชจเชƒเชชเซเชฐเชพเชฐเช‚เชญ เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ เชšเชพเชตเซ€เช“ เช–เซ‹เชตเชพเชˆ เชจ เชœเชพเชฏ.

เช…เชฎเซ‡ เชคเซ‡เชจเซ‡ เชฒเซ‹เช•เชฒ เชธเซเชŸเซ‹เชฐเซ‡เชœเชฎเชพเช‚ เชธเซเชŸเซ‹เชฐ เช•เชฐเซ€เชถเซเช‚, เชฆเชฐเซ‡เช• เชซเซ‡เชฐเชซเชพเชฐ เชธเชพเชฅเซ‡ เชคเซ‡เชจเซ‡ เช“เชตเชฐเชฐเชพเชˆเชŸ เช•เชฐเซ€เชถเซเช‚. เชคเซเชฏเชพเชฐเชฌเชพเชฆ, UI เชฎเชพเชŸเซ‡ เชชเชฃ เชคเซ‡เชจเซ€ เชเช•เซเชธเซ‡เชธ เชœเชฐเซ‚เชฐเซ€ เชฐเชนเซ‡เชถเซ‡, เช…เชจเซ‡ เชนเซเช‚ เชซเซ‡เชฐเชซเชพเชฐเซ‹ เชฎเชพเชŸเซ‡ เชธเชฌเซเชธเซเช•เซเชฐเชพเช‡เชฌ เช•เชฐเชตเชพ เชชเชฃ เชˆเชšเซเช›เซเช‚ เช›เซเช‚. เชคเซ‡เชจเชพ เช†เชงเชพเชฐเซ‡, เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ เชธเซเชŸเซ‹เชฐเซ‡เชœ เชฌเชจเชพเชตเชตเชพ เช…เชจเซ‡ เชคเซ‡เชจเชพ เชซเซ‡เชฐเชซเชพเชฐเซ‹ เชฎเชพเชŸเซ‡ เชธเชฌเซเชธเซเช•เซเชฐเชพเช‡เชฌ เช•เชฐเชตเชพเชจเซเช‚ เช…เชจเซเช•เซ‚เชณ เชฐเชนเซ‡เชถเซ‡.

เช…เชฎเซ‡ mobx เชชเซเชธเซเชคเช•เชพเชฒเชฏเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชถเซเช‚ (https://github.com/mobxjs/mobx). เชชเชธเช‚เชฆเช—เซ€ เชคเซ‡เชจเชพ เชชเชฐ เชชเชกเซ€ เช•เชพเชฐเชฃ เช•เซ‡ เชฎเชพเชฐเซ‡ เชคเซ‡เชจเซ€ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชจเชนเซ‹เชคเซ€, เชชเชฐเช‚เชคเซ เชนเซเช‚ เช–เชฐเซ‡เช–เชฐ เชคเซ‡เชจเซ‹ เช…เชญเซเชฏเชพเชธ เช•เชฐเชตเชพ เชฎเชพเช‚เช—เชคเซ‹ เชนเชคเซ‹.

เชšเชพเชฒเซ‹ เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เชธเซเชฅเชฟเชคเชฟเชจเซ€ เชถเชฐเซ‚เช†เชค เช‰เชฎเซ‡เชฐเซ€เช เช…เชจเซ‡ เชธเซเชŸเซ‹เชฐเชจเซ‡ เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ เชฌเชจเชพเชตเซ€เช:

import {observable, action} from 'mobx';
import {setupDnode} from "./utils/setupDnode";

export class SignerApp {

    constructor(initState = {}) {
        // ะ’ะฝะตัˆะฝะต store ั‚ะฐะบ ะธ ะพัั‚ะฐะฝะตั‚ัั ั‚ะตะผ ะถะต ะพะฑัŠะตะบั‚ะพะผ, ั‚ะพะปัŒะบะพ ั‚ะตะฟะตั€ัŒ ะฒัะต ะตะณะพ ะฟะพะปั ัั‚ะฐะปะธ proxy, ะบะพั‚ะพั€ั‹ะต ะพั‚ัะปะตะถะธะฒะฐัŽั‚ ะดะพัั‚ัƒะฟ ะบ ะฝะธะผ
        this.store =  observable.object({
            keys: initState.keys || [],
        });
    }

    // ะœะตั‚ะพะดั‹, ะบะพั‚ะพั€ั‹ะต ะผะตะฝััŽั‚ observable ะฟั€ะธะฝัั‚ะพ ะพะฑะพั€ะฐั‡ะธะฒะฐั‚ัŒ ะดะตะบะพั€ะฐั‚ะพั€ะพะผ
    @action
    addKey(key) {
        this.store.keys.push(key)
    }

    @action
    removeKey(index) {
        this.store.keys.splice(index, 1)
    }

    ...

}

"เชนเซ‚เชก เชนเซ‡เช เชณ," mobx เช เชคเชฎเชพเชฎ เชธเซเชŸเซ‹เชฐ เชซเซ€เชฒเซเชกเซเชธเชจเซ‡ เชชเซเชฐเซ‹เช•เซเชธเซ€ เชธเชพเชฅเซ‡ เชฌเชฆเชฒเซเชฏเชพ เช›เซ‡ เช…เชจเซ‡ เชคเซ‡เชฎเชจเซ‡ เชคเชฎเชพเชฎ เช•เซ‰เชฒเซเชธเชจเซ‡ เช…เชŸเช•เชพเชตเซ‡ เช›เซ‡. เช† เชธเช‚เชฆเซ‡เชถเชพเช“ เชชเชฐ เชธเชฌเซเชธเซเช•เซเชฐเชพเช‡เชฌ เช•เชฐเชตเชพเชจเซเช‚ เชถเช•เซเชฏ เชฌเชจเชถเซ‡.

เชจเซ€เชšเซ‡ เชนเซเช‚ เช˜เชฃเซ€ เชตเชพเชฐ "เชฌเชฆเชฒเชคเซ€ เชตเช–เชคเซ‡" เชถเชฌเซเชฆเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชถ, เชœเซ‹ เช•เซ‡ เช† เชธเช‚เชชเซ‚เชฐเซเชฃ เชฐเซ€เชคเซ‡ เชธเชพเชšเซเช‚ เชจเชฅเซ€. Mobx เชซเซ€เชฒเซเชกเซเชธเชจเซ€ เชเช•เซเชธเซ‡เชธเชจเซ‡ เชŸเซเชฐเซ‡เช• เช•เชฐเซ‡ เช›เซ‡. เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€ เชœเซ‡ เชชเซเชฐเซ‹เช•เซเชธเซ€ เช“เชฌเซเชœเซ‡เช•เซเชŸ เชฌเชจเชพเชตเซ‡ เช›เซ‡ เชคเซ‡เชจเชพ เช—เซ‡เชŸเชฐเซเชธ เช…เชจเซ‡ เชธเซ‡เชŸเชฐเซเชธเชจเซ‹ เช‰เชชเชฏเซ‹เช— เชฅเชพเชฏ เช›เซ‡.

เชเช•เซเชถเชจ เชกเซ‡เช•เซ‹เชฐเซ‡เชŸเชฐเซเชธ เชฌเซ‡ เชนเซ‡เชคเซเช“ เชชเซ‚เชฐเชพ เชชเชพเชกเซ‡ เช›เซ‡:

  1. enforceActions เชซเซเชฒเซ‡เช— เชธเชพเชฅเซ‡ เช•เชกเช• เชฎเซ‹เชกเชฎเชพเช‚, mobx เชฐเชพเชœเซเชฏเชจเซ‡ เชธเซ€เชงเซเช‚ เชฌเชฆเชฒเชตเชพ เชชเชฐ เชชเซเชฐเชคเชฟเชฌเช‚เชง เชฎเซ‚เช•เซ‡ เช›เซ‡. เชธเช–เชค เชชเชฐเชฟเชธเซเชฅเชฟเชคเชฟเช“เชฎเชพเช‚ เช•เชพเชฎ เช•เชฐเชตเซเช‚ เช เชธเชพเชฐเซ€ เชชเซเชฐเชฅเชพ เชฎเชพเชจเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡.
  2. เชœเซ‹ เช•เซ‹เชˆ เชซเช‚เช•เซเชถเชจ เช˜เชฃเซ€ เชตเช–เชค เชธเซเชŸเซ‡เชŸเชฎเชพเช‚ เชซเซ‡เชฐเชซเชพเชฐ เช•เชฐเซ‡ เชคเซ‹ เชชเชฃ - เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เช…เชฎเซ‡ เช•เซ‹เชกเชจเซ€ เช•เซ‡เชŸเชฒเซ€เช• เชฒเชพเช‡เชจเชฎเชพเช‚ เช˜เชฃเชพ เชซเซ€เชฒเซเชกเซเชธ เชฌเชฆเชฒเซ€เช เช›เซ€เช - เชจเชฟเชฐเซ€เช•เซเชทเช•เซ‹เชจเซ‡ เชœเซเชฏเชพเชฐเซ‡ เชคเซ‡ เชชเซ‚เชฐเซเชฃ เชฅเชพเชฏ เชคเซเชฏเชพเชฐเซ‡ เชœ เชธเซ‚เชšเชฟเชค เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เช† เช…เช—เซเชฐเชญเชพเช— เชฎเชพเชŸเซ‡ เช–เชพเชธ เช•เชฐเซ€เชจเซ‡ เชฎเชนเชคเซเชตเชชเซ‚เชฐเซเชฃ เช›เซ‡, เชœเซเชฏเชพเช‚ เชฌเชฟเชจเชœเชฐเซ‚เชฐเซ€ เชธเซเชŸเซ‡เชŸ เช…เชชเชกเซ‡เชŸ เชคเชคเซเชตเซ‹เชจเชพ เชฌเชฟเชจเชœเชฐเซ‚เชฐเซ€ เชฐเซ‡เชจเซเชกเชฐเชฟเช‚เช— เชคเชฐเชซ เชฆเซ‹เชฐเซ€ เชœเชพเชฏ เช›เซ‡. เช…เชฎเชพเชฐเชพ เช•เชฟเชธเซเชธเชพเชฎเชพเช‚, เชชเซเชฐเชฅเชฎ เช•เซ‡ เชฌเซ€เชœเซเช‚ เชฌเซ‡เชฎเชพเช‚เชฅเซ€ เช•เซ‹เชˆ เช–เชพเชธ เชธเช‚เชฌเช‚เชงเชฟเชค เชจเชฅเซ€, เชชเชฐเช‚เชคเซ เช…เชฎเซ‡ เชถเซเชฐเซ‡เชทเซเช  เชชเซเชฐเชฅเชพเช“เชจเซเช‚ เชชเชพเชฒเชจ เช•เชฐเซ€เชถเซเช‚. เชคเซ‡ เชคเชฎเชพเชฎ เช•เชพเชฐเซเชฏเซ‹เชฎเชพเช‚ เชกเซ‡เช•เซ‹เชฐเซ‡เชŸเชฐเชจเซ‡ เชœเซ‹เชกเชตเชพเชจเซ‹ เชฐเชฟเชตเชพเชœ เช›เซ‡ เชœเซ‡ เช…เชตเชฒเซ‹เช•เชจ เช•เชฐเซ‡เชฒ เช•เซเชทเซ‡เชคเซเชฐเซ‹เชจเซ€ เชธเซเชฅเชฟเชคเชฟเชจเซ‡ เชฌเชฆเชฒเซ‡ เช›เซ‡.

เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟเชฎเชพเช‚ เช…เชฎเซ‡ เชฒเซ‹เช•เชฒ เชธเซเชŸเซ‹เชฐเซ‡เชœเชฎเชพเช‚ เชฐเชพเชœเซเชฏเชจเซ‡ เช†เชฐเช‚เชญ เช…เชจเซ‡ เชธเชพเชšเชตเซ€เชถเซเช‚:

import {reaction, toJS} from 'mobx';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
// ะ’ัะฟะพะผะพะณะฐั‚ะตะปัŒะฝั‹ะต ะผะตั‚ะพะดั‹. ะ—ะฐะฟะธัั‹ะฒะฐัŽั‚/ั‡ะธั‚ะฐัŽั‚ ะพะฑัŠะตะบั‚ ะฒ/ะธะท localStorage ะฒะธะดะต JSON ัั‚ั€ะพะบะธ ะฟะพ ะบะปัŽั‡ัƒ 'store'
import {loadState, saveState} from "./utils/localStorage";

const DEV_MODE = process.env.NODE_ENV !== 'production';

setupApp();

function setupApp() {
    const initState = loadState();
    const app = new SignerApp(initState);

    if (DEV_MODE) {
        global.app = app;
    }

    // Setup state persistence

    // ะ ะตะทัƒะปัŒั‚ะฐั‚ reaction ะฟั€ะธัะฒะฐะธะฒะฐะตั‚ัั ะฟะตั€ะตะผะตะฝะฝะพะน, ั‡ั‚ะพะฑั‹ ะฟะพะดะฟะธัะบัƒ ะผะพะถะฝะพ ะฑั‹ะปะพ ะพั‚ะผะตะฝะธั‚ัŒ. ะะฐะผ ัั‚ะพ ะฝะต ะฝัƒะถะฝะพ, ะพัั‚ะฐะฒะปะตะฝะพ ะดะปั ะฟั€ะธะผะตั€ะฐ
    const localStorageReaction = reaction(
        () => toJS(app.store), // ะคัƒะฝะบั†ะธั-ัะตะปะตะบั‚ะพั€ ะดะฐะฝะฝั‹ั…
        saveState // ะคัƒะฝะบั†ะธั, ะบะพั‚ะพั€ะฐั ะฑัƒะดะตั‚ ะฒั‹ะทะฒะฐะฝะฐ ะฟั€ะธ ะธะทะผะตะฝะตะฝะธะธ ะดะฐะฝะฝั‹ั…, ะบะพั‚ะพั€ั‹ะต ะฒะพะทะฒั€ะฐั‰ะฐะตั‚ ัะตะปะตะบั‚ะพั€
    );

    extensionApi.runtime.onConnect.addListener(connectRemote);

    function connectRemote(remotePort) {
        const processName = remotePort.name;
        const portStream = new PortStream(remotePort);
        if (processName === 'contentscript') {
            const origin = remotePort.sender.url
            app.connectPage(portStream, origin)
        } else {
            app.connectPopup(portStream)
        }
    }
}

เชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เช•เชพเชฐเซเชฏ เช…เชนเซ€เช‚ เชฐเชธเชชเซเชฐเชฆ เช›เซ‡. เชคเซ‡เชจเซ€ เชฌเซ‡ เชฆเชฒเซ€เชฒเซ‹ เช›เซ‡:

  1. เชกเซ‡เชŸเชพ เชธเชฟเชฒเซ‡เช•เซเชŸเชฐ.
  2. เชเช• เชนเซ‡เชจเซเชกเชฒเชฐ เช•เซ‡ เชœเซ‡เชจเซ‡ เช† เชกเซ‡เชŸเชพ เชฆเชฐ เชตเช–เชคเซ‡ เชฌเชฆเชฒเชพเชฏ เชคเซเชฏเชพเชฐเซ‡ เชคเซ‡เชจเซ€ เชธเชพเชฅเซ‡ เช•เซ‰เชฒ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡.

เชฐเซ‡เชกเช•เซเชธเชฅเซ€ เชตเชฟเชชเชฐเซ€เชค, เชœเซเชฏเชพเช‚ เช…เชฎเซ‡ เชธเซเชชเชทเซเชŸเชชเชฃเซ‡ เชฆเชฒเซ€เชฒ เชคเชฐเซ€เช•เซ‡ เชฐเชพเชœเซเชฏ เชชเซเชฐเชพเชชเซเชค เช•เชฐเซ€เช เช›เซ€เช, mobx เชฏเชพเชฆ เชฐเชพเช–เซ‡ เช›เซ‡ เช•เซ‡ เช…เชฎเซ‡ เชชเชธเช‚เชฆเช—เซ€เช•เชพเชฐเชจเซ€ เช…เช‚เชฆเชฐ เช•เชฏเชพ เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ เชตเชธเซเชคเซเช“เชจเซ‡ เชเช•เซเชธเซ‡เชธ เช•เชฐเซ€เช เช›เซ€เช, เช…เชจเซ‡ เชœเซเชฏเชพเชฐเซ‡ เชคเซ‡เช“ เชฌเชฆเชฒเชพเชฏ เช›เซ‡ เชคเซเชฏเชพเชฐเซ‡ เชœ เชนเซ‡เชจเซเชกเชฒเชฐเชจเซ‡ เช•เซ‰เชฒ เช•เชฐเซ‡ เช›เซ‡.

เชคเซ‡ เชธเชฎเชœเชตเซเช‚ เช…เช—เชคเซเชฏเชจเซเช‚ เช›เซ‡ เช•เซ‡ mobx เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชจเช•เซเช•เซ€ เช•เชฐเซ‡ เช›เซ‡ เช•เซ‡ เช†เชชเชฃเซ‡ เช•เชฏเชพ เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ เชชเชฐ เชธเชฌเซเชธเซเช•เซเชฐเชพเช‡เชฌ เช•เชฐเซ€เช เช›เซ€เช. เชœเซ‹ เชฎเซ‡เช‚ เช†เชจเชพ เชœเซ‡เชตเชพ เช•เซ‹เชกเชฎเชพเช‚ เชชเชธเช‚เชฆเช—เซ€เช•เชพเชฐ เชฒเช–เซเชฏเซเช‚() => app.store, เชคเซ‹ เชชเช›เซ€ เชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เช•เซเชฏเชพเชฐเซ‡เชฏ เช•เชนเซ‡เชตเชพเชถเซ‡ เชจเชนเซ€เช‚, เช•เชพเชฐเชฃ เช•เซ‡ เชธเช‚เช—เซเชฐเชน เชชเซ‹เชคเซ‡ เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ เชจเชฅเซ€, เชซเช•เซเชค เชคเซ‡เชจเชพ เช•เซเชทเซ‡เชคเซเชฐเซ‹ เช›เซ‡.

เชœเซ‹ เชฎเซ‡เช‚ เชคเซ‡เชจเซ‡ เช† เชฐเซ€เชคเซ‡ เชฒเช–เซเชฏเซเช‚ () => app.store.keys, เชชเช›เซ€ เชซเชฐเซ€เชฅเซ€ เช•เช‚เชˆ เชฅเชถเซ‡ เชจเชนเซ€เช‚, เช•เชพเชฐเชฃ เช•เซ‡ เชœเซเชฏเชพเชฐเซ‡ เชเชฐเซ‡ เชคเชคเซเชตเซ‹ เช‰เชฎเซ‡เชฐเซ€/เชฆเซ‚เชฐ เช•เชฐเซ€ เชฐเชนเซเชฏเชพ เชนเซ‹เชฏ, เชคเซเชฏเชพเชฐเซ‡ เชคเซ‡เชจเซ‹ เชธเช‚เชฆเชฐเซเชญ เชฌเชฆเชฒเชพเชถเซ‡ เชจเชนเซ€เช‚.

Mobx เชชเซเชฐเชฅเชฎ เชตเช–เชค เชชเชธเช‚เชฆเช—เซ€เช•เชพเชฐ เชคเชฐเซ€เช•เซ‡ เช•เชพเชฐเซเชฏ เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชฎเชพเชคเซเชฐ เช…เชฎเซ‡ เชเช•เซเชธเซ‡เชธ เช•เชฐเซ‡เชฒ เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ เชตเชธเซเชคเซเช“เชจเซ‹ เชœ เชŸเซเชฐเซ…เช• เชฐเชพเช–เซ‡ เช›เซ‡. เช† เชชเซเชฐเซ‹เช•เซเชธเซ€ เช—เซ‡เชŸเชฐ เชฆเซเชตเชพเชฐเชพ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชคเซ‡เชฅเซ€, เชฌเชฟเชฒเซเชŸ-เช‡เชจ เชซเช‚เช•เซเชถเชจเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช…เชนเซ€เช‚ เชฅเชพเชฏ เช›เซ‡ toJS. เชคเซ‡ เชฎเซ‚เชณ เช•เซเชทเซ‡เชคเซเชฐเซ‹ เชธเชพเชฅเซ‡ เชฌเชฆเชฒเชพเชฏเซ‡เชฒ เชคเชฎเชพเชฎ เชชเซเชฐเซ‹เช•เซเชธเซ€เช“ เชธเชพเชฅเซ‡ เชเช• เชจเชตเซ‹ เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡. เชเช•เซเชเซ‡เช•เซเชฏเซเชถเชจ เชฆเชฐเชฎเชฟเชฏเชพเชจ, เชคเซ‡ เช‘เชฌเซเชœเซ‡เช•เซเชŸเชจเชพ เชคเชฎเชพเชฎ เช•เซเชทเซ‡เชคเซเชฐเซ‹เชจเซ‡ เชตเชพเช‚เชšเซ‡ เช›เซ‡ - เชคเซ‡เชฅเซ€ เช—เซ‡เชŸเชฐเซเชธ เชŸเซเชฐเชฟเช—เชฐ เชฅเชพเชฏ เช›เซ‡.

เชชเซ‹เชชเช…เชช เช•เชจเซเชธเซ‹เชฒเชฎเชพเช‚ เช†เชชเชฃเซ‡ เชซเชฐเซ€เชฅเซ€ เช˜เชฃเซ€ เช•เซ€ เช‰เชฎเซ‡เชฐเซ€เชถเซเช‚. เช† เชตเช–เชคเซ‡ เชคเซ‡เช“ เชฒเซ‹เช•เชฒ เชธเซเชŸเซ‹เชฐเซ‡เชœเชฎเชพเช‚ เชชเชฃ เชธเชฎเชพเชชเซเชค เชฅเชฏเชพ:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชœเซเชฏเชพเชฐเซ‡ เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เชชเซƒเชทเซเช  เชซเชฐเซ€เชฅเซ€ เชฒเซ‹เชก เชฅเชพเชฏ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เชฎเชพเชนเชฟเชคเซ€ เชธเซเชฅเชพเชจเซ‡ เชฐเชนเซ‡ เช›เซ‡.

เช† เชฌเชฟเช‚เชฆเซ เชธเซเชงเซ€เชจเชพ เชคเชฎเชพเชฎ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช•เซ‹เชก เชœเซ‹เชˆ เชถเช•เชพเชฏ เช›เซ‡ เช…เชนเซ€เช‚.

เช–เชพเชจเช—เซ€ เช•เซ€เช“เชจเซ‹ เชธเซเชฐเช•เซเชทเชฟเชค เชธเช‚เช—เซเชฐเชน

เชธเซเชชเชทเซเชŸ เชฒเช–เชพเชฃเชฎเชพเช‚ เช–เชพเชจเช—เซ€ เช•เซ€เชจเซ‡ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเชตเซ€ เช…เชธเซเชฐเช•เซเชทเชฟเชค เช›เซ‡: เชคเชฎเชจเซ‡ เชนเซ‡เช• เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡, เชคเชฎเชพเชฐเชพ เช•เซ‹เชฎเซเชชเซเชฏเซเชŸเชฐเชจเซ€ เชเช•เซเชธเซ‡เชธ เชฎเซ‡เชณเชตเซ‹, เชตเช—เซ‡เชฐเซ‡ เชเชตเซ€ เชถเช•เซเชฏเชคเชพ เชนเช‚เชฎเซ‡เชถเชพ เชฐเชนเซ‡ เช›เซ‡. เชคเซ‡เชฅเซ€, เชฒเซ‹เช•เชฒ เชธเซเชŸเซ‹เชฐเซ‡เชœเชฎเชพเช‚ เช†เชชเชฃเซ‡ เช•เซ€เชจเซ‡ เชชเชพเชธเชตเชฐเซเชก-เชเชจเซเช•เซเชฐเชฟเชชเซเชŸเซ‡เชก เชธเซเชตเชฐเซ‚เชชเชฎเชพเช‚ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเซ€เชถเซเช‚.

เชตเชงเซ เชธเซเชฐเช•เซเชทเชพ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชฎเชพเช‚ เชเช• เชฒเซ‰เช• เช•เชฐเซ‡เชฒเซ€ เชธเซเชฅเชฟเชคเชฟ เช‰เชฎเซ‡เชฐเซ€เชถเซเช‚, เชœเซ‡เชฎเชพเช‚ เชšเชพเชตเซ€เช“เชจเซ€ เชฌเชฟเชฒเช•เซเชฒ เชเช•เซเชธเซ‡เชธ เชนเชถเซ‡ เชจเชนเซ€เช‚. เชธเชฎเชฏ เชธเชฎเชพเชชเซเชค เชฅเชตเชพเชจเซ‡ เช•เชพเชฐเชฃเซ‡ เช…เชฎเซ‡ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจเชจเซ‡ เชฒเซ‰เช• เช•เชฐเซ‡เชฒ เชธเซเชฅเชฟเชคเชฟเชฎเชพเช‚ เช†เชชเชฎเซ‡เชณเซ‡ เชธเซเชฅเชพเชจเชพเช‚เชคเชฐเชฟเชค เช•เชฐเซ€เชถเซเช‚.

Mobx เชคเชฎเชจเซ‡ เชกเซ‡เชŸเชพเชจเชพ เชจเซเชฏเซ‚เชจเชคเชฎ เชธเซ‡เชŸเชจเซ‡ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡, เช…เชจเซ‡ เชฌเชพเช•เซ€เชจเซ€ เช†เชชเชฎเซ‡เชณเซ‡ เชคเซ‡เชจเชพ เช†เชงเชพเชฐเซ‡ เช—เชฃเชคเชฐเซ€ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เช† เช•เชนเซ‡เชตเชพเชคเชพ เช—เชฃเชฟเชค เช—เซเชฃเชงเชฐเซเชฎเซ‹ เช›เซ‡. เชคเซ‡เชฎเชจเซ€ เชคเซเชฒเชจเชพ เชกเซ‡เชŸเชพเชฌเซ‡เชเชฎเชพเช‚ เชœเซ‹เชตเชพเชˆ เชธเชพเชฅเซ‡ เช•เชฐเซ€ เชถเช•เชพเชฏ เช›เซ‡:

import {observable, action} from 'mobx';
import {setupDnode} from "./utils/setupDnode";
// ะฃั‚ะธะปะธั‚ั‹ ะดะปั ะฑะตะทะพะฟะฐัะฝะพะณะพ ัˆะธั„ั€ะพะฒะฐะฝะธั ัั‚ั€ะพะบ. ะ˜ัะฟะพะปัŒะทัƒัŽั‚ crypto-js
import {encrypt, decrypt} from "./utils/cryptoUtils";

export class SignerApp {
    constructor(initState = {}) {
        this.store = observable.object({
            // ะฅั€ะฐะฝะธะผ ะฟะฐั€ะพะปัŒ ะธ ะทะฐัˆะธั„ั€ะพะฒะฐะฝะฝั‹ะต ะบะปัŽั‡ะธ. ะ•ัะปะธ ะฟะฐั€ะพะปัŒ null - ะฟั€ะธะปะพะถะตะฝะธะต locked
            password: null,
            vault: initState.vault,

            // ะ“ะตั‚ั‚ะตั€ั‹ ะดะปั ะฒั‹ั‡ะธัะปะธะผั‹ั… ะฟะพะปะตะน. ะœะพะถะฝะพ ะฟั€ะพะฒะตัั‚ะธ ะฐะฝะฐะปะพะณะธัŽ ั view ะฒ ะฑะด.
            get locked(){
                return this.password == null
            },
            get keys(){
                return this.locked ?
                    undefined :
                    SignerApp._decryptVault(this.vault, this.password)
            },
            get initialized(){
                return this.vault !== undefined
            }
        })
    }
    // ะ˜ะฝะธั†ะธะฐะปะธะทะฐั†ะธั ะฟัƒัั‚ะพะณะพ ั…ั€ะฐะฝะธะปะธั‰ะฐ ะฝะพะฒั‹ะผ ะฟะฐั€ะพะปะตะผ
    @action
    initVault(password){
        this.store.vault = SignerApp._encryptVault([], password)
    }
    @action
    lock() {
        this.store.password = null
    }
    @action
    unlock(password) {
        this._checkPassword(password);
        this.store.password = password
    }
    @action
    addKey(key) {
        this._checkLocked();
        this.store.vault = SignerApp._encryptVault(this.store.keys.concat(key), this.store.password)
    }
    @action
    removeKey(index) {
        this._checkLocked();
        this.store.vault = SignerApp._encryptVault([
                ...this.store.keys.slice(0, index),
                ...this.store.keys.slice(index + 1)
            ],
            this.store.password
        )
    }

    ... // ะบะพะด ะฟะพะดะบะปัŽั‡ะตะฝะธั ะธ api

    // private
    _checkPassword(password) {
        SignerApp._decryptVault(this.store.vault, password);
    }

    _checkLocked() {
        if (this.store.locked){
            throw new Error('App is locked')
        }
    }

    // ะœะตั‚ะพะดั‹ ะดะปั ัˆะธั„ั€ะพะฒะบะธ/ะดะตัˆะธั„ั€ะพะฒะบะธ ั…ั€ะฐะฝะธะปะธั‰ะฐ
    static _encryptVault(obj, pass){
        const jsonString = JSON.stringify(obj)
        return encrypt(jsonString, pass)
    }

    static _decryptVault(str, pass){
        if (str === undefined){
            throw new Error('Vault not initialized')
        }
        try {
            const jsonString = decrypt(str, pass)
            return JSON.parse(jsonString)
        }catch (e) {
            throw new Error('Wrong password')
        }
    }
}

เชนเชตเซ‡ เช…เชฎเซ‡ เชซเช•เซเชค เชเชจเซเช•เซเชฐเชฟเชชเซเชŸเซ‡เชก เช•เซ€ เช…เชจเซ‡ เชชเชพเชธเชตเชฐเซเชก เชธเซเชŸเซ‹เชฐ เช•เชฐเซ€เช เช›เซ€เช. เชฌเชพเช•เซ€เชจเซเช‚ เชฌเชงเซเช‚ เช—เชฃเชพเชฏ เช›เซ‡. เช…เชฎเซ‡ เชฐเชพเชœเซเชฏเชฎเชพเช‚เชฅเซ€ เชชเชพเชธเชตเชฐเซเชก เชฆเซ‚เชฐ เช•เชฐเซ€เชจเซ‡ เชฒเซ‰เช• เช•เชฐเซ‡เชฒเซ€ เชธเซเชฅเชฟเชคเชฟเชฎเชพเช‚ เชŸเซเชฐเชพเชจเซเชธเชซเชฐ เช•เชฐเซ€เช เช›เซ€เช. เชธเชพเชฐเซเชตเชœเชจเชฟเช• API เชชเชพเชธเซ‡ เชนเชตเซ‡ เชธเซเชŸเซ‹เชฐเซ‡เชœ เชถเชฐเซ‚ เช•เชฐเชตเชพเชจเซ€ เชชเชฆเซเชงเชคเชฟ เช›เซ‡.

เชเชจเซเช•เซเชฐเชฟเชชเซเชถเชจ เชฎเชพเชŸเซ‡ เชฒเช–เชพเชฏเซ‡เชฒ เช•เซเชฐเชฟเชชเซเชŸเซ‹-เชœเซ‡เชเชธเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เช‰เชชเชฏเซ‹เช—เชฟเชคเชพเช“:

import CryptoJS from 'crypto-js'

// ะ˜ัะฟะพะปัŒะทัƒะตั‚ัั ะดะปั ะพัะปะพะถะฝะตะฝะธั ะฟะพะดะฑะพั€ะฐ ะฟะฐั€ะพะปั ะฟะตั€ะตะฑะพั€ะพะผ. ะะฐ ะบะฐะถะดั‹ะน ะฒะฐั€ะธะฐะฝั‚ ะฟะฐั€ะพะปั ะทะปะพัƒะผั‹ัˆะปะตะฝะฝะธะบัƒ ะฟั€ะธะดะตั‚ัั ัะดะตะปะฐั‚ัŒ 5000 ั…ะตัˆะตะน
function strengthenPassword(pass, rounds = 5000) {
    while (rounds-- > 0){
        pass = CryptoJS.SHA256(pass).toString()
    }
    return pass
}

export function encrypt(str, pass){
    const strongPass = strengthenPassword(pass);
    return CryptoJS.AES.encrypt(str, strongPass).toString()
}

export function decrypt(str, pass){
    const strongPass = strengthenPassword(pass)
    const decrypted = CryptoJS.AES.decrypt(str, strongPass);
    return decrypted.toString(CryptoJS.enc.Utf8)
}

เชฌเซเชฐเชพเช‰เชเชฐเชฎเชพเช‚ เชเช• เชจเชฟเชทเซเช•เซเชฐเชฟเชฏ API เช›เซ‡ เชœเซ‡เชจเชพ เชฆเซเชตเชพเชฐเชพ เชคเชฎเซ‡ เช‡เชตเซ‡เชจเซเชŸเชฎเชพเช‚ เชธเชฌเซเชธเซเช•เซเชฐเชพเช‡เชฌ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹ - เชธเซเชŸเซ‡เชŸ เชซเซ‡เชฐเชซเชพเชฐเซ‹. เชฐเชพเชœเซเชฏ, เชคเซ‡ เชฎเซเชœเชฌ, เชนเซ‹เชˆ เชถเช•เซ‡ เช›เซ‡ idle, active ะธ locked. เชจเชฟเชทเซเช•เซเชฐเชฟเชฏ เชฎเชพเชŸเซ‡ เชคเชฎเซ‡ เชธเชฎเชฏเชธเชฎเชพเชชเซเชคเชฟ เชธเซ‡เชŸ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹, เช…เชจเซ‡ เชœเซเชฏเชพเชฐเซ‡ OS เชชเซ‹เชคเซ‡ เชœ เช…เชตเชฐเซ‹เชงเชฟเชค เชนเซ‹เชฏ เชคเซเชฏเชพเชฐเซ‡ เชฒเซ‰เช• เชธเซ‡เชŸ เชฅเชพเชฏ เช›เซ‡. เช…เชฎเซ‡ เชฒเซ‹เช•เชฒ เชธเซเชŸเซ‹เชฐเซ‡เชœเชฎเชพเช‚ เชธเชพเชšเชตเชตเชพ เชฎเชพเชŸเซ‡ เชชเชธเช‚เชฆเช—เซ€เช•เชพเชฐเชจเซ‡ เชชเชฃ เชฌเชฆเชฒเซ€เชถเซเช‚:

import {reaction, toJS} from 'mobx';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
import {loadState, saveState} from "./utils/localStorage";

const DEV_MODE = process.env.NODE_ENV !== 'production';
const IDLE_INTERVAL = 30;

setupApp();

function setupApp() {
    const initState = loadState();
    const app = new SignerApp(initState);

    if (DEV_MODE) {
        global.app = app;
    }

    // ะขะตะฟะตั€ัŒ ะผั‹ ัะฒะฝะพ ัƒะทั‹ะฒะฐะตะผ ะฟะพะปะต, ะบะพั‚ะพั€ะพะผัƒ ะฑัƒะดะตั‚ ะฟั€ะพะธัั…ะพะดะธั‚ัŒ ะดะพัั‚ัƒะฟ, reaction ะพั‚ั€ะฐะฑะพั‚ะฐะตั‚ ะฝะพั€ะผะฐะปัŒะฝะพ
    reaction(
        () => ({
            vault: app.store.vault
        }),
        saveState
    );

    // ะขะฐะนะผะฐัƒั‚ ะฑะตะทะดะตะนัั‚ะฒะธั, ะบะพะณะดะฐ ัั€ะฐะฑะพั‚ะฐะตั‚ ัะพะฑั‹ั‚ะธะต
    extensionApi.idle.setDetectionInterval(IDLE_INTERVAL);
    // ะ•ัะปะธ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปัŒ ะทะฐะปะพั‡ะธะป ัะบั€ะฐะฝ ะธะปะธ ะฑะตะทะดะตะนัั‚ะฒะพะฒะฐะป ะฒ ั‚ะตั‡ะตะฝะธะต ัƒะบะฐะทะฐะฝะฝะพะณะพ ะธะฝั‚ะตั€ะฒะฐะปะฐ ะปะพั‡ะธะผ ะฟั€ะธะปะพะถะตะฝะธะต
    extensionApi.idle.onStateChanged.addListener(state => {
        if (['locked', 'idle'].indexOf(state) > -1) {
            app.lock()
        }
    });

    // Connect to other contexts
    extensionApi.runtime.onConnect.addListener(connectRemote);

    function connectRemote(remotePort) {
        const processName = remotePort.name;
        const portStream = new PortStream(remotePort);
        if (processName === 'contentscript') {
            const origin = remotePort.sender.url
            app.connectPage(portStream, origin)
        } else {
            app.connectPopup(portStream)
        }
    }
}

เช† เชชเช—เชฒเชพ เชชเชนเซ‡เชฒเชพเชจเซ‹ เช•เซ‹เชก เช›เซ‡ เช…เชนเซ€เช‚.

เชตเซเชฏเชตเชนเชพเชฐ

เชคเซ‡เชฅเซ€, เช…เชฎเซ‡ เชธเซŒเชฅเซ€ เชฎเชนเชคเซเชตเชจเซ€ เชฌเชพเชฌเชค เชชเชฐ เช†เชตเซ€เช เช›เซ€เช: เชฌเซเชฒเซ‹เช•เชšเซ‡เชจ เชชเชฐ เชตเซเชฏเชตเชนเชพเชฐเซ‹ เชฌเชจเชพเชตเชตเชพ เช…เชจเซ‡ เชนเชธเซเชคเชพเช•เซเชทเชฐ เช•เชฐเชตเชพ. เช…เชฎเซ‡ WAVES เชฌเซเชฒเซ‹เช•เชšเซ‡เชจ เช…เชจเซ‡ เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชถเซเช‚ เชคเชฐเช‚เช—เซ‹-เชตเซเชฏเชตเชนเชพเชฐเซ‹.

เชชเซเชฐเชฅเชฎ, เชšเชพเชฒเซ‹ เชฐเชพเชœเซเชฏเชฎเชพเช‚ เชธเช‚เชฆเซ‡เชถเชพเช“เชจเซ€ เชถเซเชฐเซ‡เชฃเซ€ เช‰เชฎเซ‡เชฐเซ€เช เช•เซ‡ เชœเซ‡เชจเชพ เชชเชฐ เชธเชนเซ€ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡, เชชเช›เซ€ เชจเชตเซ‹ เชธเช‚เชฆเซ‡เชถ เช‰เชฎเซ‡เชฐเชตเชพ, เชนเชธเซเชคเชพเช•เซเชทเชฐเชจเซ€ เชชเซเชทเซเชŸเชฟ เช•เชฐเชตเชพ เช…เชจเซ‡ เชจเช•เชพเชฐเชตเชพ เชฎเชพเชŸเซ‡เชจเซ€ เชชเชฆเซเชงเชคเชฟเช“ เช‰เชฎเซ‡เชฐเซ‹:

import {action, observable, reaction} from 'mobx';
import uuid from 'uuid/v4';
import {signTx} from '@waves/waves-transactions'
import {setupDnode} from "./utils/setupDnode";
import {decrypt, encrypt} from "./utils/cryptoUtils";

export class SignerApp {

    ...

    @action
    newMessage(data, origin) {
        // ะ”ะปั ะบะฐะถะดะพะณะพ ัะพะพะฑั‰ะตะฝะธั ัะพะทะดะฐะตะผ ะผะตั‚ะฐะดะฐะฝะฝั‹ะต ั id, ัั‚ะฐั‚ัƒัะพะผ, ะฒั‹ั€ะตะผะตะฝะตะผ ัะพะทะดะฐะฝะธั ะธ ั‚ะด.
        const message = observable.object({
            id: uuid(), // ะ˜ะดะตะฝั‚ะธั„ะธะบะฐั‚ะพั€, ะธัะฟะพะปัŒะทัƒัŽัŽ uuid
            origin, // Origin ะฑัƒะดะตะผ ะฒะฟะพัะปะตะดัั‚ะฒะธะธ ะฟะพะบะฐะทั‹ะฒะฐั‚ัŒ ะฒ ะธะฝั‚ะตั€ั„ะตะนัะต
            data, //
            status: 'new', // ะกั‚ะฐั‚ัƒัะพะฒ ะฑัƒะดะตั‚ ั‡ะตั‚ั‹ั€ะต: new, signed, rejected ะธ failed
            timestamp: Date.now()
        });
        console.log(`new message: ${JSON.stringify(message, null, 2)}`);

        this.store.messages.push(message);

        // ะ’ะพะทะฒั€ะฐั‰ะฐะตะผ ะฟั€ะพะผะธั ะฒะฝัƒั‚ั€ะธ ะบะพั‚ะพั€ะพะณะพ mobx ะผะพะฝะธั‚ะพั€ะธั‚ ะธะทะผะตะฝะตะฝะธั ัะพะพะฑั‰ะตะฝะธั. ะšะฐะบ ั‚ะพะปัŒะบะพ ัั‚ะฐั‚ัƒั ะฟะพะผะตะฝัะตั‚ัั ะผั‹ ะทะฐั€ะตะทะพะปะฒะธะผ ะตะณะพ
        return new Promise((resolve, reject) => {
            reaction(
                () => message.status, //ะ‘ัƒะดะตะผ ะพะฑัะตั€ะฒะธั‚ัŒ ัั‚ะฐั‚ัƒั ัะพะพะฑั‰ะตะฝั
                (status, reaction) => { // ะฒั‚ะพั€ะพะน ะฐั€ะณัƒะผะตะฝั‚ ัั‚ะพ ััั‹ะปะบะฐ ะฝะฐ ัะฐะผ reaction, ั‡ั‚ะพะฑั‹ ะตะณะพ ะผะพะถะฝะพ ะฑั‹ะปะพ ัƒะฝะธั‡ั‚ะพะถั‚ัŒ ะฒะฝัƒั‚ั€ะธ ะฒั‹ะทะพะฒะฐ
                    switch (status) {
                        case 'signed':
                            resolve(message.data);
                            break;
                        case 'rejected':
                            reject(new Error('User rejected message'));
                            break;
                        case 'failed':
                            reject(new Error(message.err.message));
                            break;
                        default:
                            return
                    }
                    reaction.dispose()
                }
            )
        })
    }
    @action
    approve(id, keyIndex = 0) {
        const message = this.store.messages.find(msg => msg.id === id);
        if (message == null) throw new Error(`No msg with id:${id}`);
        try {
            message.data = signTx(message.data, this.store.keys[keyIndex]);
            message.status = 'signed'
        } catch (e) {
            message.err = {
                stack: e.stack,
                message: e.message
            };
            message.status = 'failed'
            throw e
        }
    }
    @action
    reject(id) {
        const message = this.store.messages.find(msg => msg.id === id);
        if (message == null) throw new Error(`No msg with id:${id}`);
        message.status = 'rejected'
    }

    ...
}

เชœเซเชฏเชพเชฐเซ‡ เช…เชฎเชจเซ‡ เชจเชตเซ‹ เชธเช‚เชฆเซ‡เชถ เชฎเชณเซ‡ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เช…เชฎเซ‡ เชคเซ‡เชฎเชพเช‚ เชฎเซ‡เชŸเชพเชกเซ‡เชŸเชพ เช‰เชฎเซ‡เชฐเซ€เช เช›เซ€เช, เช•เชฐเซ‹ observable เช…เชจเซ‡ เช‰เชฎเซ‡เชฐเซ‹ store.messages.

เชœเซ‹ เชคเชฎเซ‡ เชจเชพ เช•เชฐเซ‹ observable เชฎเซ‡เชจเซเชฏเซเช…เชฒเซ€, เชชเช›เซ€ เชเชฐเซ‡เชฎเชพเช‚ เชธเช‚เชฆเซ‡เชถเชพเช“ เช‰เชฎเซ‡เชฐเชคเซ€ เชตเช–เชคเซ‡ mobx เชคเซ‡ เชชเซ‹เชคเซ‡ เช•เชฐเชถเซ‡. เชœเซ‹ เช•เซ‡, เชคเซ‡ เชเช• เชจเชตเซ‹ เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชฌเชจเชพเชตเชถเซ‡ เชœเซ‡เชจเซ‹ เช…เชฎเชพเชฐเซ€ เชชเชพเชธเซ‡ เช•เซ‹เชˆ เชธเช‚เชฆเชฐเซเชญ เชจเชฅเซ€, เชชเชฐเช‚เชคเซ เช…เชฎเชจเซ‡ เช†เช—เชฒเชพ เชชเช—เชฒเชพ เชฎเชพเชŸเซ‡ เชคเซ‡เชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡.

เช†เช—เชณ, เช…เชฎเซ‡ เชเช• เชตเชšเชจ เชชเชฐเชค เช•เชฐเซ€เช เช›เซ€เช เชœเซ‡ เชธเช‚เชฆเซ‡เชถเชจเซ€ เชธเซเชฅเชฟเชคเชฟ เชฌเชฆเชฒเชพเชฏ เชคเซเชฏเชพเชฐเซ‡ เช‰เช•เซ‡เชฒเชพเชˆ เชœเชพเชฏ เช›เซ‡. เชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เชฆเซเชตเชพเชฐเชพ เชธเซเชฅเชฟเชคเชฟเชจเซเช‚ เชจเชฟเชฐเซ€เช•เซเชทเชฃ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชœเซ‡ เชธเซเชฅเชฟเชคเชฟ เชฌเชฆเชฒเชพเชฏ เชคเซเชฏเชพเชฐเซ‡ "เชชเซ‹เชคเชพเชจเซ‡ เชฎเชพเชฐเซ€ เชจเชพเช–เชถเซ‡".

เชชเชฆเซเชงเชคเชฟ เช•เซ‹เชก approve ะธ reject เช–เซ‚เชฌ เชœ เชธเชฐเชณ: เชœเซ‹ เชœเชฐเซ‚เชฐเซ€ เชนเซ‹เชฏ เชคเซ‹ เชธเชพเช‡เชจ เช•เชฐเซเชฏเชพ เชชเช›เซ€, เช…เชฎเซ‡ เชซเช•เซเชค เชธเช‚เชฆเซ‡เชถเชจเซ€ เชธเซเชฅเชฟเชคเชฟ เชฌเชฆเชฒเซ€เช เช›เซ€เช.

เช…เชฎเซ‡ UI API เชฎเชพเช‚ เชฎเช‚เชœเซ‚เชฐ เช…เชจเซ‡ เช…เชธเซเชตเซ€เช•เชพเชฐ เชฎเซ‚เช•เซ€เช เช›เซ€เช, เชชเซ‡เชœ API เชฎเชพเช‚ เชจเชตเซ‹ เชธเช‚เชฆเซ‡เชถ:

export class SignerApp {
    ...
    popupApi() {
        return {
            addKey: async (key) => this.addKey(key),
            removeKey: async (index) => this.removeKey(index),

            lock: async () => this.lock(),
            unlock: async (password) => this.unlock(password),
            initVault: async (password) => this.initVault(password),

            approve: async (id, keyIndex) => this.approve(id, keyIndex),
            reject: async (id) => this.reject(id)
        }
    }

    pageApi(origin) {
        return {
            signTransaction: async (txParams) => this.newMessage(txParams, origin)
        }
    }

    ...
}

เชนเชตเซ‡ เชšเชพเชฒเซ‹ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเชพเชฅเซ‡ เชŸเซเชฐเชพเชจเซเชเซ‡เช•เซเชถเชจ เชชเชฐ เชธเชนเซ€ เช•เชฐเชตเชพเชจเซ‹ เชชเซเชฐเชฏเชพเชธ เช•เชฐเซ€เช:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชธเชพเชฎเชพเชจเซเชฏ เชฐเซ€เชคเซ‡, เชฌเชงเซเช‚ เชคเซˆเชฏเชพเชฐ เช›เซ‡, เชœเซ‡ เชฌเชพเช•เซ€ เช›เซ‡ เชคเซ‡ เช›เซ‡ เชธเชฐเชณ UI เช‰เชฎเซ‡เชฐเซ‹.

UI

เช‡เชจเซเชŸเชฐเชซเซ‡เชธเชจเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชธเซเชŸเซ‡เชŸเชจเซ€ เชเช•เซเชธเซ‡เชธเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. UI เชฌเชพเชœเซเช เช…เชฎเซ‡ เช•เชฐเซ€เชถเซเช‚ observable เชธเซเชŸเซ‡เชŸ เช•เชฐเซ‹ เช…เชจเซ‡ API เชฎเชพเช‚ เชซเช‚เช•เซเชถเชจ เช‰เชฎเซ‡เชฐเซ‹ เชœเซ‡ เช† เชธเซเชฅเชฟเชคเชฟเชจเซ‡ เชฌเชฆเชฒเชถเซ‡. เชšเชพเชฒเซ‹ เช‰เชฎเซ‡เชฐเซ€เช observable เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟเชฎเชพเช‚เชฅเซ€ เชชเซเชฐเชพเชชเซเชค API เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชชเชฐ:

import {observable} from 'mobx'
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {cbToPromise, setupDnode, transformMethods} from "./utils/setupDnode";
import {initApp} from "./ui/index";

const DEV_MODE = process.env.NODE_ENV !== 'production';

setupUi().catch(console.error);

async function setupUi() {
    // ะŸะพะดะบะปัŽั‡ะฐะตะผัั ะบ ะฟะพั€ั‚ัƒ, ัะพะทะดะฐะตะผ ะธะท ะฝะตะณะพ ัั‚ั€ะธะผ
    const backgroundPort = extensionApi.runtime.connect({name: 'popup'});
    const connectionStream = new PortStream(backgroundPort);

    // ะกะพะทะดะฐะตะผ ะฟัƒัั‚ะพะน observable ะดะปั ัะพัั‚ะพัะฝะธั background'a
    let backgroundState = observable.object({});
    const api = {
        //ะžั‚ะดะฐะตะผ ะฑะตะบะณั€ะฐัƒะฝะดัƒ ั„ัƒะฝะบั†ะธัŽ, ะบะพั‚ะพั€ะฐั ะฑัƒะดะตั‚ ะพะฑะฝะพะฒะปัั‚ัŒ observable
        updateState: async state => {
            Object.assign(backgroundState, state)
        }
    };

    // ะ”ะตะปะฐะตะผ RPC ะพะฑัŠะตะบั‚
    const dnode = setupDnode(connectionStream, api);
    const background = await new Promise(resolve => {
        dnode.once('remote', remoteApi => {
            resolve(transformMethods(cbToPromise, remoteApi))
        })
    });

    // ะ”ะพะฑะฐะฒะปัะตะผ ะฒ background observable ัะพ ัั‚ะตะนั‚ะพะผ
    background.state = backgroundState;

    if (DEV_MODE) {
        global.background = background;
    }

    // ะ—ะฐะฟัƒัะบ ะธะฝั‚ะตั€ั„ะตะนัะฐ
    await initApp(background)
}

เช…เช‚เชคเซ‡ เช…เชฎเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช‡เชจเซเชŸเชฐเชซเซ‡เชธ เชฐเซ‡เชจเซเชกเชฐ เช•เชฐเชตเชพเชจเซเช‚ เชถเชฐเซ‚ เช•เชฐเซ€เช เช›เซ€เช. เช† เชเช• เชฐเชฟเชเช•เซเชŸ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เช›เซ‡. เชชเซƒเชทเซเช เชญเซ‚เชฎเชฟ เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชชเซเชฐเซ‹เชชเซเชธเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชธเชฐเชณ เชฐเซ€เชคเซ‡ เชชเชธเชพเชฐ เชฅเชพเชฏ เช›เซ‡. เชคเซ‡ เชธเชพเชšเซเช‚ เชนเชถเซ‡, เช…เชฒเชฌเชคเซเชค, เชชเชฆเซเชงเชคเชฟเช“ เชฎเชพเชŸเซ‡ เชเช• เช…เชฒเช— เชธเซ‡เชตเชพ เช…เชจเซ‡ เชฐเชพเชœเซเชฏ เชฎเชพเชŸเซ‡ เชธเซเชŸเซ‹เชฐ เชฌเชจเชพเชตเชตเซ€, เชชเชฐเช‚เชคเซ เช† เชฒเซ‡เช–เชจเชพ เชนเซ‡เชคเซเช“ เชฎเชพเชŸเซ‡ เช† เชชเซ‚เชฐเชคเซเช‚ เช›เซ‡:

import {render} from 'react-dom'
import App from './App'
import React from "react";

// ะ˜ะฝะธั†ะธะฐะปะธะทะธั€ัƒะตะผ ะฟั€ะธะปะพะถะตะฝะธะต ั background ะพะฑัŠะตะบั‚ะพะผ ะฒ ะบะฐั‡ะตัั‚ ะฒะต props
export async function initApp(background){
    render(
        <App background={background}/>,
        document.getElementById('app-content')
    );
}

เชœเซเชฏเชพเชฐเซ‡ เชกเซ‡เชŸเชพ เชฌเชฆเชฒเชพเชฏ เชคเซเชฏเชพเชฐเซ‡ เชฐเซ‡เชจเซเชกเชฐเชฟเช‚เช— เชถเชฐเซ‚ เช•เชฐเชตเซเช‚ mobx เชธเชพเชฅเซ‡ เช–เซ‚เชฌ เชœ เชธเชฐเชณ เช›เซ‡. เช…เชฎเซ‡ เชซเช•เซเชค เชชเซ‡เช•เซ‡เชœเชฎเชพเช‚เชฅเซ€ เชจเชฟเชฐเซ€เช•เซเชทเช• เชกเซ‡เช•เซ‹เชฐเซ‡เชŸเชฐเชจเซ‡ เช…เชŸเช•เซ€ เชœเชˆเช เช›เซ€เช mobx-เชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ เช˜เชŸเช• เชชเชฐ, เช…เชจเซ‡ เชฐเซ‡เชจเซเชกเชฐ เช†เชชเซ‹เช†เชช เช•เซ‰เชฒ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡ เชœเซเชฏเชพเชฐเซ‡ เช˜เชŸเช• เชซเซ‡เชฐเชซเชพเชฐ เชฆเซเชตเชพเชฐเชพ เชธเช‚เชฆเชฐเซเชญเชฟเชค เช•เซ‹เชˆเชชเชฃ เช…เชตเชฒเซ‹เช•เชจเช•เซเชทเชฎ. เชคเชฎเชพเชฐเซ‡ เช•เซ‹เชˆ เชฎเซ‡เชชเชธเซเชŸเซ‡เชŸเชŸเซ‹เชชเซเชฐเซ‹เชชเซเชธเชจเซ€ เชœเชฐเซ‚เชฐ เชจเชฅเซ€ เช…เชฅเชตเชพ เชฐเซ‡เชกเช•เซเชธเชจเซ€ เชœเซ‡เชฎ เช•เชจเซ‡เช•เซเชŸ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชจเชฅเซ€. เชฌเชงเซเช‚ เชœ เชฌเซ‰เช•เซเชธเชจเซ€ เชฌเชนเชพเชฐ เช•เชพเชฎ เช•เชฐเซ‡ เช›เซ‡:

import React, {Component, Fragment} from 'react'
import {observer} from "mobx-react";
import Init from './components/Initialize'
import Keys from './components/Keys'
import Sign from './components/Sign'
import Unlock from './components/Unlock'

@observer // ะฃ ะšะพะผะฟะพะฝะตั‚ะฐ ั ัั‚ะธะผ ะดะตะบะพั€ะฐั‚ะพะผ ะฑัƒะดะตั‚ ะฐะฒั‚ะพะผะฐั‚ะธั‡ะตัะบะธ ะฒั‹ะทะฒะฐะฝ ะผะตั‚ะพะด render, ะตัะปะธ ะฑัƒะดัƒั‚ ะธะทะผะตะฝะตะฝั‹ observable ะฝะฐ ะบะพั‚ะพั€ั‹ะต ะพะฝ ััั‹ะปะฐะตั‚ัั
export default class App extends Component {

    // ะŸั€ะฐะฒะธะปัŒะฝะพ ะบะพะฝะตั‡ะฝะพ ะฒั‹ะฝะตัั‚ะธ ะปะพะณะธะบัƒ ั€ะตะฝะดะตั€ะฐ ัั‚ั€ะฐะฝะธั† ะฒ ั€ะพัƒั‚ะธะฝะณ ะธ ะฝะต ะธัะฟะพะปัŒะทะพะฒะฐั‚ัŒ ะฒะปะพะถะตะฝะฝั‹ะต ั‚ะตั€ะฝะฐั€ะฝั‹ะต ะพะฟะตั€ะฐั‚ะพั€ั‹,
    // ะธ ะฟั€ะธะฒัะทั‹ะฒะฐั‚ัŒ observable ะธ ะผะตั‚ะพะดั‹ background ะฝะตะฟะพัั€ะตะดัั‚ะฒะตะฝะฝะพ ะบ ั‚ะตะผ ะบะพะผะฟะพะฝะตะฝั‚ะฐะผ, ะบะพั‚ะพั€ั‹ะต ะธั… ะธัะฟะพะปัŒะทัƒัŽั‚
    render() {
        const {keys, messages, initialized, locked} = this.props.background.state;
        const {lock, unlock, addKey, removeKey, initVault, deleteVault, approve, reject} = this.props.background;

        return <Fragment>
            {!initialized
                ?
                <Init onInit={initVault}/>
                :
                locked
                    ?
                    <Unlock onUnlock={unlock}/>
                    :
                    messages.length > 0
                        ?
                        <Sign keys={keys} message={messages[messages.length - 1]} onApprove={approve} onReject={reject}/>
                        :
                        <Keys keys={keys} onAdd={addKey} onRemove={removeKey}/>
            }
            <div>
                {!locked && <button onClick={() => lock()}>Lock App</button>}
                {initialized && <button onClick={() => deleteVault()}>Delete all keys and init</button>}
            </div>
        </Fragment>
    }
}

เชฌเชพเช•เซ€เชจเชพ เช˜เชŸเช•เซ‹ เช•เซ‹เชกเชฎเชพเช‚ เชœเซ‹เชˆ เชถเช•เชพเชฏ เช›เซ‡ UI เชซเซ‹เชฒเซเชกเชฐเชฎเชพเช‚.

เชนเชตเซ‡ เชเชชเซเชฒเซ€เช•เซ‡เชถเชจ เช•เซเชฒเชพเชธเชฎเชพเช‚ เชคเชฎเชพเชฐเซ‡ UI เชฎเชพเชŸเซ‡ เชธเซเชŸเซ‡เชŸ เชธเชฟเชฒเซ‡เช•เซเชŸเชฐ เชฌเชจเชพเชตเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ เช…เชจเซ‡ เชœเซเชฏเชพเชฐเซ‡ UI เชฌเชฆเชฒเชพเชฏ เชคเซเชฏเชพเชฐเซ‡ เชคเซ‡เชจเซ‡ เชธเซ‚เชšเชฟเชค เช•เชฐเชตเซเช‚ เชชเชกเชถเซ‡. เช† เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, เชšเชพเชฒเซ‹ เชเช• เชชเชฆเซเชงเชคเชฟ เช‰เชฎเซ‡เชฐเซ€เช getState ะธ reactionเช•เซ‰เชฒเชฟเช‚เช— remote.updateState:

import {action, observable, reaction} from 'mobx';
import uuid from 'uuid/v4';
import {signTx} from '@waves/waves-transactions'
import {setupDnode} from "./utils/setupDnode";
import {decrypt, encrypt} from "./utils/cryptoUtils";

export class SignerApp {

    ...

    // public
    getState() {
        return {
            keys: this.store.keys,
            messages: this.store.newMessages,
            initialized: this.store.initialized,
            locked: this.store.locked
        }
    }

    ...

    //
    connectPopup(connectionStream) {
        const api = this.popupApi();
        const dnode = setupDnode(connectionStream, api);

        dnode.once('remote', (remote) => {
            // ะกะพะทะดะฐะตะผ reaction ะฝะฐ ะธะทะผะตะฝะตะฝะธั ัั‚ะตะนั‚ะฐ, ะบะพั‚ะพั€ั‹ะน ัะดะตะปะฐะตั‚ ะฒั‹ะทะพะฒะตั‚ ัƒะดะฐะปะตะฝะฝัƒ ะฟั€ะพั†ะตะดัƒั€ัƒ ะธ ะพะฑะฝะพะฒะธั‚ ัั‚ะตะนั‚ ะฒ ui ะฟั€ะพั†ะตััะต
            const updateStateReaction = reaction(
                () => this.getState(),
                (state) => remote.updateState(state),
                // ะขั€ะตั‚ัŒะธะผ ะฐั€ะณัƒะผะตะฝั‚ะพะผ ะผะพะถะฝะพ ะฟะตั€ะตะดะฐะฒะฐั‚ัŒ ะฟะฐั€ะฐะผะตั‚ั€ั‹. fireImmediatly ะทะฝะฐั‡ะธั‚ ั‡ั‚ะพ reaction ะฒั‹ะฟะพะปะฝะธั‚ัŒัั ะฟะตั€ะฒั‹ะน ั€ะฐะท ัั€ะฐะทัƒ.
                // ะญั‚ะพ ะฝะตะพะฑั…ะพะดะธะผะพ, ั‡ั‚ะพะฑั‹ ะฟะพะปัƒั‡ะธั‚ัŒ ะฝะฐั‡ะฐะปัŒะฝะพะต ัะพัั‚ะพัะฝะธะต. Delay ะฟะพะทะฒะพะปัะตั‚ ัƒัั‚ะฐะฝะพะฒะธั‚ัŒ debounce
                {fireImmediately: true, delay: 500}
            );
            // ะฃะดะฐะปะธะผ ะฟะพะดะฟะธัะบัƒ ะฟั€ะธ ะพั‚ะบะปัŽั‡ะตะฝะธะธ ะบะปะธะตะฝั‚ะฐ
            dnode.once('end', () => updateStateReaction.dispose())

        })
    }

    ...
}

เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชชเซเชฐเชพเชชเซเชค เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ remote เชฌเชจเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ€ เชฐเชนเซ€ เช›เซ‡ reaction UI เชฌเชพเชœเซ เชชเชฐ เชซเช‚เช•เซเชถเชจเชจเซ‡ เช•เซ‰เชฒ เช•เชฐเชคเซ€ เชธเซเชฅเชฟเชคเชฟเชจเซ‡ เชฌเชฆเชฒเชตเชพ เชฎเชพเชŸเซ‡.

เช…เช‚เชคเชฟเชฎ เชธเซเชชเชฐเซเชถ เช เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เช†เชฏเช•เชจ เชชเชฐ เชจเชตเชพ เชธเช‚เชฆเซ‡เชถเชพเช“เชจเซเช‚ เชชเซเชฐเชฆเชฐเซเชถเชจ เช‰เชฎเซ‡เชฐเชตเชพเชจเซเช‚ เช›เซ‡:

function setupApp() {
...

    // Reaction ะฝะฐ ะฒั‹ัั‚ะฐะฒะปะตะฝะธะต ั‚ะตะบัั‚ะฐ ะฑะตะดะถะฐ.
    reaction(
        () => app.store.newMessages.length > 0 ? app.store.newMessages.length.toString() : '',
        text => extensionApi.browserAction.setBadgeText({text}),
        {fireImmediately: true}
    );

...
}

เชคเซ‡เชฅเซ€, เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชคเซˆเชฏเชพเชฐ เช›เซ‡. เชตเซ‡เชฌ เชชเซƒเชทเซเช เซ‹ เชตเซเชฏเชตเชนเชพเชฐเซ‹ เชฎเชพเชŸเซ‡ เชธเชนเซ€ เชฎเชพเชŸเซ‡ เชตเชฟเชจเช‚เชคเซ€ เช•เชฐเซ€ เชถเช•เซ‡ เช›เซ‡:

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เชธเซเชฐเช•เซเชทเชฟเชค เชฌเซเชฐเชพเช‰เชเชฐ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฒเช–เชตเซเช‚

เช•เซ‹เชก เช…เชนเซ€เช‚ เช‰เชชเชฒเชฌเซเชง เช›เซ‡ เช•เชกเซ€.

เชจเชฟเชทเซเช•เชฐเซเชท

เชœเซ‹ เชคเชฎเซ‡ เชฒเซ‡เช–เชจเซ‡ เช…เช‚เชค เชธเซเชงเซ€ เชตเชพเช‚เชšเซเชฏเซ‹ เชนเซ‹เชฏ, เชชเชฐเช‚เชคเซ เชนเชœเซ เชชเชฃ เชชเซเชฐเชถเซเชจเซ‹ เชนเซ‹เชฏ, เชคเซ‹ เชคเชฎเซ‡ เชคเซ‡เชฎเชจเซ‡ เช…เชนเซ€เช‚ เชชเซ‚เช›เซ€ เชถเช•เซ‹ เช›เซ‹ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชธเชพเชฅเซ‡ เชฐเซ€เชชเซ‹เชเซ€เชŸเชฐเซ€เช. เชคเซเชฏเชพเช‚ เชคเชฎเชจเซ‡ เชฆเชฐเซ‡เช• เชจเชฟเชฏเซเช•เซเชค เชชเช—เชฒเชพ เชฎเชพเชŸเซ‡ เช•เชฎเชฟเชŸ เชชเชฃ เชฎเชณเชถเซ‡.

เช…เชจเซ‡ เชœเซ‹ เชคเชฎเชจเซ‡ เชตเชพเชธเซเชคเชตเชฟเช• เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฎเชพเชŸเซ‡ เช•เซ‹เชก เชœเซ‹เชตเชพเชฎเชพเช‚ เชฐเชธ เชนเซ‹เชฏ, เชคเซ‹ เชคเชฎเซ‡ เช† เชถเซ‹เชงเซ€ เชถเช•เซ‹ เช›เซ‹ เช…เชนเซ€เช‚.

เชคเชฐเชซเชฅเซ€ เช•เซ‹เชก, เชฐเซ€เชชเซ‹เชเซ€เชŸเชฐเซ€ เช…เชจเซ‡ เชœเซ‹เชฌ เชตเชฐเซเชฃเชจ เชธเชฟเชฎเซ‡เชฐเซ‡เชฒ

เชธเซ‹เชฐเซเชธ: www.habr.com

DDoS เชธเซเชฐเช•เซเชทเชพ, VPS VDS เชธเชฐเซเชตเชฐ เชงเชฐเชพเชตเชคเซ€ เชธเชพเช‡เชŸเซเชธ เชฎเชพเชŸเซ‡ เชตเชฟเชถเซเชตเชธเชจเซ€เชฏ เชนเซ‹เชธเซเชŸเชฟเช‚เช— เช–เชฐเซ€เชฆเซ‹ ๐Ÿ”ฅ DDoS เชธเซเชฐเช•เซเชทเชพ, VPS VDS เชธเชฐเซเชตเชฐเซเชธ เชธเชพเชฅเซ‡ เชตเชฟเชถเซเชตเชธเชจเซ€เชฏ เชตเซ‡เชฌเชธเชพเช‡เชŸ เชนเซ‹เชธเซเชŸเชฟเช‚เช— เช–เชฐเซ€เชฆเซ‹ | ProHoster