เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบšเปเปˆเบ„เบทเบเบฑเบšเบชเบฐเบ–เบฒเบ›เบฑเบ”เบ•เบฐเบเบฐเบเบณ "เบฅเบนเบเบ„เป‰เบฒ-เป€เบŠเบตเบšเป€เบงเบต" เบ—เบปเปˆเบงเป„เบ›, เปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเปˆเบ™เบ—เบตเปˆเบกเบตเบเบฒเบ™เปเบšเปˆเบ‡เบ‚เบฑเป‰เบ™เบ„เบธเป‰เบกเบ„เบญเบ‡เปเบกเปˆเบ™เบกเบตเบฅเบฑเบเบชเบฐเบ™เบฐ:

  • เบšเปเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบ–เบฒเบ™เบ‚เปเป‰เบกเบนเบ™เบ—เบตเปˆเบกเบตเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเบชเบนเปˆเบฅเบฐเบšเบปเบšเบ‚เบญเบ‡เบœเบนเป‰เปƒเบŠเป‰เปเบฅเบฐเบฅเบฐเบซเบฑเบ”เบœเปˆเบฒเบ™. เบ‚เปเป‰เบกเบนเบ™เบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เปเบกเปˆเบ™เบ–เบทเบเป€เบเบฑเบšเบฎเบฑเบเบชเบฒเป„เบงเป‰เบชเบฐเป€เบžเบฒเบฐเป‚เบ”เบเบœเบนเป‰เปƒเบŠเป‰เป€เบญเบ‡, เปเบฅเบฐเบเบฒเบ™เบขเบทเบ™เบขเบฑเบ™เบ‚เบญเบ‡เบ„เบงเบฒเบกเบ–เบทเบเบ•เป‰เบญเบ‡เบ‚เบญเบ‡เบžเบงเบเบกเบฑเบ™เป€เบเบตเบ”เบ‚เบทเป‰เบ™เปƒเบ™เบฅเบฐเบ”เบฑเบšเป‚เบ›เป‚เบ•เบ„เบญเบ™.
  • เบšเปเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เปƒเบŠเป‰เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ. เป€เบซเบ”เบœเบปเบ™เบ‚เบญเบ‡เบ„เปเบฒเบฎเป‰เบญเบ‡เบชเบฐเบซเบกเบฑเบเบชเบฒเบกเบฒเบ”เบ–เบทเบเบ›เบฐเบ•เบดเบšเบฑเบ”เบขเบนเปˆเปƒเบ™เป€เบ„เบทเบญเบ‚เปˆเบฒเบ blockchain, เบšเปˆเบญเบ™เบ—เบตเปˆเบกเบฑเบ™เป€เบ›เบฑเบ™เป„เบ›เป„เบ”เป‰เบ—เบตเปˆเบˆเบฐเป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบˆเปเบฒเบ™เบงเบ™เบ‚เปเป‰เบกเบนเบ™เบ—เบตเปˆเบ•เป‰เบญเบ‡เบเบฒเบ™.

เบกเบต 2 เบเบฒเบ™เป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบ—เบตเปˆเบ‚เป‰เบญเบ™เบ‚เป‰เบฒเบ‡เบ›เบญเบ”เป„เบžเบชเปเบฒเบฅเบฑเบšเบฅเบฐเบซเบฑเบ”เบœเบนเป‰เปƒเบŠเป‰ - เบฎเบฒเบ”เปเบง wallets เปเบฅเบฐเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš. เบฎเบฒเบ”เปเบง wallets เบชเปˆเบงเบ™เบซเบผเบฒเบเปเบกเปˆเบ™เบ›เบญเบ”เป„เบžเบ—เบตเปˆเบชเบธเบ”, เปเบ•เปˆเบเบฒเบเบ—เบตเปˆเบˆเบฐเปƒเบŠเป‰เปเบฅเบฐเบขเบนเปˆเป„เบเบˆเบฒเบเบšเปเปˆเป€เบชเบเบ„เปˆเบฒ, เปเบ•เปˆเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเปเบกเปˆเบ™เบเบฒเบ™เบ›เบฐเบชเบปเบกเบ›เบฐเบชเบฒเบ™เบ—เบตเปˆเบชเบปเบกเบšเบนเบ™เปเบšเบšเบ‚เบญเบ‡เบ„เบงเบฒเบกเบ›เบญเบ”เป„เบžเปเบฅเบฐเบ„เบงเบฒเบกเบ‡เปˆเบฒเบเปƒเบ™เบเบฒเบ™เบ™เปเบฒเปƒเบŠเป‰, เปเบฅเบฐเบเบฑเบ‡เบชเบฒเบกเบฒเบ”เบšเปเปˆเป€เบชเบเบ„เปˆเบฒเบชเปเบฒเบฅเบฑเบšเบœเบนเป‰เปƒเบŠเป‰เบชเบธเบ”เบ—เป‰เบฒเบ.

เป‚เบ”เบเบžเบดเบˆเบฒเบฅเบฐเบ™เบฒเบ—เบฑเบ‡เบซเบกเบปเบ”เบ™เบตเป‰, เบžเบงเบเป€เบฎเบปเบฒเบ•เป‰เบญเบ‡เบเบฒเบ™เบ—เบตเปˆเบˆเบฐเป€เบฎเบฑเบ”เปƒเบซเป‰เบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเบ—เบตเปˆเบ›เบญเบ”เป„เบžเบ—เบตเปˆเบชเบธเบ”เบ—เบตเปˆเป€เบฎเบฑเบ”เปƒเบซเป‰เบกเบฑเบ™เบ‡เปˆเบฒเบเบ‚เบถเป‰เบ™เปƒเบ™เบเบฒเบ™เบžเบฑเบ”เบ—เบฐเบ™เบฒเบ„เปเบฒเบฎเป‰เบญเบ‡เบชเบฐเบซเบกเบฑเบเบเบฒเบ™เปเบšเปˆเบ‡เบ‚เบฑเป‰เบ™เบ„เบธเป‰เบกเบ„เบญเบ‡เป‚เบ”เบเบเบฒเบ™เบชเบฐเบซเบ™เบญเบ‡ API เบ‡เปˆเบฒเบเบ”เบฒเบเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเบ—เบธเบฅเบฐเบเปเบฒเปเบฅเบฐเบฅเบฒเบเป€เบŠเบฑเบ™.
เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบšเบญเบเบ—เปˆเบฒเบ™เบเปˆเบฝเบงเบเบฑเบšเบ›เบฐเบชเบปเบšเบเบฒเบ™เบ™เบตเป‰เบ‚เป‰เบฒเบ‡เบฅเบธเปˆเบกเบ™เบตเป‰.

เบšเบปเบ”เบ„เบงเบฒเบกเบˆเบฐเบ›เบฐเบเบญเบšเบ”เป‰เบงเบเบ„เปเบฒเปเบ™เบฐเบ™เปเบฒเบ‚เบฑเป‰เบ™เบ•เบญเบ™เป‚เบ”เบเบ‚เบฑเป‰เบ™เบ•เบญเบ™เบเปˆเบฝเบงเบเบฑเบšเบงเบดเบ—เบตเบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš, เบกเบตเบ•เบปเบงเบขเปˆเบฒเบ‡เบฅเบฐเบซเบฑเบ”เปเบฅเบฐ screenshots. เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบŠเบญเบเบซเบฒเบฅเบฐเบซเบฑเบ”เบ—เบฑเบ‡เบซเบกเบปเบ”เปƒเบ™ เบ„เบฑเบ‡เป€เบเบฑเบšเบกเป‰เบฝเบ™. เปเบ•เปˆเบฅเบฐเบ„เปเบฒเบซเบกเบฑเป‰เบ™เบชเบฑเบ™เบเบฒเบขเปˆเบฒเบ‡เบกเบตเป€เบซเบ”เบœเบปเบ™เบเบปเบ‡เบเบฑเบšเบžเบฒเบเบชเปˆเบงเบ™เบ‚เบญเบ‡เบšเบปเบ”เบ„เบงเบฒเบกเบ™เบตเป‰.

เบ›เบฐเบซเบงเบฑเบ”เบซเบเปเป‰เบ‚เบญเบ‡เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš

เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบกเบตเบกเบฒเป€เบ›เบฑเบ™เป€เบงเบฅเบฒเบ”เบปเบ™เปเบฅเป‰เบง. เบžเบงเบเบกเบฑเบ™เบ›เบฒเบเบปเบ”เบขเบนเปˆเปƒเบ™ Internet Explorer เปƒเบ™เบ›เบต 1999, เปƒเบ™ Firefox เปƒเบ™เบ›เบต 2004. เบขเปˆเบฒเบ‡เปƒเบ”เบเปเบ•เบฒเบก, เบชเปเบฒเบฅเบฑเบšเป€เบงเบฅเบฒเบ”เบปเบ™เบ™เบฒเบ™เบšเปเปˆเบกเบตเบกเบฒเบ”เบ•เบฐเบ–เบฒเบ™เบ”เบฝเบงเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบ.

เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เป€เบงเบปเป‰เบฒเป„เบ”เป‰เบงเปˆเบฒเบกเบฑเบ™เบ›เบฒเบเบปเบ”เบžเป‰เบญเบกเบเบฑเบšเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเปƒเบ™ Google Chrome เบฎเบธเปˆเบ™เบ—เบตเบชเบตเปˆ. เปเบ™เปˆเบ™เบญเบ™, เบšเปเปˆเบกเบตเบ‚เปเป‰เบกเบนเบ™เบชเบฐเป€เบžเบฒเบฐเบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™, เปเบ•เปˆเบกเบฑเบ™เปเบกเปˆเบ™ Chrome API เบ—เบตเปˆเบเบฒเบเป€เบ›เบฑเบ™เบžเบทเป‰เบ™เบ–เบฒเบ™เบ‚เบญเบ‡เบกเบฑเบ™: เป‚เบ”เบเป„เบ”เป‰เป€เบญเบปเบฒเบŠเบฐเบ™เบฐเบ•เบฐเบซเบผเบฒเบ”เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบชเปˆเบงเบ™เปƒเบซเบเปˆเปเบฅเบฐเบกเบตเบฎเป‰เบฒเบ™เปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™, Chrome เบ•เบปเบงเบˆเบดเบ‡เปเบฅเป‰เบงเป„เบ”เป‰เบเปเบฒเบ™เบปเบ”เบกเบฒเบ”เบ•เบฐเบ–เบฒเบ™เบชเปเบฒเบฅเบฑเบšเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš.

Mozilla เบกเบตเบกเบฒเบ”เบ•เบฐเบ–เบฒเบ™เบ‚เบญเบ‡เบ•เบปเบ™เป€เบญเบ‡, เปเบ•เปˆเป€เบซเบฑเบ™เบ„เบงเบฒเบกเบ™เบดเบเบปเบกเบ‚เบญเบ‡เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบ Chrome, เบšเปเบฅเบดเบชเบฑเบ”เป„เบ”เป‰เบ•เบฑเบ”เบชเบดเบ™เปƒเบˆเบชเป‰เบฒเบ‡ API เบ—เบตเปˆเป€เบ‚เบปเป‰เบฒเบเบฑเบ™เป„เบ”เป‰. เปƒเบ™เบ›เบต 2015, เปƒเบ™เบเบฒเบ™เบฅเบดเป€เบฅเบตเปˆเบกเบ‚เบญเบ‡ Mozilla, เบเบธเปˆเบกเบžเบดเป€เบชเบ”เป„เบ”เป‰เบ–เบทเบเบชเป‰เบฒเบ‡เบ•เบฑเป‰เบ‡เบ‚เบทเป‰เบ™เบžเบฒเบเปƒเบ™ World Wide Web Consortium (W3C) เป€เบžเบทเปˆเบญเป€เบฎเบฑเบ”เบงเบฝเบเบเปˆเบฝเบงเบเบฑเบšเบ‚เปเป‰เบเปเบฒเบ™เบปเบ”เบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เป‰เบฒเบกเบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš.

เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบ API เบ—เบตเปˆเบกเบตเบขเบนเปˆเบชเปเบฒเบฅเบฑเบš Chrome เป„เบ”เป‰เบ–เบทเบเบ›เบฐเบ•เบดเบšเบฑเบ”เป€เบ›เบฑเบ™เบžเบทเป‰เบ™เบ–เบฒเบ™. เบงเบฝเบเบ‡เบฒเบ™เบ”เบฑเปˆเบ‡เบเปˆเบฒเบงเป„เบ”เป‰เบ–เบทเบเบ›เบฐเบ•เบดเบšเบฑเบ”เป‚เบ”เบเบเบฒเบ™เบชเบฐเบซเบ™เบฑเบšเบชเบฐเบซเบ™เบนเบ™เบ‚เบญเบ‡ Microsoft (Google เบ›เบฐเบ•เบดเป€เบชเบ”เบ—เบตเปˆเบˆเบฐเป€เบ‚เบปเป‰เบฒเบฎเปˆเบงเบกเปƒเบ™เบเบฒเบ™เบžเบฑเบ”เบ—เบฐเบ™เบฒเบกเบฒเบ”เบ•เบฐเบ–เบฒเบ™), เปเบฅเบฐเบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เบชเบฐเบšเบฑเบšเบฎเปˆเบฒเบ‡เป„เบ”เป‰เบ›เบฒเบเบปเบ”เบ‚เบถเป‰เบ™. เบ‚เปเป‰เบชเบฐเป€เบžเบฒเบฐ.

เบขเปˆเบฒเบ‡เป€เบ›เบฑเบ™เบ—เบฒเบ‡เบเบฒเบ™, เบ‚เปเป‰เบกเบนเบ™เบชเบฐเป€เบžเบฒเบฐเปเบกเปˆเบ™เบชเบฐเบซเบ™เบฑเบšเบชเบฐเบซเบ™เบนเบ™เป‚เบ”เบ Edge, Firefox เปเบฅเบฐ Opera (เบชเบฑเบ‡เป€เบเบ”เบงเปˆเบฒ Chrome เบšเปเปˆเบขเบนเปˆเปƒเบ™เบšเบฑเบ™เบŠเบตเบฅเบฒเบเบŠเบทเปˆเบ™เบตเป‰). เปเบ•เปˆเปƒเบ™เบ„เบงเบฒเบกเป€เบ›เบฑเบ™เบˆเบดเบ‡, เบกเบฒเบ”เบ•เบฐเบ–เบฒเบ™เปเบกเปˆเบ™เป€เบซเบกเบฒเบฐเบชเบปเบกเบเบฑเบš Chrome เบชเปˆเบงเบ™เปƒเบซเบเปˆ, เป€เบžเบฒเบฐเบงเปˆเบฒเบกเบฑเบ™เบ–เบทเบเบ‚เบฝเบ™เป‚เบ”เบเบญเบตเบ‡เปƒเบชเปˆเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบกเบฑเบ™. เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบญเปˆเบฒเบ™เป€เบžเบตเปˆเบกเป€เบ•เบตเบกเบเปˆเบฝเบงเบเบฑเบš WebExtensions API เบ—เบตเปˆเบ™เบตเป‰.

เป‚เบ„เบ‡เบ›เบฐเบเบญเบšเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบ

เป„เบŸเบฅเปŒเบ”เบฝเบงเบ—เบตเปˆเบ•เป‰เบญเบ‡เบเบฒเบ™เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเปเบกเปˆเบ™ manifest (manifest.json). เบกเบฑเบ™เบเบฑเบ‡เป€เบ›เบฑเบ™ "เบˆเบธเบ”เป€เบ‚เบปเป‰เบฒ" เปƒเบ™เบเบฒเบ™เบ‚เบฐเบซเบเบฒเบ.

เบเบฒเบ™เบชเบฐเปเบ”เบ‡

เบญเบตเบ‡เบ•เบฒเบกเบ‚เปเป‰เบกเบนเบ™เบชเบฐเป€เบžเบฒเบฐ, เป„เบŸเบฅเปŒ manifest เปเบกเปˆเบ™เป„เบŸเบฅเปŒ JSON เบ—เบตเปˆเบ–เบทเบเบ•เป‰เบญเบ‡. เบฅเบฒเบโ€‹เบฅเบฐโ€‹เบญเบฝเบ”โ€‹เป€เบ•เบฑเบกโ€‹เบ—เบตเปˆโ€‹เบ‚เบญเบ‡โ€‹เบเบฐโ€‹เปเบˆ manifest เบ—เบตเปˆโ€‹เบกเบตโ€‹เบ‚เปเป‰โ€‹เบกเบนเบ™โ€‹เบเปˆเบฝเบงโ€‹เบเบฑเบšโ€‹เบเบฒเบ™โ€‹เบ—เบตเปˆโ€‹เบเบฐโ€‹เปเบˆโ€‹เบ—เบตเปˆโ€‹เบ–เบทเบโ€‹เบฎเบญเบ‡โ€‹เบฎเบฑเบšโ€‹เปƒเบ™โ€‹เบ—เบตเปˆโ€‹เบ•เบปเบงโ€‹เบ—เปˆเบญเบ‡โ€‹เป€เบงเบฑเบšโ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เป€เบšเบดเปˆเบ‡โ€‹เป„เบ”เป‰โ€‹ เบ—เบตเปˆเบ™เบตเป‰.

เบเบฐเปเบˆเบ—เบตเปˆเบšเปเปˆเบขเบนเปˆเปƒเบ™เบชเบฐเป€เบžเบฒเบฐ "เบญเบฒเบ”เบˆเบฐ" เบ–เบทเบเบฅเบฐเป€เบฅเบตเบ (เบ—เบฑเบ‡ Chrome เปเบฅเบฐ Firefox เบฅเบฒเบเบ‡เบฒเบ™เบ„เบงเบฒเบกเบœเบดเบ”เบžเบฒเบ”, เปเบ•เปˆเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบเบฑเบ‡เบชเบทเบšเบ•เปเปˆเป€เบฎเบฑเบ”เบงเบฝเบ).

เปเบฅเบฐเบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒเบขเบฒเบเบˆเบฐเบ”เบถเบ‡เบ”เบนเบ”เป€เบญเบปเบฒเบšเบฒเบ‡เบˆเบธเบ”.

  1. เบ„เบงเบฒเบกเป€เบ›เบฑเบ™เบกเบฒ - เบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบ›เบฐเบเบญเบšเบกเบตเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบ•เปเปˆเป„เบ›เบ™เบตเป‰:
    1. scripts โ€” array เบ‚เบญเบ‡ scripts เบ—เบตเปˆเบˆเบฐเบ–เบทเบเบ›เบฐเบ•เบดเบšเบฑเบ”เปƒเบ™เบชเบฐเบžเบฒเบšเบžเบทเป‰เบ™เบ–เบฒเบ™ (เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบงเบปเป‰เบฒเบเปˆเบฝเบงเบเบฑเบšเป€เบฅเบทเปˆเบญเบ‡เบ™เบตเป‰เบ•เปเปˆเบกเบฒเป€เบฅเบฑเบเบ™เป‰เบญเบ);
    2. Page - เปเบ—เบ™เบ—เบตเปˆเบˆเบฐเป€เบ›เบฑเบ™ scripts เบ—เบตเปˆเบˆเบฐเบ–เบทเบเบ›เบฐเบ•เบดเบšเบฑเบ”เปƒเบ™เบซเบ™เป‰เบฒเป€เบ›เบปเปˆเบฒ, เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบเปเบฒเบ™เบปเบ” html เบเบฑเบšเป€เบ™เบทเป‰เบญเบซเบฒ. เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ™เบตเป‰, เบŠเปˆเบญเบ‡เบชเบฐเบ„เบฃเบดเบšเบˆเบฐเบ–เบทเบเบฅเบฐเป€เบฅเบตเบ, เปเบฅเบฐ scripts เบˆเบฐเบ•เป‰เบญเบ‡เบ–เบทเบเปƒเบชเปˆเป€เบ‚เบปเป‰เบฒเป„เบ›เปƒเบ™เบซเบ™เป‰เบฒเป€เบ™เบทเป‰เบญเบซเบฒ;
    3. เบขเบนเปˆ โ€” เบ—เบธเบ‡เบ„เบนเปˆ, เบ–เป‰เบฒเบšเปเปˆเบฅเบฐเบšเบธ, เบšเบฃเบฒเบงเป€เบŠเบตเบˆเบฐ โ€œเบ‚เป‰เบฒโ€ เบ‚เบฐเบšเบงเบ™เบเบฒเบ™เบžเบทเป‰เบ™เบซเบผเบฑเบ‡เป€เบกเบทเปˆเบญเบกเบฑเบ™เบžเบดเบˆเบฒเบฅเบฐเบ™เบฒเบงเปˆเบฒเบกเบฑเบ™เบšเปเปˆเป„เบ”เป‰เป€เบฎเบฑเบ”เบซเบเบฑเบ‡, เปเบฅเบฐเป€เบ›เบตเบ”เบกเบฑเบ™เบ„เบทเบ™เปƒเปเปˆเบ–เป‰เบฒเบˆเบณเป€เบ›เบฑเบ™. เบ–เป‰เบฒเบšเปเปˆเบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เบซเบ™เป‰เบฒเป€เบงเบฑเบšเบˆเบฐเบ–เบทเบเบเบปเบเป€เบฅเบตเบเป€เบกเบทเปˆเบญเบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ–เบทเบเบ›เบดเบ”. เบšเปเปˆเบฎเบญเบ‡เบฎเบฑเบšเปƒเบ™ Firefox.
  2. content_scripts โ€” array เบ‚เบญเบ‡เบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบญเบฐเบ™เบธเบเบฒเบ”เปƒเบซเป‰เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เป‚เบซเบฅเบ” scripts เบ—เบตเปˆเปเบ•เบเบ•เปˆเบฒเบ‡เบเบฑเบ™เป„เบ›เบซเบฒเบซเบ™เป‰เบฒเป€เบงเบฑเบšเบ•เปˆเบฒเบ‡เป†. เปเบ•เปˆเบฅเบฐเบงเบฑเบ”เบ–เบธเบกเบตเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบชเบณเบ„เบฑเบ™เบ•เปเปˆเป„เบ›เบ™เบตเป‰:
    1. เบเบปเบ‡เบเบฑเบ™ - url เบฎเบนเบšเปเบšเบš, เป€เบŠเบดเปˆเบ‡เบเปเบฒเบ™เบปเบ”เบงเปˆเบฒเบชเบฐเบ„เบดเบšเป€เบ™เบทเป‰เบญเบซเบฒเป‚เบ”เบเบชเบฐเป€เบžเบฒเบฐเบˆเบฐเบ–เบทเบเบฅเบงเบกเป€เบ‚เบปเป‰เบฒเบซเบผเบทเบšเปเปˆ.
    2. js โ€” เบšเบฑเบ™โ€‹เบŠเบตโ€‹เบฅเบฒเบโ€‹เบŠเบทเปˆโ€‹เบ‚เบญเบ‡ scripts เบ—เบตเปˆโ€‹เบˆเบฐโ€‹เป„เบ”เป‰โ€‹เบฎเบฑเบšโ€‹เบเบฒเบ™โ€‹เป‚เบซเบผเบ”โ€‹เป€เบ‚เบปเป‰เบฒโ€‹เป„เบ›โ€‹เปƒเบ™โ€‹เบเบฒเบ™โ€‹เปเบ‚เปˆเบ‡โ€‹เบ‚เบฑเบ™โ€‹เบ™เบตเป‰โ€‹;
    3. exclude_match - เบšเปเปˆเบฅเบงเบกเบˆเบฒเบเบžเบฒเบเบชเบฐเบซเบ™เบฒเบก match URL เบ—เบตเปˆเบเบปเบ‡เบเบฑเบšเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบ™เบตเป‰.
  3. page_action - เบ•เบปเบงเบˆเบดเบ‡เปเบฅเป‰เบงเปเบกเปˆเบ™เบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบฎเบฑเบšเบœเบดเบ”เบŠเบญเบšเบชเปเบฒเบฅเบฑเบšเป„เบญเบ„เบญเบ™เบ—เบตเปˆเบชเบฐเปเบ”เบ‡เบขเบนเปˆเบ‚เป‰เบฒเบ‡เปเบ–เบšเบ—เบตเปˆเบขเบนเปˆเปƒเบ™เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเปเบฅเบฐเบ›เบฐเบ•เบดเบชเปเบฒเบžเบฑเบ™เบเบฑเบšเบกเบฑเบ™. เบกเบฑเบ™เบเบฑเบ‡เบญเบฐเบ™เบธเบเบฒเบ”เปƒเบซเป‰เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบชเบฐเปเบ”เบ‡เบ›เปˆเบญเบ‡เบขเป‰เบฝเบกเบ›เปŠเบญเบšเบญเบฑเบš, เป€เบŠเบดเปˆเบ‡เบ–เบทเบเบเปเบฒเบ™เบปเบ”เป‚เบ”เบเปƒเบŠเป‰ HTML, CSS เปเบฅเบฐ JS เบ‚เบญเบ‡เบ—เปˆเบฒเบ™เป€เบญเบ‡.
    1. default_popup โ€” เป€เบชเบฑเป‰เบ™เบ—เบฒเบ‡เป„เบ›เบซเบฒเป„เบŸเบฅเปŒ HTML เบ—เบตเปˆเบกเบตเบเบฒเบ™เป‚เบ•เป‰เบ•เบญเบšเบ›เปŠเบญเบšเบญเบฑเบš, เบญเบฒเบ”เบˆเบฐเบกเบต CSS เปเบฅเบฐ JS.
  4. เบเบฒเบ™เบญเบฐเบ™เบธเบเบฒเบ” โ€” array เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบ„เบธเป‰เบกเบ„เบญเบ‡เบชเบดเบ”เบ—เบดเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบ. เบกเบต 3 เบ›เบฐเป€เบžเบ”เบ‚เบญเบ‡เบชเบดเบ”เบ—เบด, เป€เบŠเบดเปˆเบ‡เป„เบ”เป‰เบญเบฐเบ—เบดเบšเบฒเบเบขเปˆเบฒเบ‡เบฅเบฐเบญเบฝเบ” เบ—เบตเปˆเบ™เบตเป‰
  5. web_accessible_resources โ€” เบŠเบฑเบšเบžเบฐเบเบฒเบเบญเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ—เบตเปˆเบซเบ™เป‰เบฒเป€เบงเบฑเบšเบชเบฒเบกเบฒเบ”เบฎเป‰เบญเบ‡เบ‚เปเป„เบ”เป‰, เบ•เบปเบงเบขเปˆเบฒเบ‡, เบฎเบนเบšเบžเบฒเบš, JS, CSS, HTML.
  6. externally_connectable โ€” เปƒเบ™โ€‹เบ—เบตเปˆโ€‹เบ™เบตเป‰โ€‹เบ—เปˆเบฒเบ™โ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เบฅเบฐโ€‹เบšเบธโ€‹เบขเปˆเบฒเบ‡โ€‹เบŠเบฑเบ”โ€‹เป€เบˆเบ™ IDs เบ‚เบญเบ‡โ€‹เบเบฒเบ™โ€‹เบ‚เบฐโ€‹เบซเบเบฒเบโ€‹เบญเบทเปˆเบ™เป†โ€‹เปเบฅเบฐโ€‹เป‚เบ”โ€‹เป€เบกเบ™โ€‹เบ‚เบญเบ‡โ€‹เบซเบ™เป‰เบฒโ€‹เป€เบงเบฑเบšโ€‹เบ—เบตเปˆโ€‹เบ—เปˆเบฒเบ™โ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เป€เบŠเบทเปˆเบญเบกโ€‹เบ•เปเปˆโ€‹. เป‚เบ”เป€เบกเบ™เบชเบฒเบกเบฒเบ”เป€เบ›เบฑเบ™เบฅเบฐเบ”เบฑเบšเบ—เบตเบชเบญเบ‡เบซเบผเบทเบชเบนเบ‡เบเบงเปˆเบฒ. เปƒเบŠเป‰เบšเปเปˆเป„เบ”เป‰เปƒเบ™ Firefox.

เบšเปเบฅเบดเบšเบปเบ”เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”

เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบกเบตเบชเบฒเบกเบชเบฐเบžเบฒเบšเบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบฅเบฐเบซเบฑเบ”, เบ™เบฑเป‰เบ™เปเบกเปˆเบ™, เปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™เบ›เบฐเบเบญเบšเบ”เป‰เบงเบเบชเบฒเบกเบชเปˆเบงเบ™เบ—เบตเปˆเบกเบตเบฅเบฐเบ”เบฑเบšเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡ API เบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš.

เบšเปเบฅเบดเบšเบปเบ”เบเบฒเบ™เบ‚เบฐเบซเบเบฒเบ

เบชเปˆเบงเบ™เปƒเบซเบเปˆเบ‚เบญเบ‡ API เปเบกเปˆเบ™เบกเบตเบขเบนเปˆเบ—เบตเปˆเบ™เบตเป‰. เปƒเบ™เบชเบฐเบžเบฒเบšเบเบฒเบ™เบ™เบตเป‰เบžเบงเบเป€เบ‚เบปเบฒ "เบ”เปเบฒเบฅเบปเบ‡เบŠเบตเบงเบดเบ”":

  1. เปœเป‰เบฒเบžเบทเป‰เบ™เบซเบผเบฑเบ‡ - เบชเปˆเบงเบ™ "backend" เบ‚เบญเบ‡เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบ. เป„เบŸเบฅเปŒเบ–เบทเบเบฅเบฐเบšเบธเป„เบงเป‰เปƒเบ™ manifest เป‚เบ”เบเปƒเบŠเป‰เบ›เบธเปˆเบก "เบžเบทเป‰เบ™เบซเบผเบฑเบ‡".
  2. เปœเป‰เบฒเบ›เบฑเบญเบšเบญเบฑเบš โ€” เปœเป‰เบฒเบ›เบฑเบญเบšเบญเบฑเบšเบ—เบตเปˆเบ›เบฒเบเบปเบ”เบ‚เบถเป‰เบ™เป€เบกเบทเปˆเบญเบ—เปˆเบฒเบ™เบ„เบฅเบดเบเปƒเบชเปˆเป„เบญเบ„เบญเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบ. เปƒเบ™ manifesto เป„เบ”เป‰ browser_action -> default_popup.
  3. เปœเป‰เบฒเบเบณเบ™เบปเบ”เป€เบญเบ‡ - เบซเบ™เป‰เบฒเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบ, "เบ”เปเบฒเบฅเบปเบ‡เบŠเบตเบงเบดเบ”" เปƒเบ™เปเบ–เบšเปเบเบเบ•เปˆเบฒเบ‡เบซเบฒเบเบ‚เบญเบ‡เบกเบธเบกเป€เบšเบดเปˆเบ‡ chrome-extension://<id_ั€ะฐััˆะธั€ะตะฝะธั>/customPage.html.

เบšเปเบฅเบดเบšเบปเบ”เบ™เบตเป‰เบกเบตเบขเบนเปˆเป€เบ›เบฑเบ™เป€เบญเบเบฐเบฅเบฒเบ”เบˆเบฒเบเปœเป‰เบฒเบ•เปˆเบฒเบ‡ เปเบฅเบฐเปเบ–เบšเบ‚เบญเบ‡เบšเบฃเบฒเบงเป€เบŠเบต. เปœเป‰เบฒเบžเบทเป‰เบ™เบซเบผเบฑเบ‡ เบกเบตเบขเบนเปˆเปƒเบ™เบชเปเบฒเป€เบ™เบปเบฒเบ”เบฝเบงเปเบฅเบฐเป€เบฎเบฑเบ”เบงเบฝเบเบชเบฐเป€เบซเบกเบต (เบ‚เปเป‰เบเบปเบเป€เบงเบฑเป‰เบ™เปเบกเปˆเบ™เบซเบ™เป‰เบฒเป€เบซเบ”เบเบฒเบ™, เป€เบกเบทเปˆเบญเบชเบฐเบ„เบดเบšเบžเบทเป‰เบ™เบซเบฅเบฑเบ‡เบ–เบทเบเป€เบ›เบตเบ”เบ•เบปเบงเป‚เบ”เบเป€เบซเบ”เบเบฒเบ™เปเบฅเบฐ "เบ•เบฒเบ" เบซเบผเบฑเบ‡เบˆเบฒเบเบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ‚เบญเบ‡เบกเบฑเบ™). เปœเป‰เบฒเบ›เบฑเบญเบšเบญเบฑเบš เบกเบตเบขเบนเปˆเปƒเบ™เป€เบงเบฅเบฒเบ—เบตเปˆเบ›เปˆเบญเบ‡เบขเป‰เบฝเบกเบ›เปŠเบญเบšเบญเบฑเบšเป€เบ›เบตเบ”, เปเบฅเบฐ เปœเป‰เบฒเบเบณเบ™เบปเบ”เป€เบญเบ‡ โ€” เปƒเบ™เบ‚เบฐเบ™เบฐเบ—เบตเปˆเปเบ–เบšเบ—เบตเปˆเบกเบตเบกเบฑเบ™เป€เบ›เบตเบ”เบขเบนเปˆ. เบšเปเปˆเบกเบตเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เปเบ–เบšเบญเบทเปˆเบ™ เปเบฅเบฐเป€เบ™เบทเป‰เบญเบซเบฒเบ‚เบญเบ‡เบžเบงเบเบกเบฑเบ™เบˆเบฒเบเบšเปเบฅเบดเบšเบปเบ”เบ™เบตเป‰.

เบชเบฐเบ„เบฃเบดเบšเป€เบ™เบทเป‰เบญเบซเบฒ

