Мой уголок – Telegram
Мой уголок
273 subscribers
2 photos
28 links
Щит-репосты и посты о всяких технологиях и не только. @bondiano
Download Telegram
Channel created
Недавно столкнулся с проблемой, что требуется https для локальной разработки. Решение нашел довольно быстро: Ngrok. Это платформа с удобной CLI утилитой (написана, кстати, на node.js), которая позволяет организовать удалённый доступ на веб-сервер, ну или любой другой сервер, запущенный на ПК. ПК при этом может не иметь статического IP адреса или быть за NAT'ом.
Так что если вам нужно показать что-то локально поднятое, а ещё желательно, чтобы SSL или TLS сверху накинули эта тулза отлично подойдет.
https://ngrok.com/
#w_corner_tools
Чисто лол-кек, В Хроме, 6**9 === Math.pow(6, 9) //false.
Очередная крутая фича браузера и зачем нам эти браузерные войны в статье Саши Майорова.
https://medium.com/@frontman/funes-8-math-pow-не-равно-972955382594
З.Ы. в комментах-таки говорят, что в Chrome Nightly уже поправили
Деструктуризация динамических полей
Совсем недавно задумался над тем, как получать значение поля, которое было назначенно динамически.

const myActionName = 'ACTION'
const actionByActionName = { [myActionName]: action }

Кажется, что теперь мы не можем прямо из объекта получить свойство по ключу(одноименная переменная уже есть в скоупе). Но тут на помощь нам приходит переименование деструктуризированных свойств:

const { [myActionName]: actionName } = actionByActionName
child_process

В ноде есть 4 способа запуска дочернего процесса:
child_process

.spawn - начинает асинхронно отдтавать данные сразу, как запустится процесс. Его выгодно использовать при работе с большими объемами данных, например, с огромными файлами или выводом результатов утилит вроде curl. Данные получаем при помощи event-listener'а(.on('data', () => {...})).

.exec - ждёт от процесса сигнала завершения и получаем набор данных от него целиком, хорошо подходит, чтобы  получить какой-то не большой текст, например код статуса, данные возвращает в callback. При попытке работать с большим потоком вывода по-умолчанию нода вернёт ошибку.

.execFile - как exec, но напрямую запускает исполняемый файл, без оболочки.

.fork - как spawn, но позволяет обмениваться сообщениями с дочерними процессами через функции send/on.
#w_corner_nodejs
keyof в поисках типа

Жили были функции без типов. Среди них были такие,
как prop, возвращающие поля из объекта.


const prop = (obj, key) => obj[key]

Когда наступило время раздора и хаоса и пришли типы, неопытные программисты часто ошибались в том, как организовать верный вывод типов таких функций, но для любого поля результат был any. (и жили они без noImplicitAny)


const prop = (obj: {}, key: any) => obj[key]
const example = { meow: 'meow' }
prop(example, 'meow') // type: any


"Как так!", — восклицали они, — "Ведь мы видели, что по ключам всегда получаем тип!"

И были правы. В TypeScript существует поиск типов (Lookup Types) , который и позволяет получить связанный с ключем тип.

Тогда к ним пришел важный тип и дал новое магическое слово - keyof.

Оно давало особую способность проникать в публичные имена ключей типов и создавать литеральный объединенный тип (Union).*


const example = { meow: 'meow', murr: 'murr' }
type CatSays = keyof typeof example // "meow" | "murr"


Благодоря знанию о keyof и Lookup Types они быстро сообразили, как затипизировать их функцию prop:


const prop<T, K extends keyof T>(obj: T, key: K) => obj[key]

Теперь они смогут спокойно жить и припевать в типобезопасном мире, типизируя любые функции, которые как-то получают только нужные поля объекта!


* с помощью keyof можно получить все публичные не статические ключи пренадлежащие именно типу, потому в примере мы вынужденны получать запросом типа typeof.
#w_corner_ts
TSLint to ESLint

TSLint обещает скоро уже стать deprecated. И уже пора бы переносить все проекты с ним на новые рельсы. Очевидно, что единсвенный выбор сейчас - ESLint. Ну и куда же в современном проекте без Prettier'а. Пройдемся немного по шагам, которые сделал я для подобного переноса.

1. Во первых, не хотелось переформатировать проект, потому я сразу пошел искать тулзу, которая мне переконвертирует все мои правила из TSLint, и супер, что такая уже есть - tslint-to-eslint-config.

Запускаем npx tslint-to-eslint-config

