Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@proscom/prostore
Advanced tools
Prostore - это библиотека для работы с данными во фронтенде, вдохновленная apollo-client. Apollo Client решает только задачи работы с API-запросами GraphQL, в то время как Prostore позиционируется как общее расширяемое решение, позволяющее также работать с чисто фронтендовыми данными. В этом плане Prostore чем-то похож на mobx.
Prostore родился из необходимости быстрого решения частых задач в SPA проектах, написанных на React с использованием React Hooks, поэтому на данный момент код библиотеки ориентирован именно на работу с React.
Эти задачи включают в себя:
Создание цепочек зависимостей по данным
Запрос данных с бекенда с автоматическим обновлением компонента при их получении и обработкой индикатора загрузки и ошибок
Отправка данных на бекенд в виде императивных мутирующих операций, с отслеживанием состояния запроса
Синхронизация данных с localStorage
yarn add @proscom/prostore rxjs
//
npm install --save @proscom/prostore rxjs
Данные в Prostore хранятся в сторах. Каждый стор представляет собой некое хранилище, выполняющее какую-то одну функцию. Например, стором может быть состояние выполнения API-запроса, или данные о текущем пользователе.
В основе Prostore лежит RxJS - мощная библиотека для работы с Observable. Observable это как EventEmitter, только с одним типом событий - обновление данных. Но благодаря функциям-операторам из rxjs можно создавать цепочки зависимостей одних Observable от других. Поближе познакомиться с rxjs можно здесь.
Каждый стор в Prostore реализует следующий интерфейс:
export interface IStore<State> {
readonly state: State;
readonly state$: Observable<State>;
}
Это позволяет создавать из сторов цепочки зависимостей. Например, при обновлении данных о текущем пользователе можно автоматически перевыполнить API-запрос, зависящий от них. Также в любой момент можно получить актуальное состояние стора.
Для удобства есть базовый класс BehaviorStore
у которого state$
представляет собой BehaviorSubject
из rxjs.
Расширив этот класс, можно создавать свои собственные сторы, у которых
работа с состоянием похожа на классовые компоненты в React. Например,
import { BehaviorStore } from '@proscom/prostore';
class UserStore extends BehaviorStore {
constructor() {
// в конструкторе передаем начальное состояние
super({
user: null
});
}
updateUser(newUser) {
this.setState({ user: newUser });
}
}
При расширении BehaviorStore в конструктор базового класса надо передать первоначальное состояние стора - любой JS объект.
Это должен быть именно объект. Состояние не может быть массивом или простым типом. Поэтому если надо использова не-объект, то оберните его в объект, присвоив какому-нибудь ключу:
super({
data: [1, 2, 3]
});
В любом месте этого класса (а также снаружи, но это не рекомендуется)
можно вызывать функцию this.setState
, которая принимает обновление
состояния, либо функцию обновления состояния (как в реакте).
При вызове this.setState
происходит одноуровневое слияние старого
состояния с новым (типа newState = {...oldState, ...changes}
).
Если же передана функция, то она сразу вызывается и в аргумент ей передается текущее состояние, а вернуть она должна изменения.
Если нужно сбросить состояние целиком, например, чтобы удалить какие-то ключи,
можно воспользоваться более низкоуровневым вызовом this.state$.next(newState)
.
Создав такой стор, можно дальше подписаться на него стандартными средствами rxjs:
const userStore = new UserStore();
const subscription = userStore.state$.subscribe((state) => {
console.log('state changed', state);
});
userStore.updateUser('Tester');
subscription.unsubscribe();
// Выведет:
// state changed { user: null }
// state changed { user: 'Tester' }
https://codesandbox.io/s/prostore-example-behavior-jomsr
Для удобства работы с подписками в React, смотри библиотеку prostore-react
.
Иногда может быть полезно не применять изменения состояния стора сразу, а отложить их до следующего цикла. Так например делает React при работе с классовыми компонентами.
В Prostore есть класс AsyncBehaviorStore, который собирает все вызовы
this.setState
в текущем синхронном цикле и выполняет их все сразу
последовательно в следующем (с помощью setTimeout
).
Это может быть полезно, если стор может измениться более одного раза за синхронный цикл. С точки зрения подписчиков, AsyncBehaviorStore изменится только один раз, в то время как BehaviorStore вызовет своих подписчиков при каждом изменении.
import { AsyncBehaviorStore } from '@proscom/prostore';
class AsyncUserStore extends AsyncBehaviorStore {
constructor() {
super({
firstName: null,
lastName: null
});
}
setFirstName(firstName) {
this.setState({ firstName });
}
setLastName(lastName) {
this.setState({ lastName });
}
}
// ...
const userStore = new AsyncUserStore();
userStore.state$.subscribe((state) => {
console.log('state changed', state);
});
userStore.setFirstName('first');
userStore.setLastName('last');
// Выведет:
// state changed: { firstName: null, lastName: null }
// state changed: { firstName: 'first', lastName: 'last' }
https://codesandbox.io/s/prostore-example-async-kwnns
RequestStore это более высокоуровневая абстракция, которая представляет
собой состояние какого-либо запроса. Запрос - это произвольная функция,
возможно асинхронная, которая превращает свои параметры variables
в результат data
. Например, эта функция может выполнять GET HTTP-запрос
с помощью fetch, передавая variables
как query-параметры, и сохранять
тело результата как data
.
Состояние RequestStore имеет следующий тип:
export interface IRequestState<Vars, Data> {
data: Data | null;
loading: boolean;
loaded: boolean;
error: any;
variables: Vars | null;
}
У RequestStore есть основной метод, который можно вызывать снаружи:
async function loadData(
variables: Vars,
options: any = {}
): Promise<IRequestState<Vars, Data>> {}
При вызове этой функции запускается выполнение нового запроса данных с новыми variables. Функция завершается, когда запрос будет выполнен успешно либо с ошибкой.
Для создания собственного стора надо расширить класс RequestStore
,
переопределив функцию
export type IObservableData<Data> = Promise<Data> | Observable<Data>;
function performRequest(
variables: Vars,
options: Options
): IObservableData<Data>;
Пример можно посмотреть на CodeSandbox: https://codesandbox.io/s/prostore-example-request-h9641
В библиотеках prostore-apollo
и prostore-axios
доступны свои классы,
расширяющие RequestStore, реализующие GraphQL-запросы и обычные HTTP-запросы
соответственно.
При вызове конструктора RequestStore
необходимо передать аргумент типа:
export interface IRequestStoreParams<Vars, Data> {
// Первоначальное значение data
initialData?: Data;
// Функция, позволяющая пропустить вызов performRequest
// Эта функция должна вернуть undefined, если запрос не надо пропускать
// Любое другое возвращенное значение будет сохранено как data
skipQuery?: ISkipQueryFn<Vars, Data>;
// Функция, которая позволяет не просто перезаписать data,
// а объединить старую data с новой.
// Это может быть полезно для запросов с пагинацией
updateData?: IUpdateDataFn<Vars, Data>;
// Идентификатор этого стора для передачи данных с сервера
// при использовании Server-Side-Rendering
ssrId?: string;
}
export type ISkipQueryFn<Vars, Data> = (vars: Vars) => Data | null | undefined;
export type IUpdateDataFn<Vars, Data> = (
data: Data,
oldData: Data,
params: { store: any; variables: Vars; options: any }
) => Data;
Для удобства в качестве skipQuery
можно передать одну из двух
предопределенных функций:
import { skipIf, skipIfNull } from '@proscom/prostore';
new MyRequestStore({
// Если vars не удовлетворяет функции condition, то
// data = defaultData
skipQuery: skipIf((vars) => !vars, defaultData),
// или
// Если vars равно null, то data = defaultData
skipQuery: skipIfNull(defaultData)
});
В случаях когда нужна пагинация с дозагрузкой (например, бесконечный скролл с дозагрузкой)
можно использовать функцию updateData
.
Пример:
const myStore = new MyRequestStore({
updateData: (data, oldData, params) {
const page = params.variables.page;
const perPage = params.variables.perPage;
return [
...oldData?.slice(0, page * perPage),
...data,
...oldData?.slice((page + 1) * perPage)
];
}
});
let state;
state = await myStore.loadData({ page: 0, perPage: 2 });
// data=[1,2] oldData=null params={ variables: { page: 0, perPage: 2 }, ... }
// state.data = [1,2]
state = await myStore.loadData({ page: 1, perPage: 2 });
// data=[3,4] oldData=[1,2] params={ variables: { page: 1, perPage: 2 }, ... }
// state.data = [1,2,3,4]
FAQs
State management library with multiple stores based on rxjs
The npm package @proscom/prostore receives a total of 11 weekly downloads. As such, @proscom/prostore popularity was classified as not popular.
We found that @proscom/prostore demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.