Взываю к помощи коллективного разума.
Смотрите, имеем node.js-процесс. Процесс спаунит чайл-процессы. Убийство родителя по
Так в чём здесь отличие
UPD коллективный разум нашёл ответ https://stackoverflow.com/questions/8398845/what-is-the-difference-between-ctrl-c-and-sigint/8406413#8406413
Баш сам пробрасывает SIGINT по всей группе процессов.
Смотрите, имеем node.js-процесс. Процесс спаунит чайл-процессы. Убийство родителя по
ctrl+c приводит и к смерти чайлд-процессов. Но почему отправка SIGINT не приводит к такому результату и чайлды не умирают? Ни SIGINT, ни SIGTERM ни злобный SIGKILL не трогают чайлдов. Так в чём здесь отличие
ctrl+c?UPD коллективный разум нашёл ответ https://stackoverflow.com/questions/8398845/what-is-the-difference-between-ctrl-c-and-sigint/8406413#8406413
Баш сам пробрасывает SIGINT по всей группе процессов.
Stack Overflow
What is the difference between Ctrl-C and SIGINT?
I have been debugging a Python program which segfaults after receiving a KeyboardInterrupt exception. This is normally done by pressing Ctrl+C from the shell. To test if a particular code change fi...
👍16🔥3
Опять Гугл всё сломал
В мае Гугл запустил новый домен первого уровня
Заметили подставу? А если бы вам не сказали, что в ссылке есть проблема?
Переход по такой ссылке направит нас на
А чтобы жизнь была ещё веселей, Гугл добавил TLD
Подробнее о проблеме и вариантах атак: https://medium.com/@bobbyrsec/the-dangers-of-googles-zip-tld-5e1e675e59a5
UPD
В фишинговой ссылке из примера используется два хака: символ @ и «другой» слеш ∕ (U+2215)
В мае Гугл запустил новый домен первого уровня
.zip и неплохо так насолил безопасникам. Дело в том, что браузеру тоже можно скормить урл с userinfo частью (<протокол>//<userinfo>@<домен>), а значит, можно подсунуть пользователю адрес вида https://github.com∕kubernetes∕kubernetes∕archive∕refs∕tags∕@v1.27.1.zip.
Заметили подставу? А если бы вам не сказали, что в ссылке есть проблема?
Переход по такой ссылке направит нас на
v1.27.1.zip. Более того, собачку в адресе можно скрыть в интерфейсе, уменьшив шрифт.
А чтобы жизнь была ещё веселей, Гугл добавил TLD
.mov. А мессенджеры превратили это в ссылки. Так и живём.Подробнее о проблеме и вариантах атак: https://medium.com/@bobbyrsec/the-dangers-of-googles-zip-tld-5e1e675e59a5
UPD
В фишинговой ссылке из примера используется два хака: символ @ и «другой» слеш ∕ (U+2215)
😁23🤯20😱4❤2🤬2👍1😢1
Каждый год перед запуском ctf мучительно ищу подходящий RSA-ключ чтобы настроить каждый сервер. 1Password сделал свой ssh-agent но только в облачной версии, на которую я по понятным причинам переходить не хочу. Хранить на юбиках? Ну так ещё проще потерять юбик, чем облако.
Вот и продолжаю хранить в файликах и подбирать подходящий. Ещё один вариант — сделать мастер пароль и спрятать его в 1Password, а при утере ключа — ну и фиг с ним, залить новый.
Эй, подписчики, а вы как храните свои ключи?
Вот и продолжаю хранить в файликах и подбирать подходящий. Ещё один вариант — сделать мастер пароль и спрятать его в 1Password, а при утере ключа — ну и фиг с ним, залить новый.
Эй, подписчики, а вы как храните свои ключи?
Кто поленился настроить UFW и выставил ctf портами наружу — тот я :) Спасибо Саше Шоронову, что посканировал порты и пришёл с багой. Сколько не ставь Basic Auth в nginx, если рядом нода слушает сеть, то ой. А делал бы на юникс сокетах — горя бы не знал, но что-то подвела привычка (когда писал первую ctf уже жил в парадигме, что в контейнере nginx не нужен, и нода сама слушает сеть, как бы это не странно звучало).
В общем, не будьте мной, не ленитесь в виртуалках прикрывать порты. А unix-сокеты так-то всё ещё хороши.
Если же nginx гонит трафик на node через unix-сокет, то даже при открытых портах снаружи через сеть туда уже будет не достучаться.
В общем, не будьте мной, не ленитесь в виртуалках прикрывать порты. А unix-сокеты так-то всё ещё хороши.
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
UPD
Тут просят пояснить, что за набор букв я написал. Смотрите, запущенный сервер на node.js слушает либо unix-сокет (который описывается как файл на файловой системе) либо сетевой порт. Какой-нибудь 3000, например. Перед нодой стоит какой-нибудь nginx, слушает порты 80 и 443 и закидывает трафик на 3000 порт локлхоста который и слушает node.js. Это называется «реверс-прокси». Если не закрыть порты фаерволом, оставив только 80, 443 и какой-нибудь 22 для ssh, то можно будет просканировать все порты снаружи, найти наш 3000 и постучаться на ноду в обход nginx, забив на все его правила. Если же nginx гонит трафик на node через unix-сокет, то даже при открытых портах снаружи через сеть туда уже будет не достучаться.
😱15❤11👍4
Вот загадка, почему на macOS не идёт в комплекте git. Каждый раз этот квест с установкой Xcode Command Line Tools или
Но мне же просто нужен бинарь гита в комплекте, работающий после любого апдейта. Вы же типа профессиональная ось, линукс с человеческим лицом. Положите в коробку тулзу, пусть лежит.
Вот SSH-клиент у нас же есть. Плохонький, но есть.
brew install git (который под капотом ставит Xcode Command Line Tools). Но мне же просто нужен бинарь гита в комплекте, работающий после любого апдейта. Вы же типа профессиональная ось, линукс с человеческим лицом. Положите в коробку тулзу, пусть лежит.
Вот SSH-клиент у нас же есть. Плохонький, но есть.
👍32
В поезде прочитал интересные размышления от François Zaninotto — Is React Having An Angular.js Moment?
Франсуа задаётся вопросом, не подошёл ли Реакт к той же точке, в которую однажды упёрся Ангуляр, когда вторая версия оказалась полностью переписанной с использованием других парадигм. В итоге, многие разработчики не стали переписывать свои приложения на новую версию, вместо этого просто сменив фреймворк.
И вот у нас React предлагает поменять парадигму и перейти на Server Components. И это опасное место.
Теперь наши компоненты уже не просто рисуют отображение по состоянию. Они становятся сложнее. Теперь прямо в компонентах мы можем использовать
Вообще
CSS-in-JS решения тоже пока не работают в серверных компонентах. (Тут я конечно немного радуюсь, что «классические» решения всё так же хороши).
С отладкой тоже пока не очень, но тут наверное допилят (должны допилить!).
Экосистемы для RSC просто нет. Знакомые нам либы не работают (react-query, swr, react-hook-form и т.д.). Короче, всё, что на хуках.
Если они нам нужны, то мы должны заворачивать всё в обёртки с
Почти всё сломано. А ещё и контекст отобрали, как теперь лёгкий DI пилить?
И запросы на сервер странные. Формат намеренно не документирован.
Разработчикам SPA всё это не нравится. Они обеспокоены. Для SSR они готовы брать другие решения. Почему Реакт отговаривает от SPA? Почему официальная документация рекомендует Next, а Next рекомендует RSC? Может быть это способ помочь Vercel заработать на React? Ведь для RSC нужен бэкенд, а бэкенд нужно где-то запускать.
Но отвечая на заглавный вопрос, проходит ли React свой «момент Angular», автор говорит — нет, не проходит. Потому что мы всё ещё можем писать «по-старому». Без Server Components.
Но в то же время пользователи теперь вынуждены выбирать между старым рабочим решением и новым сияющим активно рекламируемым. И это опасно. Между двух альтернатив выбора может возникнуть третья — иной фреймворк. И вот тут React вполне может нанести вред своему сообществу. Зачем мне RSC, с той же долей риска я могу перейти на Solid или ещё куда-нибудь.
В заключении Франсуа призывает команды React и Next одуматься и притушить рекламный поток для RSC. Не выпячивать это решение как противовес «классике» и единственное возможное будущее, чтобы не навредить экосистеме React.
Франсуа задаётся вопросом, не подошёл ли Реакт к той же точке, в которую однажды упёрся Ангуляр, когда вторая версия оказалась полностью переписанной с использованием других парадигм. В итоге, многие разработчики не стали переписывать свои приложения на новую версию, вместо этого просто сменив фреймворк.
И вот у нас React предлагает поменять парадигму и перейти на Server Components. И это опасное место.
Теперь наши компоненты уже не просто рисуют отображение по состоянию. Они становятся сложнее. Теперь прямо в компонентах мы можем использовать
fetch не заворачивая его useEffect. И это не браузерный fetch, это патченая его версия. Зачем патченая? Для того, чтобы бороться с лишним ре-фетчем данных и обложить всё кешами из коробки. А кешировать забрасывая данные в контекст мы больше не можем — нет в серверных компонентах контекста.Вообще
useState, useContext и useEffect — все они не работают. Мы можем их включить, используя use client, но это теперь не поведение по умолчанию. CSS-in-JS решения тоже пока не работают в серверных компонентах. (Тут я конечно немного радуюсь, что «классические» решения всё так же хороши).
С отладкой тоже пока не очень, но тут наверное допилят (должны допилить!).
Экосистемы для RSC просто нет. Знакомые нам либы не работают (react-query, swr, react-hook-form и т.д.). Короче, всё, что на хуках.
Если они нам нужны, то мы должны заворачивать всё в обёртки с
use client. Почти всё сломано. А ещё и контекст отобрали, как теперь лёгкий DI пилить?
И запросы на сервер странные. Формат намеренно не документирован.
Разработчикам SPA всё это не нравится. Они обеспокоены. Для SSR они готовы брать другие решения. Почему Реакт отговаривает от SPA? Почему официальная документация рекомендует Next, а Next рекомендует RSC? Может быть это способ помочь Vercel заработать на React? Ведь для RSC нужен бэкенд, а бэкенд нужно где-то запускать.
Но отвечая на заглавный вопрос, проходит ли React свой «момент Angular», автор говорит — нет, не проходит. Потому что мы всё ещё можем писать «по-старому». Без Server Components.
Но в то же время пользователи теперь вынуждены выбирать между старым рабочим решением и новым сияющим активно рекламируемым. И это опасно. Между двух альтернатив выбора может возникнуть третья — иной фреймворк. И вот тут React вполне может нанести вред своему сообществу. Зачем мне RSC, с той же долей риска я могу перейти на Solid или ещё куда-нибудь.
В заключении Франсуа призывает команды React и Next одуматься и притушить рекламный поток для RSC. Не выпячивать это решение как противовес «классике» и единственное возможное будущее, чтобы не навредить экосистеме React.
Marmelab
Is React Having An Angular.js Moment?
React Server Components, while innovative, risk causing division in the React community due to their potential to undermine Single-Page App architecture.
👍65❤8😱1💘1
Студент прислал вопрос — почему этот код тормозит?
Вот эта штука крутится почти две минуты. И даже убрать тело цикла в функцию, чтобы гарантированно попасть в кэши JIT никак не помогает. Неужели JIT настолько плох, что не может оптимизировать бесполезные вызовы?
Что же, открываем Deopt Explorer и ищем проблему. На самом деле, вот этот код будет тормозить точно так же:
Deopt Explorer сразу даёт нам причину деоптимизации —
Проверим?
А вот мы вышли за границы SMI:
Такой подход называется pointer tagging — за счёт отдельного бита мы можем точно сказать, что в данном случае у нас не указатель на heap, а непосредственно значение примитива. Т.е. брать значение прямо из стека, а не бегать за ним в кучу. Получается, что как только мы выходим за границу SMI мы уже начинаем работать с объектом в куче и теряем в производительности.
В итоге как обычно попали в ловушку синтетических тестов и протестировали не то, что хотели протестировать, но многое поняли :)
UPD
Бенчмарки для оригинального цикла: 1:35.161 (m:ss.mmm)
Для решения с вложенным циклом (чтобы указатели остались в smi): 48.156s
А если мы поможем JIT и вложенный цикл уберём в функцию, то: 32.325s
console.time ("answer time");
const max = 100000000000;
console.log(' max=', max);
let i = 0;
while(i < max) {
const y = Math.pow(2,100);
const z = Math.pow(15, 100000);
i++;
}
console.timeEnd("answer time");
Вот эта штука крутится почти две минуты. И даже убрать тело цикла в функцию, чтобы гарантированно попасть в кэши JIT никак не помогает. Неужели JIT настолько плох, что не может оптимизировать бесполезные вызовы?
Что же, открываем Deopt Explorer и ищем проблему. На самом деле, вот этот код будет тормозить точно так же:
const max = 100000000000;
let i = 0;
while(i < max) {
i++;
}
Deopt Explorer сразу даёт нам причину деоптимизации —
i++ (overflow). Что за дела, спросите вы? Это же далеко не Number.MAX_SAFE_INTEGER, запаса достаточно. Дело в том, что V8 (как и другие JS-движки) умеет эффективно работать со SMI (small integers). На 64-битных платформах это соответственно диапазон от -2³¹ до 2³¹-1.
Проверим?
%DebugPrint(2147483647);
DebugPrint: Smi: 0x7fffffff (2147483647)
А вот мы вышли за границы SMI:
%DebugPrint(2147483648);
DebugPrint: 2147483648.0
0x148993d415e9: [Map] in ReadOnlySpace
- type: HEAP_NUMBER_TYPE
- instance size: 16
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x148993d415b9 <undefined>
- prototype_validity cell: 0
- instance denoscriptors (own) #0: 0x148993d41269 <Other heap object (STRONG_DESCRIPTOR_ARRAY_TYPE)>
- prototype: 0x148993d41339 <null>
- constructor: 0x148993d41339 <null>
- dependent code: 0x148993d41251 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
Такой подход называется pointer tagging — за счёт отдельного бита мы можем точно сказать, что в данном случае у нас не указатель на heap, а непосредственно значение примитива. Т.е. брать значение прямо из стека, а не бегать за ним в кучу. Получается, что как только мы выходим за границу SMI мы уже начинаем работать с объектом в куче и теряем в производительности.
В итоге как обычно попали в ловушку синтетических тестов и протестировали не то, что хотели протестировать, но многое поняли :)
UPD
Бенчмарки для оригинального цикла: 1:35.161 (m:ss.mmm)
Для решения с вложенным циклом (чтобы указатели остались в smi): 48.156s
А если мы поможем JIT и вложенный цикл уберём в функцию, то: 32.325s
🔥58❤19🤯8👍6❤🔥1
Нашёл неплохую статью с подробным объяснением того, как JS работает с памятью
TL;DR:
All JavaScript values are allocated on the heap accessed by pointers no matter if they are objects, arrays, strings or numbers (except for small integers i.e. smi in V8 due to pointer tagging).
The stack only stores temporary, function-local and small variables (mostly pointers) and that's largely unrelated to JavaScript types.
https://www.zhenghao.io/posts/javanoscript-memory
TL;DR:
All JavaScript values are allocated on the heap accessed by pointers no matter if they are objects, arrays, strings or numbers (except for small integers i.e. smi in V8 due to pointer tagging).
The stack only stores temporary, function-local and small variables (mostly pointers) and that's largely unrelated to JavaScript types.
https://www.zhenghao.io/posts/javanoscript-memory
👍29❤1
Немного удивлён, что никто не обратил внимания на фразу «На 64-битных платформах диапазон значений для SMI от -2³¹ до 2³¹-1». А для 32-битных как? А почему?
32-битные системы хранят указатели и SMI вот так (речь про v8)
64-битные вот так:
Т.е. в 32-битных системах мы теряем один бит на то, чтобы запомнить (протегировать) — указатель у нас или SMI, в SMI влазит только int31. В 64-битных системах метка (тег) переезжает в «свободные» 32-бита и мы можем хранить int32.
Кстати, с этим связан один неприятный момент. Когда в V8 решили включить Pointer Compression (сжатие указателей), то оказалось, что да, мы можем хранить два 32-битных указателя в одном 64-битном слове (там применяется достаточно сложный алгоритм), но вот снова положить два int31 — это приводит к деградации производительности (1% на Octane). А два int32 не помещаются, негде хранить тег.
Так как производительность важнее, а SMI у нас не так много в коде, то оставили как есть, всё равно выигрыш по памяти получился приличный, потребление практически вернулось к значениям для 32-битных систем.
UPD
В комментах @cevek подсказал, что всё же включили компрессию для SMI, и в браузере в SMI будет именно int31, а не int32 «как раньше».
С нодой интереснее, в ноде компрессия указателей изначально не была включена из-за проблем совместимости с нативными аддонами, потому мы продолжаем видеть в SMI int32. Есть такое обсуждение.
32-битные системы хранят указатели и SMI вот так (речь про v8)
|----- 32 bits -----|
Pointer: |_____address_____w1|
Smi: |___int31_value____0|
64-битные вот так:
|----- 32 bits -----|----- 32 bits -----|
Pointer: |________________address______________w1|
Smi: |____int32_value____|0000000000000000000|
Т.е. в 32-битных системах мы теряем один бит на то, чтобы запомнить (протегировать) — указатель у нас или SMI, в SMI влазит только int31. В 64-битных системах метка (тег) переезжает в «свободные» 32-бита и мы можем хранить int32.
Кстати, с этим связан один неприятный момент. Когда в V8 решили включить Pointer Compression (сжатие указателей), то оказалось, что да, мы можем хранить два 32-битных указателя в одном 64-битном слове (там применяется достаточно сложный алгоритм), но вот снова положить два int31 — это приводит к деградации производительности (1% на Octane). А два int32 не помещаются, негде хранить тег.
UPD
В комментах @cevek подсказал, что всё же включили компрессию для SMI, и в браузере в SMI будет именно int31, а не int32 «как раньше».
%DebugPrint(2147483647)
DebugPrint: 2147483647.0
0x5f700000339: [Map] in ReadOnlySpace
С нодой интереснее, в ноде компрессия указателей изначально не была включена из-за проблем совместимости с нативными аддонами, потому мы продолжаем видеть в SMI int32. Есть такое обсуждение.
🔥14🤯6❤2👍1
Красота какая едет к нам в TS 5.2 (да и в ECMAScript тоже). С новой декларацией
Закончили работу — освободили ресурс почти автоматически (всё равно код придётся написать).
Подробнее тут
using мы сможем вешать коллбеки на событие того, что ресурс больше не используется.Закончили работу — освободили ресурс почти автоматически (всё равно код придётся написать).
Подробнее тут
🔥47❤2🤯2🆒1
В ноде есть встроенный механизм песочницы для запуска опасного кода.
Но вот ещё про одну забавную проблему расскажу.
Как можно подвесить процесс через песочницу? Очень просто:
Но вы скажете, что есть же параметр timeout:
Уже лучше. Но проблема в том, что timeout не ловит микротаски. Выносим луп в микротаску и снова DoS
Ок, проблема известная, в 15-й ноде дырку заделали фанерой.
Почему такое апи? Ума не приложу :) Но в этом режиме песочница разматывает микротаски сразу после макротасок и таймаут общий. Почти хорошо стало, но что если мы решили передать в песочницу что-нибудь асинхронное? И пользователь сможет выйти на следующий луп. Тогда ой:
Всё попало, шеф. У нас DoS.
В общем, посмотрите лучше на vm2 или на более интересную изоляцию через isolated-vm
vm.runInNewContext(<код>, <контекст>) и погнали. Лучше и безопаснее eval, но... со своими проблемами. Всё ещё можно вылезти из песочницы и ударить в глобал. this.constructor.constructor и побежали веселиться. Но вот ещё про одну забавную проблему расскажу.
Как можно подвесить процесс через песочницу? Очень просто:
vm.runInNewContext('while(true){}',{});
Но вы скажете, что есть же параметр timeout:
vm.runInNewContext('while(true){}',{timeout: 5});
Уже лучше. Но проблема в том, что timeout не ловит микротаски. Выносим луп в микротаску и снова DoS
vm.runInNewContext(
'Promise.resolve().then(()=>{while(true) {}});',
{},
{timeout:5}
);
Ок, проблема известная, в 15-й ноде дырку заделали фанерой.
vm.runInNewContext(
'Promise.resolve().then(()=>{while(true) {}});',
{},
{timeout:5, microtaskMode: 'afterEvaluate'}
);
Почему такое апи? Ума не приложу :) Но в этом режиме песочница разматывает микротаски сразу после макротасок и таймаут общий. Почти хорошо стало, но что если мы решили передать в песочницу что-нибудь асинхронное? И пользователь сможет выйти на следующий луп. Тогда ой:
vm.runInNewContext(
`
setTimeout(() => {
while(true) {};
}, 10000);`,
{setTimeout},
{timeout:5, microtaskMode: 'afterEvaluate'}
);
Всё попало, шеф. У нас DoS.
В общем, посмотрите лучше на vm2 или на более интересную изоляцию через isolated-vm
GitHub
vm: add run-after-evaluate microtask mode · nodejs/node@f63436d
This allows timeouts to apply to e.g. `Promise`s and `async function`s
from code running inside of `vm.Context`s, by giving the Context its
own microtasks queue.
Fixes: https://github.com/nodejs/n...
from code running inside of `vm.Context`s, by giving the Context its
own microtasks queue.
Fixes: https://github.com/nodejs/n...
❤20🔥10👍4👀1
Метапакеты
Часто с возрастом появляется желание иметь пакет для пакетов — положить в него всё нужное и накатывать одной командой. Чтобы он привозил и eslint, и prettier и browserslist. Вот только npm не предназначен для метапакетов. npm не гарантирует, что транзитивные зависимости будут лежать плоско на первом уровне. Вот вообще не гарантирует. А пакеты на это завязываются.
Классика — проблемы с плагинами eslint которые не могут лежать, на втором уровне. Ну ок, это кажется починили в новой системе конфигов (я ещё не попроовал).
Или вот
Предположим такую схему зависимостей:
Предположим у вас npm 6 или вы работаете в режиме
Вроде не страшно?
А теперь делаем
Всё сломалось!
Да, если у вас npm 7 и выше и вы перешли на новую работу с peerDeps то получите
Может быть. А может повезёт и оно дедупнется. Гарантий нет. В этом и проблема метапакетов. Они не дают гарантий, транзитивные зависимости нельзя магически превратить в зависимости первого уровня.
Что тут можно сделать? Ну, например, написать скрипт, который контролирует версии базовых зависимостей и сам делает пул-реквесты по их поднятию. Либо остаться на мета-пакете и очень внимательно следить за структурой.
Часто с возрастом появляется желание иметь пакет для пакетов — положить в него всё нужное и накатывать одной командой. Чтобы он привозил и eslint, и prettier и browserslist. Вот только npm не предназначен для метапакетов. npm не гарантирует, что транзитивные зависимости будут лежать плоско на первом уровне. Вот вообще не гарантирует. А пакеты на это завязываются.
Классика — проблемы с плагинами eslint которые не могут лежать, на втором уровне. Ну ок, это кажется починили в новой системе конфигов (я ещё не попроовал).
Или вот
browserslist-useragent перенёс browserslist в peerDependencies. Что это значит? Где тут проблемы метапакетов?Предположим такую схему зависимостей:
dependencies
<метапакет>
browserslist-useragent: 4.2.2
browserslist: 4.2.2
devDependencies
webpack
browserslist: 4.1.1
Предположим у вас npm 6 или вы работаете в режиме
--legacy-peer-deps. Ставим зависимости и оказываемся в ситуации
node_modules
<метапакет>
node_modules
browserslist: 4.2.2
webpack
browserslist: 4.1.1
browserslist-useragent: 4.2.2
Вроде не страшно?
А теперь делаем
npm prune --production
node_modules
<метапакет>
node_modules
browserslist: 4.2.2
browserslist-useragent: 4.2.2
Всё сломалось!
browserslist-useragent больше не видит browserslist и падает.Да, если у вас npm 7 и выше и вы перешли на новую работу с peerDeps то получите
node_modules
<метапакет>
node_modules
browserslist: 4.2.2
browserslist-useragent: 4.2.2
node_modules
browserslist: 4.2.2
Может быть. А может повезёт и оно дедупнется. Гарантий нет. В этом и проблема метапакетов. Они не дают гарантий, транзитивные зависимости нельзя магически превратить в зависимости первого уровня.
Что тут можно сделать? Ну, например, написать скрипт, который контролирует версии базовых зависимостей и сам делает пул-реквесты по их поднятию. Либо остаться на мета-пакете и очень внимательно следить за структурой.
GitHub
Make browserslist a peer dependency · browserslist/browserslist-useragent@ee7bb63
Fixes #73
👍22❤3❤🔥1
Кто-то уже слышал, кто-то ещё услышит, что Андрей Ситник порекомендовал переезжать с PostCSS на Lightning CSS. Очередная тулза в экосистеме, написанная на Rust. В наших рабочих сборках она уже появилась (под флагом), и вот захожу я в зависимости и вижу:
Прикольно. Это тебе не node-pre-gyp выкачивающий готовые билды с S3 и ломающийся на твоём защищённом от внешнего мира CI. А как так сделали?
Довольно изящно. У пакета прописаны опциональные зависимости от бинарников
ightning CSS. Очередная тулза в экосистеме, написанная на Rust. В
Так как зависимости указаны опциональными, то npm при их установке тихо отстреливает те, что сфейлились.
И остаётся только та, что нужна (в моём случае `lightningcss-darwin-arm64`).
lightningcss
lightningcss-darwin-arm64
Прикольно. Это тебе не node-pre-gyp выкачивающий готовые билды с S3 и ломающийся на твоём защищённом от внешнего мира CI. А как так сделали?
Довольно изящно. У пакета прописаны опциональные зависимости от бинарников
У каждого такого пакета с бинарником в
"optionalDependencies": {
"lightningcss-darwin-x64": "1.21.5",
"lightningcss-linux-x64-gnu": "1.21.5",
"lightningcss-win32-x64-msvc": "1.21.5",
"lightningcss-darwin-arm64": "1.21.5",
"lightningcss-linux-arm64-gnu": "1.21.5",
"lightningcss-linux-arm-gnueabihf": "1.21.5",
"lightningcss-linux-arm64-musl": "1.21.5",
"lightningcss-linux-x64-musl": "1.21.5"
}
package.json указана операционная система и архитектура:ightning CSS. Очередная тулза в экосистеме, написанная на Rust. В
Так как зависимости указаны опциональными, то npm при их установке тихо отстреливает те, что сфейлились.
npm verb reify failed optional dependency /Users/melikhov/test/node_modules/lightningcss-darwin-x64
npm sill reify mark deleted [ '/Users/melikhov/test/node_modules/lightningcss-darwin-x64' ]
И остаётся только та, что нужна (в моём случае `lightningcss-darwin-arm64`).
YouTube
Andrey Sitnik - PostCSS, Browserslist, Autoprefixer, Evil Martians
This week we are joined by Andrey Sitnik, the creator of PostCSS, Browserslist, Autoprefixer, and many other tools. We talk about the origins of PostCSS, the future of CSS, and the power of open source and distributed applications. We also go into OKLCH,…
🔥80👍11❤6😍2
Вот тут Серёжа печатает про джунов. Как им (представителям эдтеха) видится ситуация с наймом новичков. Мне, как человеку поработавшему в крупных энтерпрайзах и небольших и больших стартапах, нанимавшему и обучавшему — ситуация видится иначе. Сразу скажу, что это мой взгляд, правды тут может быть ни на грош, в бизнесе я ни в зуб ногой.
Как обычно, всё в упирается в деньги. Бесплатные внутренние школы, конференции, курсы, хакатоны, и так далее и тому подобное, всё это так или иначе должно решить задачи найма и выйти в плюс на длинных горизонтах планирования. Иногда, при хорошем положительном балансе на счету компания может сделать что-то и в минус. Просто так, потому что технарь у руля решил, что надо не только брать, но и отдавать. Но это редкость, корабль всё же должен оставаться на плаву.
Так что не так с джунами? Проблема в том, что на коротком горизонте джун в крупной компании/сложном проекте генерирует убыток. Это не связано с его зарплатой, это связано с тем, что введение в команду джуна снизит производительность миддла или синьора, который будет этого джуна обучать. Чем сложнее проект, чем больше в компании необычной специфики — тем больше нужно потратить сил на погружение человека. И чем больше вы набираете джунов, тем меньше бизнес-вэлью производит команда.
Уменьшить этот эффект можно внутренними школами. Взять сразу много разработчиков, посадить их за парты и познакомить со всей спецификой комплексно и сразу. Вместо того чтобы один синьор занимался одним джуном — он обучит сразу 10/20/30. И стоимость погружения в проект снижается. Но подводный камень тут есть, даже подводная скала и имя ей снова деньги. Внутренняя школа стоит дорого, как минимум она стоит времени всех задействованных в ней людей (а нужны люди с хорошими скиллами). И построить такие школы могут только достаточно крупные компании, которые, опять же, мыслят длинными горизонтами планирования. Им нужна база хороших разработчиков на несколько лет вперёд.
Стартапы и небольшие компании себе такого позволить не могут. Им нужны рабочие руки здесь и сейчас. Каждая копейка на счету (если конечно цель стартапа не заключается в прожигании денег инвесторов). Им проще заплатить выше рынка и запылесосить миддлов и синьоров, чем взращивать джунов — потому что не известно, понадобятся ли тебе сильные роботяги завтра, если ты не успеешь сегодня.
Ну и остаются просто небольшие компании, где джун способен справиться сам. Вот там проблемы нет, можно масштабироваться джунами, но учиться придётся на месте и самому, а зарплата... ну так в этом и был смысл масштабирования на джунах
А поинт мой в том, что не стоит думать, что компании такие плохие, не хотят брать новичков. Да нет, хотят, но просто не могут себе это позволить. Баланс не сходится.
Как обычно, всё в упирается в деньги. Бесплатные внутренние школы, конференции, курсы, хакатоны, и так далее и тому подобное, всё это так или иначе должно решить задачи найма и выйти в плюс на длинных горизонтах планирования. Иногда, при хорошем положительном балансе на счету компания может сделать что-то и в минус. Просто так, потому что технарь у руля решил, что надо не только брать, но и отдавать. Но это редкость, корабль всё же должен оставаться на плаву.
Так что не так с джунами? Проблема в том, что на коротком горизонте джун в крупной компании/сложном проекте генерирует убыток. Это не связано с его зарплатой, это связано с тем, что введение в команду джуна снизит производительность миддла или синьора, который будет этого джуна обучать. Чем сложнее проект, чем больше в компании необычной специфики — тем больше нужно потратить сил на погружение человека. И чем больше вы набираете джунов, тем меньше бизнес-вэлью производит команда.
Уменьшить этот эффект можно внутренними школами. Взять сразу много разработчиков, посадить их за парты и познакомить со всей спецификой комплексно и сразу. Вместо того чтобы один синьор занимался одним джуном — он обучит сразу 10/20/30. И стоимость погружения в проект снижается. Но подводный камень тут есть, даже подводная скала и имя ей снова деньги. Внутренняя школа стоит дорого, как минимум она стоит времени всех задействованных в ней людей (а нужны люди с хорошими скиллами). И построить такие школы могут только достаточно крупные компании, которые, опять же, мыслят длинными горизонтами планирования. Им нужна база хороших разработчиков на несколько лет вперёд.
Стартапы и небольшие компании себе такого позволить не могут. Им нужны рабочие руки здесь и сейчас. Каждая копейка на счету (если конечно цель стартапа не заключается в прожигании денег инвесторов). Им проще заплатить выше рынка и запылесосить миддлов и синьоров, чем взращивать джунов — потому что не известно, понадобятся ли тебе сильные роботяги завтра, если ты не успеешь сегодня.
Ну и остаются просто небольшие компании, где джун способен справиться сам. Вот там проблемы нет, можно масштабироваться джунами, но учиться придётся на месте и самому, а зарплата... ну так в этом и был смысл масштабирования на джунах
А поинт мой в том, что не стоит думать, что компании такие плохие, не хотят брать новичков. Да нет, хотят, но просто не могут себе это позволить. Баланс не сходится.
Telegram
··• Серёжа печатает
Дальше о джунах. Я писал о проблеме с джунами, но одну решил выделить.
Компании поделились на два лагеря.
Первый лагерь говорит, что с джунами надо работать, с ними возможно выстроить процессы, научиться отбирать и растить, сохранять в компании. Под них…
Компании поделились на два лагеря.
Первый лагерь говорит, что с джунами надо работать, с ними возможно выстроить процессы, научиться отбирать и растить, сохранять в компании. Под них…
👍52❤12🤡3🤔1💩1
Я не использую разные удобные нестандартные утилиты (httpie, curlie и.т.д) и не пишу алиасы(альясы?) для команд по той простой причине, что когда я зайду в очередной контейнер — там ничего этого не будет. И даже mc не будет. И история команд будет пустая. И дефолтным редактором будет vi.
Вот и делаю профилактику памяти каждый день.
Вот и делаю профилактику памяти каждый день.
🔥32👍20❤4😁4👎2🤔2😢2💊2
Разруливаем CORS на NGINX
Как известно, в Node.js всё наоборот. Не приложение запускается внутри сервера, а сервер поднимается внутри приложения. И бывает, что нужно делать интересные приседания для казалось бы базовых вещей. Вот, например, CORS. Стандарт сам по себе вычурный — сначала браузер должен отправить preflight-запрос типа OPTIONS и получить в ответе заголовок
Плюс, заголовок может понадобиться, а может и не понадобиться для `GET`-запроса (зависит от того, посчитает ли его эвристика браузера достаточно «простым»). Критерии «простоты» достаточно мутные и гуляют от браузера к браузеру.
Итак, при разработке апишек мы приходим к необходимости интегрировать в наше приложение знание о спецификах CORS. Но зачем, у нас же есть Nginx, который прекрасно с этим справится, а заодно разгрузит наше приложение от «ненужных» OPTIONS-запросов.
Магия конфигов, например:
Таким образом, если наш допустимый источник запроса попал в регулярку, то мы запишем его имя в
Тут ещё может стать проблемой если в приложении подключена CORS-миддлвара и она тоже захочет сама прописать заголовки, когда до неё дойдёт какой-нибуть POST-запрос. Ну это тоже Nginx-умеет чистить
К вопросу о том, зачем фронтендерам базовый девопс.
Как известно, в Node.js всё наоборот. Не приложение запускается внутри сервера, а сервер поднимается внутри приложения. И бывает, что нужно делать интересные приседания для казалось бы базовых вещей. Вот, например, CORS. Стандарт сам по себе вычурный — сначала браузер должен отправить preflight-запрос типа OPTIONS и получить в ответе заголовок
Access-Control-Allow-Origin, в котором будет сказано, что данные можно раздавать или всем (`*`) или конкретному ориджину (`mysite.com`) который мы установим вручную на сервере и отдадим в ответе. Вайлдкэрд тут недопустим (никаких *.mysite.com, только a.mysite.com ).Плюс, заголовок может понадобиться, а может и не понадобиться для `GET`-запроса (зависит от того, посчитает ли его эвристика браузера достаточно «простым»). Критерии «простоты» достаточно мутные и гуляют от браузера к браузеру.
Итак, при разработке апишек мы приходим к необходимости интегрировать в наше приложение знание о спецификах CORS. Но зачем, у нас же есть Nginx, который прекрасно с этим справится, а заодно разгрузит наше приложение от «ненужных» OPTIONS-запросов.
Магия конфигов, например:
map $http_origin $cors_header {
default "";
"~^https://[^/]+\.mydomain\.com(:[0-9]+)?$" "$http_origin";
}
server {
add_header Access-Control-Allow-Headers "Content-Type" always;
add_header Access-Control-Allow-Origin $cors_header always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Max-Age: 86400;
if ($request_method = OPTIONS) {
return 200;
}
}
Таким образом, если наш допустимый источник запроса попал в регулярку, то мы запишем его имя в
Access-Control-Allow-Origin. А если это был OPTIONS запрос, то даже не будем отправлять его глубже в приложение, а сразу ответим браузеру статусом 200 и правильным заголовком.Тут ещё может стать проблемой если в приложении подключена CORS-миддлвара и она тоже захочет сама прописать заголовки, когда до неё дойдёт какой-нибуть POST-запрос. Ну это тоже Nginx-умеет чистить
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Headers;
...
К вопросу о том, зачем фронтендерам базовый девопс.
👍83🔥8❤🔥1
Марк Эриксон «Мой опыт модернизации пакетов до ESM»
ссылка на оригинал
Наши мнения можете послушать в подкасте, а ниже вольный пересказ (статья больно интересная).
TL;DR Марк мейнтейнер redux, react-redux, redux-thunk, reselect и @reduxjs/toolkit. В конце 2022 он попытался перетащить эти пакеты на ESM но к августу 2023 так и не смог закончить работу. Потому что всё очень плохо.
Итак, на дворе 2022 и Марк садится за работу. Пора бы уже перетащить пакеты на ESM! В этот момент он осознаёт, что нет ни одного руководства, как сделать универсальный пакет, который будет работать везде. Как вы можете догадаться, он не хочет идти по пути Синдре Сорхуса и выпускать Pure ESM, нет, он хочет оставить совместимость для старых приложений и поддержать вообще всё, что можно. Включая Webpack 4. Похвальное желание.
Прочитав 175 статей он приходит к выводу, что нужно:
- добавить
- прописать exports с энтри-поинтами для разных окружений
- кажется нужно поставить расширение
Дело нехитрое, Марк собирает RTK с новым
Альфа собрана, опубликована и... БУМ! Тайпскрипт с
- Если мы написали
- Если мы не написали
Всё по спеке! Да, бандлерам зачастую тут по барабану, они проглотят и так, но Node.js требовательна. Открытым остаётся вопрос нужно ли делать несколько
Марк собирается с духом и делает ещё один заход. Чтобы было проще, он откладывает в сторону RTK и берёт в руки
Тут он узнаёт, что Webpack 4 всё ещё популярнее Webpack 5. А Webpack 4 не поддерживает поле exports и ему не нравится .mjs в поле main. А так же Webpack 4 не понимает optional chaining. Придётся для него сделать артефакт-фоллбек: ESM в синтаксисе ES2017, с расширением .js. И пусть этот артефакт лежит в поле main.
Кажется победа близка, альфы выпущены, отзывы хорошие. Всё работает!
В начале мая выходит
Тут Марк сгорел. Он потратил месяцы на стабилизацию сборки и вот такая засада — нужно пилить новое решение чтобы не ломались RSC. И никакой нормальной документации нет. Просто всё стало сложнее, для мейнтейнеров пакетов.
Ну и вывод в целом такой, что переход с CJS на ESM это кошмар и конца ему не видно. А тут ещё и RSC. Удивительно, что наша экосистема хоть как-то работает. А Redux Toolkit 2.0 выйдет, точно выйдет.
ссылка на оригинал
Наши мнения можете послушать в подкасте, а ниже вольный пересказ (статья больно интересная).
TL;DR Марк мейнтейнер redux, react-redux, redux-thunk, reselect и @reduxjs/toolkit. В конце 2022 он попытался перетащить эти пакеты на ESM но к августу 2023 так и не смог закончить работу. Потому что всё очень плохо.
Итак, на дворе 2022 и Марк садится за работу. Пора бы уже перетащить пакеты на ESM! В этот момент он осознаёт, что нет ни одного руководства, как сделать универсальный пакет, который будет работать везде. Как вы можете догадаться, он не хочет идти по пути Синдре Сорхуса и выпускать Pure ESM, нет, он хочет оставить совместимость для старых приложений и поддержать вообще всё, что можно. Включая Webpack 4. Похвальное желание.
Прочитав 175 статей он приходит к выводу, что нужно:
- добавить
"type": "module"в
package.jsonчтобы показать, что это ESM
- прописать exports с энтри-поинтами для разных окружений
- кажется нужно поставить расширение
.mjs, но это уродливо, так что нафиг
Дело нехитрое, Марк собирает RTK с новым
package.json, всё выглядит неплохо, он кидает PR и БУМ, всё сломалось. Да, рантайм работает как надо, но вот
Jestотвалился.
Jestне переварил `
export default` в ESM и как бы Марк не пытался обойти проблему — ничего не вышло. Что же, пришлось перейти на
Vitest. Прощай,
Jest.
Альфа собрана, опубликована и... БУМ! Тайпскрипт с
moduleResolution
node16и
nodenextне может вытащить типы. А
Node.jsне может работать
.jsCommonJS если пакет переключен впакеты на ESM но к а CommonJS файлы в таких пакетах должны иметь расширение
.cjs.
- Если мы написали
"type": "module"то используем
.jsдля ESM и
.cjsдля CommonJS
- Если мы не написали
"type": "module"то используем
.mjsдля ESM и
.jsдля CommonJS
Всё по спеке! Да, бандлерам зачастую тут по барабану, они проглотят и так, но Node.js требовательна. Открытым остаётся вопрос нужно ли делать несколько
d.ts-файлов. Есть мнение, что нужно.
Марк собирается с духом и делает ещё один заход. Чтобы было проще, он откладывает в сторону RTK и берёт в руки
redux-thunk. 20 строк кода, что может пойти не так? Выкидывает
Babelс
Rollup, берет в руки
ESBuildи tsup. Пара часов игры с конфигом и вот на выходе два файла:
.mjsи
.cjs
Тут он узнаёт, что Webpack 4 всё ещё популярнее Webpack 5. А Webpack 4 не поддерживает поле exports и ему не нравится .mjs в поле main. А так же Webpack 4 не понимает optional chaining. Придётся для него сделать артефакт-фоллбек: ESM в синтаксисе ES2017, с расширением .js. И пусть этот артефакт лежит в поле main.
Кажется победа близка, альфы выпущены, отзывы хорошие. Всё работает!
В начале мая выходит
Next 13.4. Да-да, тот самый, с React Server Components. Те самые RSC в которых не работает код использующий контекст.
React.createContext() и useLayoutEffect — они отсутствовали на сервере и взрывали код. Команда Next предложила использовать отдельный export react-server в который положить код оптимизированный для серверных компонент (рекомендация с «use client» появилась чуть позже).Тут Марк сгорел. Он потратил месяцы на стабилизацию сборки и вот такая засада — нужно пилить новое решение чтобы не ломались RSC. И никакой нормальной документации нет. Просто всё стало сложнее, для мейнтейнеров пакетов.
Ну и вывод в целом такой, что переход с CJS на ESM это кошмар и конца ему не видно. А тут ещё и RSC. Удивительно, что наша экосистема хоть как-то работает. А Redux Toolkit 2.0 выйдет, точно выйдет.
Mark's Dev Blog
Blogged Answers: My Experience Modernizing Packages to ESM
Details on the painful experiences and hard-earned lessons I've learned migrating the Redux packages to ESM
👍58🔥10😱6😁5😢2👻1
По поводу тайпогейта
Тут на днях всех взбудоражило, что DHH выпилил тайпскрипт из Турбо 8 (про то, как это было сделано, это отдельная тема для разговора). На этом фоне как-то незаметно прошло майское выпиливание тайпскрипта из Svelte. И вот Николас Закас вспомнил, с какой агрессией столкнулся, рассказав, что новый ESLint не будет переписан на TS. Неудобствами работы с TS делился и Маттео Коллина. И Тимур Шемсединов активно выступает против.
Что тут важно. Во всех этих случаях речь идёт про библиотеки, про низкоуровневый код. Очень хорошо Рич Харрис ответил на вопросы про Свелт:
«Мы не отказываемся от типобезопасности. Мы обожаем статическую типизацию. Мы просто переносим описание типов в JSDoc. Это не сломает ничего для потребителя фреймвока — более того, пакеты станут меньше и вы сможете по клику по функции сразу попадать в её реализацию, вместо бесполезного для вас объявления типов. Да, писать JSDoc не так удобно, но в остальном DX лучше (мы уже давно работаем так со SvelteKit)»
Полностью поддерживаю. Достаточно неудобно работать в большом проекте, разбитом на множество TS-библиотек, вся эта дихотомия ts и js файлов мешает. Код написанный только для удовлетворения TS мешает. Дебаг с сорсмапами мешает. Я ловлю большой кайф, когда для ноды пишу на чистом JS и мне не нужно никаких дополнительных инструментов и шагов транспиляции. Да, Bun и Deno решают эти вопросы, для них *.ts это входной формат файлов. Для ноды — нет (а потом мы видим как либы тащат ts-node ради копеечных скриптов).
Но, конечно, когда мы переходим к описанию бизнес-логики, вот тут я ни за что не готов слезать с TS. Вот тут бы я очень удивился, если бы какой-то проект внезапно решил соскочить со статической типизации (если, конечно, статическая типизация не состояла из обмазывания кода
Тут на днях всех взбудоражило, что DHH выпилил тайпскрипт из Турбо 8 (про то, как это было сделано, это отдельная тема для разговора). На этом фоне как-то незаметно прошло майское выпиливание тайпскрипта из Svelte. И вот Николас Закас вспомнил, с какой агрессией столкнулся, рассказав, что новый ESLint не будет переписан на TS. Неудобствами работы с TS делился и Маттео Коллина. И Тимур Шемсединов активно выступает против.
Что тут важно. Во всех этих случаях речь идёт про библиотеки, про низкоуровневый код. Очень хорошо Рич Харрис ответил на вопросы про Свелт:
«Мы не отказываемся от типобезопасности. Мы обожаем статическую типизацию. Мы просто переносим описание типов в JSDoc. Это не сломает ничего для потребителя фреймвока — более того, пакеты станут меньше и вы сможете по клику по функции сразу попадать в её реализацию, вместо бесполезного для вас объявления типов. Да, писать JSDoc не так удобно, но в остальном DX лучше (мы уже давно работаем так со SvelteKit)»
Полностью поддерживаю. Достаточно неудобно работать в большом проекте, разбитом на множество TS-библиотек, вся эта дихотомия ts и js файлов мешает. Код написанный только для удовлетворения TS мешает. Дебаг с сорсмапами мешает. Я ловлю большой кайф, когда для ноды пишу на чистом JS и мне не нужно никаких дополнительных инструментов и шагов транспиляции. Да, Bun и Deno решают эти вопросы, для них *.ts это входной формат файлов. Для ноды — нет (а потом мы видим как либы тащат ts-node ради копеечных скриптов).
Но, конечно, когда мы переходим к описанию бизнес-логики, вот тут я ни за что не готов слезать с TS. Вот тут бы я очень удивился, если бы какой-то проект внезапно решил соскочить со статической типизации (если, конечно, статическая типизация не состояла из обмазывания кода
any). А либы — да всё нормально, это вполне понятное решение. Лишь бы тесты были, да d.ts наружу поставляли.👍89🥴8❤4🤔3🔥1