เป„เบŸเบฅเปŒ script เป€เบ™เบทเป‰เบญเบซเบฒเบ–เบทเบเป€เบ›เบตเบ”เบ•เบปเบงเบžเป‰เบญเบกเบเบฑเบšเปเบ•เปˆเบฅเบฐเปเบ–เบšเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš. เบกเบฑเบ™เบกเบตเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบชเปˆเบงเบ™เบซเบ™เบถเปˆเบ‡เบ‚เบญเบ‡ API เบ‚เบญเบ‡เบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเปเบฅเบฐเบ•เบปเป‰เบ™เป„เบกเป‰ DOM เบ‚เบญเบ‡เบซเบ™เป‰เบฒเป€เบงเบฑเบš. เบกเบฑเบ™เป€เบ›เบฑเบ™ scripts เป€เบ™เบทเป‰เบญเปƒเบ™เบ—เบตเปˆเบฎเบฑเบšเบœเบดเบ”เบŠเบญเบšเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบžเบปเบงเบžเบฑเบ™เบเบฑเบšเบซเบ™เป‰เบฒ. เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ—เบตเปˆ manipulate เบ•เบปเป‰เบ™เป„เบกเป‰ DOM เป€เบฎเบฑเบ”เบชเบดเปˆเบ‡เบ™เบตเป‰เปƒเบ™เบชเบฐเบ„เบดเบšเป€เบ™เบทเป‰เบญเบซเบฒ - เบ•เบปเบงเบขเปˆเบฒเบ‡, เบ•เบปเบงเบชเบฐเบเบฑเบ”เป‚เบ„เบชเบฐเบ™เบฒเบซเบผเบทเบ™เบฑเบเปเบ›. เบ™เบญเบเบˆเบฒเบเบ™เบตเป‰, script เป€เบ™เบทเป‰เบญเบซเบฒเบชเบฒเบกเบฒเบ”เบชเบทเปˆเบชเบฒเบ™เบเบฑเบšเบซเบ™เป‰เบฒเป‚เบ”เบเบœเปˆเบฒเบ™เบกเบฒเบ”เบ•เบฐเบ–เบฒเบ™ postMessage.

เบšเปเบฅเบดเบšเบปเบ”เบ‚เบญเบ‡เบซเบ™เป‰เบฒเป€เบงเบฑเบš

เบ™เบตเป‰เปเบกเปˆเบ™เบซเบ™เป‰เบฒเป€เบงเบฑเบšเบ•เบปเบงเบˆเบดเบ‡เบ‚เบญเบ‡เบกเบฑเบ™เป€เบญเบ‡. เบกเบฑเบ™เบšเปเปˆเบกเบตเบซเบเบฑเบ‡เบเปˆเบฝเบงเบ‚เป‰เบญเบ‡เบเบฑเบšเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเปเบฅเบฐเบšเปเปˆเบกเบตเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบ—เบตเปˆเบ™เบฑเป‰เบ™, เบเบปเบเป€เบงเบฑเป‰เบ™เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ—เบตเปˆเป‚เบ”เป€เบกเบ™เบ‚เบญเบ‡เบซเบ™เป‰เบฒเบ™เบตเป‰เบšเปเปˆเป„เบ”เป‰เบฅเบฐเบšเบธเบขเปˆเบฒเบ‡เบŠเบฑเบ”เป€เบˆเบ™เปƒเบ™ manifest (เป€เบžเบตเปˆเบกเป€เบ•เบตเบกเบขเบนเปˆเบ‚เป‰เบฒเบ‡เบฅเบธเปˆเบกเบ™เบตเป‰).

เบเบฒเบ™เปเบฅเบเบ›เปˆเบฝเบ™เบ‚เปเป‰เบ„เบงเบฒเบก

เบžเบฒเบเบชเปˆเบงเบ™เบ•เปˆเบฒเบ‡เป†เบ‚เบญเบ‡เปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™เบ•เป‰เบญเบ‡เปเบฅเบเบ›เปˆเบฝเบ™เบ‚เปเป‰เบ„เบงเบฒเบกเป€เบŠเบดเปˆเบ‡เบเบฑเบ™เปเบฅเบฐเบเบฑเบ™. เบกเบต 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 เปเบกเปˆเบ™เบˆเปเบฒเป€เบ›เบฑเบ™เปƒเบ™เบ—เบธเบเป‚เบ”เป€เบกเบ™, เปเบฅเบฐเบžเบฝเบ‡เปเบ•เปˆเบซเบ™เบถเปˆเบ‡เบชเบฐเป€เบžเบฒเบฐเบ—เบตเปˆเบชเบฒเบกเบฒเบ”เบ–เบทเบเบฅเบฐเบšเบธเป„เบงเป‰เปƒเบ™ manifest. เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เปเบœเบ™เบงเบฒเบ”เบˆเบฐเบกเบตเบฅเบฑเบเบชเบฐเบ™เบฐเบ”เบฑเปˆเบ‡เบ™เบตเป‰:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบˆเบฐโ€‹เบกเบตโ€‹เบ•เบปเบงโ€‹เบญเบฑเบโ€‹เบชเบญเบ™โ€‹เบญเบทเปˆเบ™ - inpage, เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบชเบตเบ”เป€เบ‚เบปเป‰เบฒเป„เบ›เปƒเบ™เบซเบ™เป‰เบฒ. เบกเบฑเบ™เบˆเบฐเบ”เปเบฒเป€เบ™เบตเบ™เบเบฒเบ™เปƒเบ™เบชเบฐเบžเบฒเบšเบเบฒเบ™เบ‚เบญเบ‡เบกเบฑเบ™เปเบฅเบฐเบชเบฐเบซเบ™เบญเบ‡ API เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบ.

ะะฐั‡ะฐะปะพ

เบฅเบฐเบซเบฑเบ”เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบฑเบ‡เปเบปเบ”เปเบกเปˆเบ™เบกเบตเบขเบนเปˆ GitHub. เปƒเบ™เบฅเบฐเบซเบงเปˆเบฒเบ‡เบ„เปเบฒเบญเบฐเบ—เบดเบšเบฒเบเบˆเบฐเบกเบตเบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเบเบฑเบšเบ„เปเบฒเบซเบกเบฑเป‰เบ™เบชเบฑเบ™เบเบฒ.

เปƒเบซเป‰เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เบ”เป‰เบงเบ manifesto:

{
  // ะ˜ะผั ะธ ะพะฟะธัะฐะฝะธะต, ะฒะตั€ัะธั. ะ’ัะต ัั‚ะพ ะฑัƒะดะตั‚ ะฒะธะดะฝะพ ะฒ ะฑั€ะฐัƒะทะตั€ะต ะฒ 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 เปเบฅเป‰เบงเปเบฅเบฐเปƒเบซเป‰เปเบ™เปˆเปƒเบˆเบงเปˆเบฒเบกเบฑเบ™เป€เบฎเบฑเบ”เบงเบฝเบ.

เป€เบžเบทเปˆเบญเบเบงเบ”เบชเบญเบšเบ™เบตเป‰, เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เป€เบญเบปเบฒเบฅเบฐเบซเบฑเบ” เบˆเบฒเบเบ™เบตเป‰. เบ™เบญเบเป€เบซเบ™เบทเบญเบˆเบฒเบเบชเบดเปˆเบ‡เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เป€เบฎเบฑเบ”, เบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเป„เบ”เป‰เบเปเบฒเบ™เบปเบ”เบเบฒเบ™เบ›เบฐเบเบญเบšเป‚เบ„เบ‡เบเบฒเบ™เป‚เบ”เบเปƒเบŠเป‰ webpack. เป€เบžเบทเปˆเบญเป€เบžเบตเปˆเบกเปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™เปƒเบชเปˆเบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš, เปƒเบ™ chrome://extensions เบ—เปˆเบฒเบ™เบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เป€เบฅเบทเบญเบ load unpacked เปเบฅเบฐเป‚เบŸเบ™เป€เบ”เบตเบ—เบตเปˆเบกเบตเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ—เบตเปˆเบชเบญเบ”เบ„เป‰เบญเบ‡เบเบฑเบ™ - เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ dist.

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เปƒเบ™เบ›เบฑเบ”เบˆเบธเบšเบฑเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเบ–เบทเบเบ•เบดเบ”เบ•เบฑเป‰เบ‡เปเบฅเบฐเป€เบฎเบฑเบ”เบงเบฝเบ. เบ—เปˆเบฒเบ™โ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เปƒเบŠเป‰โ€‹เป€เบ„เบทเปˆเบญเบ‡โ€‹เบกเบทโ€‹เบเบฒเบ™โ€‹เบžเบฑเบ”โ€‹เบ—เบฐโ€‹เบ™เบฒโ€‹เบชเปเบฒโ€‹เบฅเบฑเบšโ€‹เบชเบฐโ€‹เบžเบฒเบšโ€‹เบเบฒเบ™โ€‹เบ—เบตเปˆโ€‹เปเบ•เบโ€‹เบ•เปˆเบฒเบ‡โ€‹เบเบฑเบ™โ€‹เบ”เบฑเปˆเบ‡โ€‹เบ•เปเปˆโ€‹เป„เบ›โ€‹เบ™เบตเป‰โ€‹:

เบ›เปŠเบญเบšเบญเบฑเบš ->

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡ console script เป€เบ™เบทเป‰เบญเบซเบฒเปเบกเปˆเบ™เบ”เปเบฒเป€เบ™เบตเบ™เป‚เบ”เบเบœเปˆเบฒเบ™ console เบ‚เบญเบ‡เบซเบ™เป‰เบฒเบ‚เบญเบ‡เบกเบฑเบ™เป€เบญเบ‡เบ—เบตเปˆเบกเบฑเบ™เบ–เบทเบเป€เบ›เบตเบ”เบ•เบปเบง.เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบเบฒเบ™เปเบฅเบเบ›เปˆเบฝเบ™เบ‚เปเป‰เบ„เบงเบฒเบก

เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เบžเบงเบเป€เบฎเบปเบฒเบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เบชเป‰เบฒเบ‡เบชเบญเบ‡เบŠเปˆเบญเบ‡เบ—เบฒเบ‡เบเบฒเบ™เบชเบทเปˆเบชเบฒเบ™: inpage <-> background เปเบฅเบฐ popup <-> background. เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”, เปเบ™เปˆเบ™เบญเบ™, เบžเบฝเบ‡เปเบ•เปˆเบชเบปเปˆเบ‡เบ‚เปเป‰เบ„เบงเบฒเบกเป„เบ›เบซเบฒเบžเบญเบ”เปเบฅเบฐเบ›เบฐเบ”เบดเบ”เป‚เบ›เป‚เบ•เบ„เบญเบ™เบ‚เบญเบ‡เบ—เปˆเบฒเบ™เป€เบญเบ‡, เปเบ•เปˆเบ‚เป‰เบญเบเบกเบฑเบเบงเบดเบ—เบตเบเบฒเบ™เบ—เบตเปˆเบ‚เป‰เบญเบเป€เบซเบฑเบ™เปƒเบ™เป‚เบ„เบ‡เบเบฒเบ™ metamask open source.

เบ™เบตเป‰เปเบกเปˆเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเป€เบ„เบทเบญเบ‚เปˆเบฒเบ Ethereum. เปƒเบ™เบกเบฑเบ™, เบžเบฒเบเบชเปˆเบงเบ™เบ•เปˆเบฒเบ‡เป†เบ‚เบญเบ‡เปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™เบ•เบดเบ”เบ•เปเปˆเบชเบทเปˆเบชเบฒเบ™เบœเปˆเบฒเบ™ RPC เป‚เบ”เบเปƒเบŠเป‰เบซเป‰เบญเบ‡เบชเบฐเบซเบกเบธเบ” dnode. เบกเบฑเบ™เบญเบฐเบ™เบธเบเบฒเบ”เปƒเบซเป‰เบ—เปˆเบฒเบ™เบˆเบฑเบ”เบเบฒเบ™เปเบฅเบเบ›เปˆเบฝเบ™เบ‚เป‰เบญเบ™เบ‚เป‰เบฒเบ‡เป„เบงเปเบฅเบฐเบชเบฐเบ”เบงเบเบ–เป‰เบฒเบ—เปˆเบฒเบ™เบชเบฐเบซเบ™เบญเบ‡เบกเบฑเบ™เบ”เป‰เบงเบเบ™เป‰เปเบฒ nodejs เป€เบ›เบฑเบ™เบเบฒเบ™เบ‚เบปเบ™เบชเบปเปˆเบ‡ (เบซเบกเบฒเบเบ„เบงเบฒเบกเบงเปˆเบฒเบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบ›เบฐเบ•เบดเบšเบฑเบ”เบเบฒเบ™เป‚เบ•เป‰เบ•เบญเบšเบ”เบฝเบงเบเบฑเบ™):

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 เบชเปเบฒเบฅเบฑเบšเบ›เปŠเบญเบšเบญเบฑเบšเปเบฅเบฐเบซเบ™เป‰เบฒเป€เบงเบฑเบš, เปเบฅเบฐเบชเป‰เบฒเบ‡ dnode เบชเปเบฒเบฅเบฑเบšเบžเบงเบเป€เบ‚เบปเบฒ:

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)
        })
    }
}