2. Тулза оказалась достаточно не плохой. Но все нужные пакеты для использования ESLint она сама не поставила :(. Поставим сами, за одно добавим Prettier. (хотя и создатель сказал не использовать, но я продлжаю выбирать yarn в качестве своего менеджера зависимостей, как минимум тк я подсел на workspaces)

yarn add eslint eslint-config-prettier eslint-plugin-prettier prettier @typenoscript-eslint/parser @typenoscript-eslint/eslint-plugin @typenoscript-eslint/eslint-plugin-tslint -D


3. Перименуем eslintrc.js в .eslintrc.js, либо придётся добавить флаг в скрипте -c eslintrc.js.

4. Обновим поле noscripts в package.json.
"lint": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.tsx"

5. Добавим Prettier. Сперва в массив plugins - plugins: [..., 'prettier'], затем в rules - rules: { 'prettier/prettier': 'error', ... }

6. Почему-то tslint-to-eslint-config генерирует несколько не сущесвующих правил в плагине @typenoscript-eslint/eslint-plugin, тч после первого запуска lint'a вам придется их подтереть или поискать альтернативы.

6. PROFIT! Можно удалять tslint и избавляться от правил из @typenoscript-eslint/tslint плагина.

Некоторые мои проекты хранятся в монорепо, тч для нормальной работы ESLint потребовалась небольшая докрутка: parserOptions.project в .eslintrc.js поддерживает также массив, тч потребовалось внести каждое приложение, содержащее tsconfig.json отдельно.

Также vscode сразу не хотел обрабатывать нужные конфиги, не беда, в issue к плагину vscode-eslint предложили рабочее решение.

P.S. Все же с tslint-to-eslint-config некоторые правила останутся не перенесеными, тч смотрим также созланный тулзой файл tslint-to-eslint-config.log.
#w_corner_ts
Второй подход к снаряду поиска типов

Сколько же решений по типизации экшенов выдает Stackoverflow, вплоть до пакетов с тремя звездочками на Github и каждый способ не лишен изъянов.
Возьмем например довольно очевидный способ создания экшена "в лоб":

interface Action<T, P> {
readonly type: T;
readonly payload?: P;
}

const createAction = <T extends string, P>(type: T) => (payload: P): Action<T, P> => {
return { type, payload };
}

В таком случае от нас TS будет требовать пробрасывать вторым аргументом в дженерик payload, что нам не всегда нужно (тк нельзя в дженерик с двумя аргументами передавать только один, ну и если попробуем от этого избавиться, то все равно в конце концов мы теряем тип от type).
И по общей договоренности с командой мы решили, что тип поля type обязательно должен быть строкой.

И так, приступим исправлять типизацию.

Что же для нас такое этот createAction в итоге.
Это функция, принимающая type и payload и возвращающая объект с этими двумя полями, а главное сохраняющая типы.

const createAction = (type) => (payload) => ({
type,
payload,
})

И первый тип, с которым надо нам надо разобраться - это тип Action, содержащий payload, type и более базовый тип - ReduxAction (для него достаточно поля type)

interface ReduxAction<T extends string> {
type: T
}

export interface Action<Type extends string, Payload = void> extends ReduxAction<Type> {
payload?: Payload
}

Тут все довольно просто. Обернем в это createAction.

const createAction = <T extends string, P>(type: T) => (payload?: P): Action<T, P> => ({
type,
payload,
})

Чтож, мы пришли примерно к тому же, что и было в примере с SO. Только payload вне зависимости от экшена не обязательный, а типы этого поля так и не выводятся. Добавим магии lookup types и посмотрим, что у нас получится.

export const createAction = <A extends Action<A['type'], A['payload']>>(type: A['type']) => (payload: A['payload']) => ({
type,
payload,
})

Теперь у нас выводится типы тайпсрипта по типу экшена. Так мы не можем доказать тайпчекеру, что тип у A['type'] - строка, и это не сработает (пример). Впрочем, мы и не пытались.
Чтобы это сделать мы воспользуемся таким вот хитрым хелпером, который позволит доказать, что тип действительно строка (если у вас есть идеи как это сделать проще - велком в личку).

type StingTypeField = {
type: string
}

type LookupStringTypeField<T, K> = K extends keyof T ? (T extends StingTypeField ? T[K] : never) : never

В LookupStringTypeField мы говорим, что поле в объекте точно есть поле K и оно точно строка, а иначе и не может быть (never).

Итоговый createAction:

const createAction = <A extends Action<LookupStringTypeField<A, 'type'>, A['payload']>>(type: A['type']) => (payload: A['payload']) => ({
type,
payload,
})

И конечно ссылка с примером.

P.S. В примерах не исключены ошибки ☺️

Всех с наступающим!🎄
#w_corner_ts
Небольшой пост по безопасности на фронтенде, куда вообще смотреть? Заметил, что мало кто в этой теме разбирается даже на базовом уровне, а тема важная.
Основные вопросы:
Виды атак, какой вектор, какая цель?
XSS, CSRF, clickjacking.
Как защититься?
CORS + SOP, CSP, Cookie: secure, httpOnly, SameSite
https://application.security/free-application-security-training - в интерактивном режиме показывается как можно произвести атаку и как от нее защититься
https://developer.mozilla.org/ru/docs/Web/HTTP/CORS
https://developer.mozilla.org/ru/docs/Web/HTTP/CSP
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
https://developer.mozilla.org/ru/docs/Web/HTTP/Cookies#secure_(%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D1%8B%D0%B5)_%D0%B8_httponly_cookies
https://habr.com/ru/post/340146/ - про то, как работает JWT
#w_corner_secure
MSW
Часто вам приходилось разрабатывать интерфейс без готовой ручки на бэкенде? Все эти временные данные на уровне стора или Promise.resolve с данными на уровне сервисов/sdk и прочие подходы, обеспечивающие нам одни и теже данные. А писать e2e тесты, когда чтобы замокать результат запросов всегда приходилось придумывать свои велосипеды (да-да, е2е с фейковыми данными запросами ужас и моветон - мы всегда поднимаем весь стек и ждём, что нигде ничего не отвалится и нашей машинке хватит сил).
Хватит. Почти всегда для того, чтобы подсунуть фэйковые данные на уровне сети я выбирал nock, но время идёт, индустрия движется, а подход к моку сервера в тестах остаётся прежним. Только не с Mock Service Worker и вот вам несколько причин перейти на него:
1. Простая установка
2. Возможность запускать одни и те же обработчики и для серверных (например,в случае с SSR) и для клиентских тестов из коробки
3. Возможность делать более умные обработчики на этапе жизни «без нужной бэкендовой ручки», а затем переиспользовать их в тестах
4. Поддержка rest и grqphql
5. Максимально прозрачное поведение и простота отладки
6. Ваши обработчики похожи на реальные express мидлвары
Ставим пакет, запускаем npx msw init public/ --save и вперед. Не забываем читать доку, и экспериментировать, думаю, обязательно найдете больше юзкейсов для себя.
#w_corner_testing