เบ—เบตเปˆเบ™เบตเป‰เปเบฅเบฐเบ‚เป‰เบฒเบ‡เบฅเบธเปˆเบกเบ™เบตเป‰, เปเบ—เบ™เบ—เบตเปˆเบˆเบฐเป€เบ›เบฑเบ™เบงเบฑเบ”เบ–เบธ Chrome เบ—เบปเปˆเบงเป‚เบฅเบ, เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰ extensionApi, เป€เบŠเบดเปˆเบ‡เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡ Chrome เปƒเบ™เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ‚เบญเบ‡ 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 เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเบเบฐเปเบช, เปเบฅเบฐเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เบฎเบฑเบšเบžเบญเบ”, เบซเป‰เบญเบ‡เบฎเบฝเบ™เบญเบฐเปเบ”เบšเป€เบ•เบตเปเบกเปˆเบ™เบˆเปเบฒเป€เบ›เบฑเบ™. เบกเบฑเบ™เบ–เบทเบเบชเป‰เบฒเบ‡เบ‚เบถเป‰เบ™เป‚เบ”เบเปƒเบŠเป‰เบซเป‰เบญเบ‡เบชเบฐเบซเบกเบธเบ”เบ—เบตเปˆเบชเบฒเบกเบฒเบ”เบญเปˆเบฒเบ™เป„เบ”เป‰, เป€เบŠเบดเปˆเบ‡เบ›เบฐเบ•เบดเบšเบฑเบ”เบเบฒเบ™เบ–เปˆเบฒเบเบ—เบญเบ” nodejs เปƒเบ™เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบš:

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;
    }
}

เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™, เบžเบงเบเป€เบฎเบปเบฒเบชเป‰เบฒเบ‡เบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเปƒเบ™ script เป€เบ™เบทเป‰เบญเปƒเบ™:

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. เบžเบงเบเป€เบฎเบปเบฒเบชเป‰เบฒเบ‡เบชเบญเบ‡เบชเบฒเบเบ™เป‰เปเบฒ. เบซเบ™เบถเปˆเบ‡ - เบ•เปเปˆเบซเบ™เป‰เบฒ, เบขเบนเปˆเป€เบ—เบดเบ‡เบชเบธเบ”เบ‚เบญเบ‡ postMessage. เบชเปเบฒเบฅเบฑเบšเบ™เบตเป‰เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰เบ™เบตเป‰ เบŠเบธเบ”เบ™เบตเป‰ เบˆเบฒเบเบœเบนเป‰เบชเป‰เบฒเบ‡ metamask. เบเบฐเปเบชเบ—เบตเบชเบญเบ‡เปเบกเปˆเบ™เป€เบžเบทเปˆเบญเบžเบทเป‰เบ™เบซเบฅเบฑเบ‡เบ‚เบญเบ‡เบžเบญเบ”เบ—เบตเปˆเป„เบ”เป‰เบฎเบฑเบšเบˆเบฒเบ runtime.connect. เปƒเบซเป‰เบŠเบทเป‰เบžเบงเบเบกเบฑเบ™. เบ•เบญเบ™เบ™เบตเป‰เปœเป‰เบฒเป€เบงเบฑเบšเบˆเบฐเบกเบตเบเบฐเปเบชเป„เบ›เบซเบฒเบžเบทเป‰เบ™เบซเบผเบฑเบ‡.
  2. เปƒเบชเปˆเบชเบฐเบ„เบฃเบดเบšเปƒเบชเปˆ DOM. เบ”เบฒเบงเป‚เบซเบฅเบ”เบชเบฐเบ„เบฃเบดเบš (เบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบกเบฑเบ™เบ–เบทเบเบญเบฐเบ™เบธเบเบฒเบ”เปƒเบซเป‰เบขเบนเปˆเปƒเบ™ manifest) เปเบฅเบฐเบชเป‰เบฒเบ‡เปเบ—เบฑเบ 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);
    }
}

เบ•เบญเบ™เบ™เบตเป‰เบžเบงเบเป€เบฎเบปเบฒเบชเป‰เบฒเบ‡เบงเบฑเบ”เบ–เบธ api เปƒเบ™ inpage เปเบฅเบฐเบ•เบฑเป‰เบ‡เบกเบฑเบ™เป€เบ›เบฑเบ™เบ—เบปเปˆเบงเป‚เบฅเบ:

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;
}

เบžเบงเบเป€เบฎเบปเบฒเบžเป‰เบญเบกเปเบฅเป‰เบง Remote Procedure Call (RPC) เบ—เบตเปˆเบกเบต API เปเบเบเบ•เปˆเบฒเบ‡เบซเบฒเบเบชเปเบฒเบฅเบฑเบšเบซเบ™เป‰เบฒเปเบฅเบฐ UI. เป€เบกเบทเปˆเบญเป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเปœเป‰เบฒเปƒเปเปˆเบเบฑเบšเบžเบทเป‰เบ™เบซเบผเบฑเบ‡เป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เป€เบซเบฑเบ™เบญเบฑเบ™เบ™เบตเป‰:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

API เบซเบงเปˆเบฒเบ‡เป€เบ›เบปเปˆเบฒ เปเบฅเบฐเบ•เบปเป‰เบ™เบเบณเป€เบ™เบตเบ”. เปƒเบ™เบ”เป‰เบฒเบ™เบซเบ™เป‰เบฒ, เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เป‚เบ—เบซเบฒเบซเบ™เป‰เบฒเบ—เบตเปˆเบชเบฐเบšเบฒเบเบ”เบตเป€เบŠเบฑเปˆเบ™เบ™เบตเป‰:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบเบฒเบ™เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเบŸเบฑเบ‡เบŠเบฑเบ™ callback เปƒเบ™ 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))
    })
});

เปเบฅเบฐเบŸเบฑเบ‡เบŠเบฑเบ™เบเบฒเบ™เป‚เบ—เบชเบปเปˆเบ‡เบ„เบทเบ™เบ„เปเบฒเบชเบฑเบ™เบเบฒ:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบฅเบธเป‰เบ™เบ—เบตเปˆเบกเบตเบŸเบฑเบ‡เบŠเบฑเบ™เบšเปเปˆเบŠเบดเป‰เบ‡เป‚เบ„เบ™ เบ—เบตเปˆเบ™เบตเป‰.

เป‚เบ”เบเบฅเบงเบกเปเบฅเป‰เบง, เบงเบดเบ—เบตเบเบฒเบ™ RPC เปเบฅเบฐ stream เป€เบšเบดเปˆเบ‡เบ„เบทเบงเปˆเบฒเบกเบตเบ„เบงเบฒเบกเบเบทเบ”เบซเบเบธเปˆเบ™เบซเบผเบฒเบ: เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เบ™เปเบฒเปƒเบŠเป‰เบเบฒเบ™ multiplexing เป„เบญเบ™เป‰เปเบฒเปเบฅเบฐเบชเป‰เบฒเบ‡ APIs เบ—เบตเปˆเปเบ•เบเบ•เปˆเบฒเบ‡เบเบฑเบ™เบซเบผเบฒเบเบชเปเบฒเบฅเบฑเบšเบงเบฝเบเบ‡เบฒเบ™เบ—เบตเปˆเปเบ•เบเบ•เปˆเบฒเบ‡เบเบฑเบ™. เปƒเบ™เบซเบผเบฑเบเบเบฒเบ™, dnode เบชเบฒเบกเบฒเบ”เบ™เปเบฒเปƒเบŠเป‰เป„เบ”เป‰เบ—เบธเบเบšเปˆเบญเบ™, เบชเบดเปˆเบ‡เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™เปเบกเปˆเบ™เบเบฒเบ™เบซเปเปˆเบเบฒเบ™เบ‚เบปเบ™เบชเบปเปˆเบ‡เปƒเบ™เบฎเบนเบšเปเบšเบšเบ‚เบญเบ‡เบ™เป‰เปเบฒ nodejs.

เบ—เบฒเบ‡เป€เบฅเบทเบญเบเบญเบทเปˆเบ™เปเบกเปˆเบ™เบฎเบนเบšเปเบšเบš JSON, เป€เบŠเบดเปˆเบ‡เบ›เบฐเบ•เบดเบšเบฑเบ”เป‚เบ›เป‚เบ•เบ„เบญเบ™ JSON RPC 2. เปเบ™เบงเปƒเบ”เบเปเปˆเบ•เบฒเบก, เบกเบฑเบ™เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเบเบฒเบ™เบ‚เบปเบ™เบชเบปเปˆเบ‡เบชเบฐเป€เบžเบฒเบฐ (TCP เปเบฅเบฐ HTTP(S)), เป€เบŠเบดเปˆเบ‡เบšเปเปˆเบชเบฒเบกเบฒเบ”เปƒเบŠเป‰เป„เบ”เป‰เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ.

เบชเบฐเบ–เบฒเบ™เบฐเบžเบฒเบเปƒเบ™ เปเบฅเบฐ localStorage

เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบ•เป‰เบญเบ‡เบเบฒเบ™เป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบชเบฐเบ–เบฒเบ™เบฐเบžเบฒเบเปƒเบ™เบ‚เบญเบ‡เบ„เปเบฒเบฎเป‰เบญเบ‡เบชเบฐเบซเบกเบฑเบ - เบขเปˆเบฒเบ‡เบซเบ™เป‰เบญเบเบฅเบฐเบซเบฑเบ”เป€เบŠเบฑเบ™เบŠเบทเปˆ. เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เป€เบžเบตเปˆเบกเบชเบฐเบ–เบฒเบ™เบฐเปƒเบซเป‰เบเบฑเบšเปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™ เปเบฅเบฐเบงเบดเบ—เบตเบเบฒเบ™เบ›เปˆเบฝเบ™เบกเบฑเบ™เบขเบนเปˆเปƒเบ™ popup 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)
        }
    }

    ...

} 

เปƒเบ™เบžเบทเป‰เบ™เบซเบฅเบฑเบ‡, เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบซเปเปˆเบ—เบธเบเบขเปˆเบฒเบ‡เปƒเบ™เบŸเบฑเบ‡เบŠเบฑเบ™เปเบฅเบฐเบ‚เบฝเบ™เบงเบฑเบ”เบ–เบธเปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™เปƒเบชเปˆเบ›เปˆเบญเบ‡เบขเป‰เบฝเบกเป€เบžเบทเปˆเบญเปƒเบซเป‰เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เป€เบฎเบฑเบ”เบงเบฝเบเบเบฑเบšเบกเบฑเบ™เบˆเบฒเบ console:

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 console เปเบฅเบฐเป€เบšเบดเปˆเบ‡เบงเปˆเบฒเป€เบเบตเบ”เบซเบเบฑเบ‡เบ‚เบถเป‰เบ™เบเบฑเบšเบชเบฐเบ–เบฒเบ™เบฐ:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบฅเบฑเบ”เบ•เป‰เบญเบ‡เป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เป€เบฎเบฑเบ”เปƒเบซเป‰เบ„เบปเบ‡เบ—เบตเปˆเป€เบžเบทเปˆเบญเบšเปเปˆเปƒเบซเป‰เบเบฐเปเบˆเบชเบนเบ™เป€เบชเบเป„เบ›เปƒเบ™เป€เบงเบฅเบฒเบ—เบตเปˆ restart.

เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบกเบฑเบ™เป„เบงเป‰เปƒเบ™ localStorage, เบ‚เบฝเบ™เบ—เบฑเบšเบกเบฑเบ™เบเบฑเบšเบ—เบธเบเป†เบเบฒเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡. เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™, เบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบกเบฑเบ™เบเบฑเบ‡เบˆเบฐเบกเบตเบ„เบงเบฒเบกเบˆเปเบฒเป€เบ›เบฑเบ™เบชเปเบฒเบฅเบฑเบš 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 เบ•เบดเบ”เบ•เบฒเบกเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™. Getters เปเบฅเบฐ setters เบ‚เบญเบ‡ proxy objects เบ—เบตเปˆเบซเป‰เบญเบ‡เบชเบฐเบซเบกเบธเบ”เบชเป‰เบฒเบ‡เปเบกเปˆเบ™เบ–เบทเบเบ™เปเบฒเปƒเบŠเป‰.

เบ™เบฑเบเบ•เบปเบšเปเบ•เปˆเบ‡เบ›เบฐเบ•เบดเบšเบฑเบ”เบฎเบฑเบšเปƒเบŠเป‰เบชเบญเบ‡เบˆเบธเบ”เบ›เบฐเบชเบปเบ‡:

  1. เปƒเบ™โ€‹เบฎเบนเบšโ€‹เปเบšเบšโ€‹เบ—เบตเปˆโ€‹เป€เบ„เบฑเปˆเบ‡โ€‹เบ„เบฑเบ”โ€‹เบ—เบตเปˆโ€‹เบกเบตโ€‹เบ—เบธเบ‡ enforceActions, mobx เบซเป‰เบฒเบกโ€‹เบเบฒเบ™โ€‹เบ›เปˆเบฝเบ™โ€‹เปเบ›เบ‡โ€‹เบฅเบฑเบ”โ€‹เป‚เบ”เบโ€‹เบเบปเบ‡โ€‹. เบกเบฑเบ™เบ–เบทเบงเปˆเบฒเป€เบ›เบฑเบ™เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ—เบตเปˆเบ”เบตเบ—เบตเปˆเบˆเบฐเป€เบฎเบฑเบ”เบงเบฝเบเบžเบฒเบเปƒเบ•เป‰เป€เบ‡เบทเปˆเบญเบ™เป„เบ‚เบ—เบตเปˆเป€เบ„เบฑเปˆเบ‡เบ„เบฑเบ”.
  2. เป€เบ–เบดเบ‡เปเบกเปˆเบ™เบงเปˆเบฒเบซเบ™เป‰เบฒเบ—เบตเปˆเบ›เปˆเบฝเบ™เปเบ›เบ‡เบฅเบฑเบ”เบซเบผเบฒเบเบ„เบฑเป‰เบ‡ - เบ•เบปเบงเบขเปˆเบฒเบ‡, เบžเบงเบเป€เบฎเบปเบฒเบ›เปˆเบฝเบ™เบซเบผเบฒเบเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™เปƒเบ™เบฅเบฐเบซเบฑเบ”เบซเบผเบฒเบเบชเบฒเบ - เบœเบนเป‰เบชเบฑเบ‡เป€เบเบ”เบเบฒเบ™เป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เปเบˆเป‰เบ‡เป€เบ•เบทเบญเบ™เบžเบฝเบ‡เปเบ•เปˆเป€เบกเบทเปˆเบญเบกเบฑเบ™เบชเปเบฒเป€เบฅเบฑเบ”. เบ™เบตเป‰เปเบกเปˆเบ™เบชเบดเปˆเบ‡เบชเปเบฒเบ„เบฑเบ™เป‚เบ”เบเบชเบฐเป€เบžเบฒเบฐเบชเปเบฒเบฅเบฑเบš frontend, เบšเปˆเบญเบ™เบ—เบตเปˆเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบฅเบฑเบ”เบ—เบตเปˆเบšเปเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เบ™เปเบฒเป„เบ›เบชเบนเปˆเบเบฒเบ™เบชเบฐเปเบ”เบ‡เบญเบปเบ‡เบ›เบฐเบเบญเบšเบ—เบตเปˆเบšเปเปˆเบˆเปเบฒเป€เบ›เบฑเบ™. เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ, เบ—เบฑเบ‡เบชเบญเบ‡เบญเบฑเบ™เบ—เปเบฒเบญเบดเบ”เบซเบผเบทเบ—เบตเบชเบญเบ‡เปเบกเปˆเบ™เบšเปเปˆเบเปˆเบฝเบงเบ‚เป‰เบญเบ‡เป‚เบ”เบเบชเบฐเป€เบžเบฒเบฐ, เปเบ•เปˆเบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบ›เบฐเบ•เบดเบšเบฑเบ”เบ•เบฒเบกเบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ—เบตเปˆเบ”เบตเบ—เบตเปˆเบชเบธเบ”. เบกเบฑเบ™เป€เบ›เบฑเบ™เบ›เบฐเป€เบžเบ™เบตเบ—เบตเปˆเบˆเบฐเบ•เบดเบ”เป€เบ„เบทเปˆเบญเบ‡เบ•เบปเบšเปเบ•เปˆเบ‡เบเบฑเบšเบซเบ™เป‰เบฒเบ—เบตเปˆเบ—เบฑเบ‡เบซเบกเบปเบ”เบ—เบตเปˆเบ›เปˆเบฝเบ™เบชเบฐเบ–เบฒเบ™เบฐเบ‚เบญเบ‡เบ—เบปเปˆเบ‡เบ™เบฒเบ—เบตเปˆเบชเบฑเบ‡เป€เบเบ”เป€เบซเบฑเบ™.

เปƒเบ™เบžเบทเป‰เบ™เบซเบฅเบฑเบ‡เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบžเบตเปˆเบกเบเบฒเบ™เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เปเบฅเบฐเบšเบฑเบ™เบ—เบถเบเบชเบฐเบ–เบฒเบ™เบฐเปƒเบ™ localStorage:

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. เบ•เบปเบงเบˆเบฑเบ”เบเบฒเบ™เบ—เบตเปˆเบˆเบฐเบ–เบทเบเป€เบญเบตเป‰เบ™เบ”เป‰เบงเบเบ‚เปเป‰เบกเบนเบ™เบ™เบตเป‰เบ—เบธเบเบ„เบฑเป‰เบ‡เบ—เบตเปˆเบกเบฑเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡.

เบšเปเปˆเป€เบซเบกเบทเบญเบ™เบเบฑเบš redux, เบšเปˆเบญเบ™เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เบฎเบฑเบšเบฅเบฑเบ”เป€เบ›เบฑเบ™เบเบฒเบ™เป‚เบ•เป‰เบ–เบฝเบ‡เบขเปˆเบฒเบ‡เบŠเบฑเบ”เป€เบˆเบ™, mobx เบˆเบทเปˆเบˆเปเบฒเบงเปˆเบฒเบžเบงเบเป€เบฎเบปเบฒเป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบžเบฒเบเปƒเบ™เบ•เบปเบงเป€เบฅเบทเบญเบ, เปเบฅเบฐเบžเบฝเบ‡เปเบ•เปˆเป‚เบ—เบซเบฒเบ•เบปเบงเบˆเบฑเบ”เบเบฒเบ™เป€เบกเบทเปˆเบญเบžเบงเบเป€เบ‚เบปเบฒเบ›เปˆเบฝเบ™เปเบ›เบ‡.

เบกเบฑเบ™เป€เบ›เบฑเบ™เบชเบดเปˆเบ‡เบชเปเบฒเบ„เบฑเบ™เบ—เบตเปˆเบˆเบฐเป€เบ‚เบปเป‰เบฒเปƒเบˆเบขเปˆเบฒเบ‡เปเบ—เป‰เบˆเบดเบ‡เบงเปˆเบฒ mobx เบ•เบฑเบ”เบชเบดเบ™เปƒเบˆเปเบ™เบงเปƒเบ”เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบ•เบดเบ”เบ•เบฒเบก. เบ–เป‰เบฒเบ‚เป‰เบญเบเบ‚เบฝเบ™เบ•เบปเบงเป€เบฅเบทเบญเบเปƒเบ™เบฅเบฐเบซเบฑเบ”เปเบšเบšเบ™เบตเป‰() => app.store, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™, เบ›เบฐเบ•เบดเบเบดเบฅเบดเบเบฒเบˆเบฐเบšเปเปˆเบ–เบทเบเป€เบญเบตเป‰เบ™เบงเปˆเบฒ, เบ™เบฑเบšเบ•เบฑเป‰เบ‡เปเบ•เปˆเบเบฒเบ™เป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบ•เบปเบงเบกเบฑเบ™เป€เบญเบ‡เบšเปเปˆเบชเบฒเบกเบฒเบ”เบชเบฑเบ‡เป€เบเบ”เป€เบซเบฑเบ™เป„เบ”เป‰, เบžเบฝเบ‡เปเบ•เปˆเบ—เบปเปˆเบ‡เบ™เบฒเบ‚เบญเบ‡เบกเบฑเบ™เป€เบ—เบปเปˆเบฒเบ™เบฑเป‰เบ™.

เบ–เป‰เบฒเบ‚เป‰เบญเบเบ‚เบฝเบ™เปเบšเบšเบ™เบตเป‰ () => app.store.keys, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™, เบญเบตเบเป€เบ—เบทเปˆเบญเบซเบ™เบถเปˆเบ‡เบšเปเปˆเบกเบตเบซเบเบฑเบ‡เป€เบเบตเบ”เบ‚เบถเป‰เบ™, เบ™เบฑเบšเบ•เบฑเป‰เบ‡เปเบ•เปˆเป€เบกเบทเปˆเบญเป€เบžเบตเปˆเบก / เบ–เบญเบ™เบญเบปเบ‡เบ›เบฐเบเบญเบš array, เบเบฒเบ™เบญเป‰เบฒเบ‡เบญเบตเบ‡เป€เบ–เบดเบ‡เบกเบฑเบ™เบˆเบฐเบšเปเปˆเบ›เปˆเบฝเบ™เปเบ›เบ‡.

Mobx เป€เบฎเบฑเบ”เปœเป‰เบฒเบ—เบตเปˆเป€เบ›เบฑเบ™เบ•เบปเบงเป€เบฅเบทเบญเบเป€เบ›เบฑเบ™เบ„เบฑเป‰เบ‡เบ—เบณเบญเบดเบ” เปเบฅเบฐเบžเบฝเบ‡เปเบ•เปˆเบ•เบดเบ”เบ•เบฒเบกเบชเบดเปˆเบ‡เบ—เบตเปˆเบชเบฑเบ‡เป€เบเบ”เป„เบ”เป‰เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡. เบ™เบตเป‰เปเบกเปˆเบ™เป€เบฎเบฑเบ”เบœเปˆเบฒเบ™เบ•เบปเบงเบฎเบฑเบšเบ•เบปเบงเปเบ—เบ™. เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เบŸเบฑเบ‡เบŠเบฑเบ™เบ—เบตเปˆเบชเป‰เบฒเบ‡เบ‚เบถเป‰เบ™เปเบกเปˆเบ™เบ–เบทเบเบ™เปเบฒเปƒเบŠเป‰เบขเบนเปˆเบ—เบตเปˆเบ™เบตเป‰ toJS. เบกเบฑเบ™เบชเบปเปˆเบ‡เบ„เบทเบ™เบงเบฑเบ”เบ–เบธเปƒเปเปˆเบ—เบตเปˆเบกเบตเบ•เบปเบงเปเบ—เบ™เบ—เบฑเบ‡เปเบปเบ”เปเบ—เบ™เบ—เบตเปˆเบ”เป‰เบงเบเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบ•เบปเป‰เบ™เบชเบฐเบšเบฑเบš. เปƒเบ™โ€‹เบฅเบฐโ€‹เบซเบงเปˆเบฒเบ‡โ€‹เบเบฒเบ™โ€‹เบ›เบฐโ€‹เบ•เบดโ€‹เบšเบฑเบ”โ€‹, เบกเบฑเบ™โ€‹เบญเปˆเบฒเบ™โ€‹เบžเบฒเบโ€‹เบชเบฐโ€‹เบซเบ™เบฒเบกโ€‹เบ—เบฑเบ‡โ€‹เบซเบกเบปเบ”โ€‹เบ‚เบญเบ‡โ€‹เบงเบฑเบ”โ€‹เบ–เบธ - เป€เบžเบฒเบฐโ€‹เบชเบฐโ€‹เบ™เบฑเป‰เบ™ getters เปเบกเปˆเบ™ triggeredโ€‹.

เปƒเบ™เบ›เบฑเบญเบšเบญเบฑเบšเบ„เบญเบ™เป‚เบŠเบ™เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบžเบตเปˆเบกเบฅเบฐเบซเบฑเบ”เบซเบผเบฒเบเบญเบฑเบ™เบญเบตเบเบ„เบฑเป‰เบ‡. เป€เบงเบฅเบฒเบ™เบตเป‰เบžเบงเบเป€เบ‚เบปเบฒเบเบฑเบ‡เบชเบดเป‰เบ™เบชเบธเบ”เบฅเบปเบ‡เปƒเบ™ localStorage:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เป€เบกเบทเปˆเบญเปœเป‰เบฒเบžเบทเป‰เบ™เบซเบผเบฑเบ‡เบ–เบทเบเป‚เบซเบผเบ”เบ„เบทเบ™เปƒเปเปˆ, เบ‚เปเป‰เบกเบนเบ™เบเบฑเบ‡เบ„เบปเบ‡เบขเบนเปˆ.

เบฅเบฐเบซเบฑเบ”เบ„เปเบฒเบฎเป‰เบญเบ‡เบชเบฐเบซเบกเบฑเบเบ—เบฑเบ‡เบซเบกเบปเบ”เป€เบ–เบดเบ‡เบˆเบธเบ”เบ™เบตเป‰เบชเบฒเบกเบฒเบ”เป€เบšเบดเปˆเบ‡เป„เบ”เป‰ เบ—เบตเปˆเบ™เบตเป‰.

เบเบฒเบ™เป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบเบฐเปเบˆเบชเปˆเบงเบ™เบ•เบปเบงเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบเบฒเบ™เป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบเบฐเปเบˆเบชเปˆเบงเบ™เบ•เบปเบงเปƒเบ™เบ‚เปเป‰เบ„เบงเบฒเบกเบ—เบตเปˆเบŠเบฑเบ”เป€เบˆเบ™เปเบกเปˆเบ™เบšเปเปˆเบ›เบญเบ”เป„เบž: เบกเบตเป‚เบญเบเบฒเบ”เบชเบฐเป€เบซเบกเบตเบ—เบตเปˆเป€เบˆเบปเป‰เบฒเบˆเบฐเบ–เบทเบเปเบฎเบฑเบ, เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบ„เบญเบกเบžเบดเบงเป€เบ•เบตเบ‚เบญเบ‡เบ—เปˆเบฒเบ™, เปเบฅเบฐเบญเบทเปˆเบ™เป†. เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เปƒเบ™ localStorage เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบเบฐเปเบˆเปƒเบ™เบฎเบนเบšเปเบšเบšเบ—เบตเปˆเป€เบ‚เบปเป‰เบฒเบฅเบฐเบซเบฑเบ”เบฅเบฑเบš.

เป€เบžเบทเปˆเบญเบ„เบงเบฒเบกเบ›เบญเบ”เป„เบžเบซเบผเบฒเบเบเบงเปˆเบฒเป€เบเบปเปˆเบฒ, เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบžเบตเปˆเบกเบชเบฐเบ–เบฒเบ™เบฐเบ—เบตเปˆเบ–เบทเบเบฅเบฑเบญเบเปƒเบซเป‰เบเบฑเบšเปเบญเบฑเบšเบžเบฅเบดเป€เบ„เบŠเบฑเบ™, เป€เบŠเบดเปˆเบ‡เปƒเบ™เบ™เบฑเป‰เบ™เบˆเบฐเบšเปเปˆเบกเบตเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เบเบฐเปเบˆเป€เบฅเบตเบ. เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป‚เบญเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเป„เบ›เปƒเบซเป‰เบชเบฐเบ–เบฒเบ™เบฐเบ—เบตเปˆเบ–เบทเบเบฅเบฑเบญเบเป‚เบ”เบเบญเบฑเบ”เบ•เบฐเป‚เบ™เบกเบฑเบ”เป€เบ™เบทเปˆเบญเบ‡เบˆเบฒเบเปเบปเบ”เป€เบงเบฅเบฒ.

Mobx เบŠเปˆเบงเบเปƒเบซเป‰เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เป€เบเบฑเบšเบ‚เปเป‰เบกเบนเบ™เป„เบ”เป‰เบžเบฝเบ‡เปเบ•เปˆเบŠเบธเบ”เบ•เปเบฒเปˆเบชเบธเบ”เบ—เบตเปˆ, เปเบฅเบฐเบชเปˆเบงเบ™เบ—เบตเปˆเป€เบซเบผเบทเบญเบˆเบฐเบ–เบทเบเบ„เบดเบ”เป„เบฅเปˆเป‚เบ”เบเบญเบฑเบ”เบ•เบฐเป‚เบ™เบกเบฑเบ”เป‚เบ”เบเบญเบตเบ‡เปƒเบชเปˆเบกเบฑเบ™. เป€เบซเบผเบปเปˆเบฒเบ™เบตเป‰เปเบกเปˆเบ™เบญเบฑเบ™เบ—เบตเปˆเป€เบญเบตเป‰เบ™เบงเปˆเบฒเบ„เบธเบ™เบชเบปเบกเบšเบฑเบ”เบ„เปเบฒเบ™เบงเบ™. เบžเบงเบเป€เบ‚เบปเบฒเบชเบฒเบกเบฒเบ”เบ–เบทเบเบ›เบฝเบšเบ—เบฝเบšเบเบฑเบš views เปƒเบ™เบ–เบฒเบ™เบ‚เปเป‰เบกเบนเบ™:

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 เบชเบฒเบ—เบฒเบฅเบฐเบ™เบฐเปƒเบ™เบ›เบฑเบ”เบˆเบธเบšเบฑเบ™เบกเบตเบงเบดเบ—เบตเบเบฒเบ™เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เบเบฒเบ™เป€เบเบฑเบšเบฎเบฑเบเบชเบฒ.

เบ‚เบฝเบ™เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เป€เบ‚เบปเป‰เบฒเบฅเบฐเบซเบฑเบ” เบœเบปเบ™เบ›เบฐเป‚เบซเบเบ”เบ—เบตเปˆเปƒเบŠเป‰ crypto-js:

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 เป‚เบ”เบเบœเปˆเบฒเบ™เบ—เบตเปˆเบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบˆเบญเบ‡เป€เบซเบ”เบเบฒเบ™ - เบเบฒเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡เบ‚เบญเบ‡เบฅเบฑเบ”. เบฅเบฑเบ”, เบ•เบฒเบกเบ„เบงเบฒเบกเป€เบซเบกเบฒเบฐเบชเบปเบก, เบญเบฒเบ”เบˆเบฐเป€เบ›เบฑเบ™ idle, active ะธ locked. เบชเปเบฒเบฅเบฑเบš idle เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบเปเบฒเบ™เบปเบ”เป€เบงเบฅเบฒเบซเบกเบปเบ”เป€เบงเบฅเบฒ, เปเบฅเบฐเบ–เบทเบเบฅเบฑเบญเบเปเบกเปˆเบ™เบ–เบทเบเบเปเบฒเบ™เบปเบ”เป€เบกเบทเปˆเบญ OS เบ•เบปเบงเบ‚เบญเบ‡เบกเบฑเบ™เป€เบญเบ‡เบ–เบทเบเบšเบฅเบฑเบญเบ. เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบเบฑเบ‡โ€‹เบˆเบฐโ€‹เบ›เปˆเบฝเบ™โ€‹เบ•เบปเบงโ€‹เป€เบฅเบทเบญเบโ€‹เบชเปเบฒโ€‹เบฅเบฑเบšโ€‹เบเบฒเบ™โ€‹เบšเบฑเบ™โ€‹เบ—เบถเบโ€‹เป€เบ›เบฑเบ™ localStorageโ€‹:

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)
        }
    }
}

เบฅเบฐเบซเบฑเบ”เบเปˆเบญเบ™เบ‚เบฑเป‰เบ™เบ•เบญเบ™เบ™เบตเป‰เปเบกเปˆเบ™ เบ—เบตเปˆเบ™เบตเป‰.

เบเบฒเบ™เป€เบฎเบฑเบ”เบ—เบธเบฅเบฐ เบเบณ

เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เบžเบงเบเป€เบฎเบปเบฒเบกเบฒเบฎเบญเบ”เบชเบดเปˆเบ‡เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™เบ—เบตเปˆเบชเบธเบ”: เบเบฒเบ™เบชเป‰เบฒเบ‡เปเบฅเบฐเป€เบŠเบฑเบ™เบเบฒเบ™เป€เบฎเบฑเบ”เบ—เบธเบฅเบฐเบเปเบฒเปƒเบ™ blockchain. เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเปƒเบŠเป‰ WAVES blockchain เปเบฅเบฐเบซเป‰เบญเบ‡เบชเบฐเบซเบกเบธเบ” waves-เบ—เบธเบฅเบฐเบเปเบฒ.

เบ—เปเบฒเบญเบดเบ”, เปƒเบซเป‰เป€เบฎเบปเบฒเบ•เบทเปˆเบกเปƒเบชเปˆเบฅเบฑเบ” array เบ‚เบญเบ‡เบ‚เปเป‰เบ„เบงเบฒเบกเบ—เบตเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เป„เบ”เป‰เป€เบŠเบฑเบ™, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™เป€เบžเบตเปˆเบกเบงเบดเบ—เบตเบเบฒเบ™เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เป€เบžเบตเปˆเบกเบ‚เปเป‰เบ„เบงเบฒเบกเปƒเบซเบกเปˆ, เบขเบทเบ™เบขเบฑเบ™เบฅเบฒเบเป€เบŠเบฑเบ™, เปเบฅเบฐเบเบฒเบ™เบ›เบฐเบ•เบดเป€เบชเบ”:

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'
    }

    ...
}

เป€เบกเบทเปˆเบญเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เบฎเบฑเบšเบ‚เปเป‰เบ„เบงเบฒเบกเปƒเบซเบกเปˆ, เบžเบงเบเป€เบฎเบปเบฒเป€เบžเบตเปˆเบก metadata เบเบฑเบšเบกเบฑเบ™, เป€เบฎเบฑเบ” observable เปเบฅเบฐเป€เบžเบตเปˆเบกเปƒเบชเปˆ store.messages.

เบ–เป‰เบฒเป€เบˆเบปเป‰เบฒเบšเปเปˆ observable เบ”เป‰เบงเบเบ•เบปเบ™เป€เบญเบ‡, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™ mobx เบˆเบฐเป€เบฎเบฑเบ”เบกเบฑเบ™เป€เบญเบ‡เป€เบกเบทเปˆเบญเป€เบžเบตเปˆเบกเบ‚เปเป‰เบ„เบงเบฒเบกเปƒเบชเปˆ array. เบขเปˆเบฒเบ‡เปƒเบ”เบเปเปˆเบ•เบฒเบก, เบกเบฑเบ™เบˆเบฐเบชเป‰เบฒเบ‡เบงเบฑเบ”เบ–เบธเปƒเบซเบกเปˆเบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบšเปเปˆเบกเบตเป€เบญเบเบฐเบชเบฒเบ™เบญเป‰เบฒเบ‡เบญเบตเบ‡, เปเบ•เปˆเบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบ•เป‰เบญเบ‡เบเบฒเบ™เบกเบฑเบ™เบชเปเบฒเบฅเบฑเบšเบ‚เบฑเป‰เบ™เบ•เบญเบ™เบ•เปเปˆเป„เบ›.

เบ•เปเปˆเป„เบ›, เบžเบงเบเป€เบฎเบปเบฒเบเบฑเบšเบ„เบทเบ™เบ„เปเบฒเบชเบฑเบ™เบเบฒเบ—เบตเปˆเปเบเป‰เป„เบ‚เป€เบกเบทเปˆเบญเบชเบฐเบ–เบฒเบ™เบฐเบเบฒเบ™เบ‚เปเป‰เบ„เบงเบฒเบกเบ›เปˆเบฝเบ™เปเบ›เบ‡. เบชเบฐเบ–เบฒเบ™เบฐเป„เบ”เป‰เบ–เบทเบเบ•เบดเบ”เบ•เบฒเบกเป‚เบ”เบเบ›เบฐเบ•เบดเบเบดเบฅเบดเบเบฒ, เป€เบŠเบดเปˆเบ‡เบˆเบฐ "เบ‚เป‰เบฒเบ•เบปเบงเบกเบฑเบ™เป€เบญเบ‡" เป€เบกเบทเปˆเบญเบชเบฐเบ–เบฒเบ™เบฐเบเบฒเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡.

เบฅเบฐเบซเบฑเบ”เบงเบดเบ—เบตเบเบฒเบ™ approve ะธ reject เบ‡เปˆเบฒเบโ€‹เบ”เบฒเบโ€‹เบซเบผเบฒเบโ€‹: เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบžเบฝเบ‡โ€‹เปเบ•เปˆโ€‹เบ›เปˆเบฝเบ™โ€‹เบชเบฐโ€‹เบ–เบฒโ€‹เบ™เบฐโ€‹เบžเบฒเบšโ€‹เบ‚เบญเบ‡โ€‹เบ‚เปเป‰โ€‹เบ„เบงเบฒเบกโ€‹, เบซเบผเบฑเบ‡โ€‹เบˆเบฒเบโ€‹เบเบฒเบ™โ€‹เป€เบŠเบฑเบ™โ€‹เบกเบฑเบ™โ€‹เบ–เป‰เบฒโ€‹เบซเบฒเบโ€‹เบงเปˆเบฒโ€‹เบˆเปเบฒโ€‹เป€เบ›เบฑเบ™โ€‹.

เบžเบงเบเป€เบฎเบปเบฒเป€เบญเบปเบฒเบเบฒเบ™เบญเบฐเบ™เบธเบกเบฑเบ” เปเบฅเบฐเบ›เบฐเบ•เบดเป€เบชเบ”เปƒเบ™ UI API, newMessage เปƒเบ™ 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 state เปเบฅเบฐเป€เบžเบตเปˆเบกเบŸเบฑเบ‡เบŠเบฑเบ™เปƒเบซเป‰เบเบฑเบš 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)
}

เปƒเบ™โ€‹เบ•เบญเบ™โ€‹เบ—เป‰เบฒเบโ€‹เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เป€เบฅเบตเปˆเบกโ€‹เบ•เบปเป‰เบ™โ€‹เบเบฒเบ™โ€‹เป€เบฎเบฑเบ”โ€‹เปƒเบซเป‰โ€‹เบเบฒเบ™โ€‹เป‚เบ•เป‰โ€‹เบ•เบญเบšโ€‹เบ‚เบญเบ‡โ€‹เบ„เปเบฒโ€‹เบฎเป‰เบญเบ‡โ€‹เบชเบฐโ€‹เบซเบกเบฑเบโ€‹. เบ™เบตเป‰โ€‹เปเบกเปˆเบ™โ€‹เบ„เปเบฒโ€‹เบฎเป‰เบญเบ‡โ€‹เบชเบฐโ€‹เบซเบกเบฑเบ reactโ€‹. เบˆเบธเบ”โ€‹เบ›เบฐโ€‹เบชเบปเบ‡โ€‹เบžเบทเป‰เบ™โ€‹เบ–เบฒเบ™โ€‹เปเบกเปˆเบ™โ€‹เบžเบฝเบ‡โ€‹เปเบ•เปˆโ€‹เบœเปˆเบฒเบ™โ€‹เบเบฒเบ™โ€‹เบ™เปเบฒโ€‹เปƒเบŠเป‰ propsโ€‹. เบกเบฑเบ™เบˆเบฐเบ–เบทเบเบ•เป‰เบญเบ‡, เปเบ™เปˆเบ™เบญเบ™, เป€เบžเบทเปˆเบญเป€เบฎเบฑเบ”เปƒเบซเป‰เบเบฒเบ™เบšเปเบฅเบดเบเบฒเบ™เปเบเบเบ•เปˆเบฒเบ‡เบซเบฒเบเบชเปเบฒเบฅเบฑเบšเบงเบดเบ—เบตเบเบฒเบ™เปเบฅเบฐเบฎเป‰เบฒเบ™เบชเปเบฒเบฅเบฑเบšเบฅเบฑเบ”, เปเบ•เปˆเบชเปเบฒเบฅเบฑเบšเบˆเบธเบ”เบ›เบฐเบชเบปเบ‡เบ‚เบญเบ‡เบšเบปเบ”เบ„เบงเบฒเบกเบ™เบตเป‰, เบ™เบตเป‰เปเบกเปˆเบ™เบžเบฝเบ‡เบžเป:

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-react เบขเบนเปˆเปƒเบ™เบญเบปเบ‡เบ›เบฐเบเบญเบš, เปเบฅเบฐ render เบˆเบฐเบ–เบทเบเป€เบญเบตเป‰เบ™เป‚เบ”เบเบญเบฑเบ”เบ•เบฐเป‚เบ™เบกเบฑเบ”เป€เบกเบทเปˆเบญเบกเบตเบเบฒเบ™เบชเบฑเบ‡เป€เบเบ”เบเบฒเบ™เบญเป‰เบฒเบ‡เบญเบตเบ‡เบˆเบฒเบเบเบฒเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡เบญเบปเบ‡เบ›เบฐเบเบญเบš. เบ—เปˆเบฒเบ™เบšเปเปˆเบ•เป‰เบญเบ‡เบเบฒเบ™ mapStateToProps เบซเบผเบทเป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเบ„เบทเบเบฑเบš redux. เบ—เบธเบเบขเปˆเบฒเบ‡เป€เบฎเบฑเบ”เบงเบฝเบเบญเบญเบเบˆเบฒเบเบเปˆเบญเบ‡:

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}
    );

...
}

เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™, เบ„เปเบฒเบฎเป‰เบญเบ‡เบชเบฐเบซเบกเบฑเบเปเบกเปˆเบ™เบเบฝเบกเบžเป‰เบญเบก. เบซเบ™เป‰เบฒเป€เบงเบฑเบšเบญเบฒเบ”เบˆเบฐเบฎเป‰เบญเบ‡เบ‚เปเบฅเบฒเบเป€เบŠเบฑเบ™เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เป€เบฎเบฑเบ”เบ—เบธเบฅเบฐเบเปเบฒ:

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบเบฒเบ™เบ‚เบฝเบ™เบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ‚เบญเบ‡เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเบ›เบญเบ”เป„เบž

เบฅเบฐเบซเบฑเบ”เปเบกเปˆเบ™เบกเบตเบขเบนเปˆเบšเปˆเบญเบ™เบ™เบตเป‰ เบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆ.

เบชเบฐเบซเบฅเบธเบš

เบ–เป‰เบฒเบ—เปˆเบฒเบ™เป„เบ”เป‰เบญเปˆเบฒเบ™เบšเบปเบ”เบ„เบงเบฒเบกเบˆเบปเบ™เป€เบ–เบดเบ‡เบ—เบตเปˆเบชเบธเบ”, เปเบ•เปˆเบเบฑเบ‡เบกเบตเบ„เปเบฒเบ–เบฒเบก, เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบ–เบฒเบกเบžเบงเบเป€เบ‚เบปเบฒเป„เบ”เป‰เบ—เบตเปˆ repositories เบ—เบตเปˆเบกเบตเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบ. เบขเบนเปˆเบ—เบตเปˆเบ™เบฑเป‰เบ™เบ—เปˆเบฒเบ™เบเบฑเบ‡เบˆเบฐเบžเบปเบšเป€เบซเบฑเบ™เบ„เปเบฒเบซเบกเบฑเป‰เบ™เบชเบฑเบ™เบเบฒเบชเปเบฒเบฅเบฑเบšเปเบ•เปˆเบฅเบฐเบ‚เบฑเป‰เบ™เบ•เบญเบ™เบ—เบตเปˆเบเปเบฒเบ™เบปเบ”.

เปเบฅเบฐเบ–เป‰เบฒเบ—เปˆเบฒเบ™เบชเบปเบ™เปƒเบˆเป€เบšเบดเปˆเบ‡เบฅเบฐเบซเบฑเบ”เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบ‚เบฐเบซเบเบฒเบเบ•เบปเบงเบˆเบดเบ‡, เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบŠเบญเบเบซเบฒเบ™เบตเป‰ เบ—เบตเปˆเบ™เบตเป‰.

เบฅเบฐเบซเบฑเบ”, repository เปเบฅเบฐเบฅเบฒเบเบฅเบฐเบญเบฝเบ”เบงเบฝเบเป€เบฎเบฑเบ”เบ‡เบฒเบ™เบ—เปเบฒเบˆเบฒเบ Siemarell

เปเบซเบผเปˆเบ‡เบ‚เปเป‰เบกเบนเบ™: www.habr.com

เป€เบžเบตเปˆเบกเบ„เบงเบฒเบกเบ„เบดเบ”เป€เบซเบฑเบ™