### Project Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/intro.md Illustrates a typical project directory structure following the Astral Architecture Guide principles, showcasing separation of concerns into distinct modules like app, screens, modules, api, and shared. ```tree ├── app/ ├── screens/ ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### Example Screen Structure Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/screens.md Illustrates a typical directory structure for screens within an application, showing nested screens and their associated store and index files. ```bash ├── app/ ├── screens/ | ├── Feedback/ | ├── NoAccess/ | ├── NotFound/ | ├── PopularGoods/ | ├── NewGoods/ | | ├── NewGoods.tsx | | ├── store/ | | └── index.ts | └── index.ts ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### ConfigService Initialization Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/guides/config.md Demonstrates how to initialize the ConfigService in the Application layer. It retrieves environment variables (e.g., PUBLIC_API_URL, PUBLIC_SENTRY_DSN) and passes them to the `init` method of the `configService`. ```ts configService.init({ apiUrl: window.__ENV__.PUBLIC_API_URL, monitoringDsn: window.__ENV__.PUBLIC_SENTRY_DSN, monitoringStand: window.__ENV__.PUBLIC_SENTRY_ENV, monitoringRelease: window.__ENV__.PUBLIC_RELEASE_TAG, }); ``` -------------------------------- ### Unit Test Example for CartScreenStore Source: https://github.com/astralfrontend/guides/blob/main/docs/unit-testing/overview.md Demonstrates a unit test for the CartScreenStore, mocking dependencies and asserting behavior when a modal is opened. This follows the SUT pattern. ```ts describe('CartScreenStore', () => { it('Процесс оплаты запускается фоном при открытии модалки', () => { const cartPaymentStoreMock = mock(); const sut = new CartScreenStore(cartPaymentStoreMock); sut.openModal(); expect(cartPaymentStoreMock.pay).toBeCalled(); }); }); ``` -------------------------------- ### DocStore Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/domain.md Demonstrates the creation and usage of a DocStore within the Domain layer, interacting with the API layer for data fetching and mutations. It includes logic for retrieving document names and managing loading states. ```typescript import { docsFetcher, type DocsFetcher, type DocsDTO } from '@example/api'; type Params = { docID: string }; class DocStore { constructor( private readonly _params: Params, private readonly _docsFetcher: DocsFetcher, ) { this.creationsDocMutation = _docsFetcher.mutations.createDoc.create(); makeAutoObservable(this, {}, { autoBind: true }); } public get docName() { if (!this.docQuery.data) { return ''; } return `Название документа: ${this.docQuery.data.name}`; } public get isLoading() { return this.docQuery.isLoading || this.creationDocMutation.isLoading; } public createDoc = (data: DocsDTO.CreateDocInput) => { this.creationDocMutation.sync(); } private get docQuery() { return this._docsFetcher.queries.doc.create(this._params.docID); } } export const createDocStore = (params: Params) => new DocStore(params, docsFetcher) ``` -------------------------------- ### AdministrationPolicyStore Example Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/policies.md Demonstrates the creation of an `AdministrationPolicyStore` using the `@astral/permissions` library. This policy is associated with the 'administration' module and prepares necessary data, such as user roles, for access control. ```ts import { makeAutoObservable } from "mobx"; import type { UserRepository } from '@example/data'; import { PermissionDenialReason } from '../../../../enums'; // @astral/permissions в реальном коде должен реэкспортироваться через shared import { PolicyManagerStore, Policy } from '@astral/permissions'; export class AdministrationPolicyStore { private readonly policy: Policy; constructor( private readonly _policyManager: PolicyManagerStore, private readonly _userRepo: UserRepository, ) { makeAutoObservable(this, {}, { autoBind: true }); // policyManager.createPolicy создает политику, которая позволит в дальнейшем создавать permissions this.policy = this._policyManager.createPolicy({ name: 'administration', // Метод для подготовки данных необходимых для формирования доступов AdministrationPolicy prepareData: async (): Promise => { await Promise.all([this._userRepo.getRolesQuery().async()]); }, }); } } ``` -------------------------------- ### Setup Function with Mocks and `prepareDataAsync` Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/testing.md Provides a `setup` function for initializing the `BooksPolicyStore` with mocked dependencies (`UserRepository`, `BillingRepository`) and calling `policyManager.prepareDataAsync()`. This ensures the policy is ready before tests are executed. ```ts describe('BooksPolicyStore', () => { const setup = async ({ isAdmin, billingInfo, }: { isAdmin: boolean; billingInfo?: Partial; }) => { const policyManager = createPolicyManagerStore(); const cacheService = createCacheService(); const userRepoMock = mock({ getRolesQuery: () => cacheService.createQuery(['roles'], async () => ({ isAdmin, })), }); const billingRepoMock = mock({ getBillingInfoQuery: () => cacheService.createQuery(['billing'], async () => billingRepositoryFaker.makeBillingInfo(billingInfo), ), }); const sut = new BooksPolicyStore( policyManager, billingRepoMock, userRepoMock, ); await policyManager.prepareDataAsync(); return { sut }; }; describe('Добавление книги на полку', () => { it('Доступно администратору', async () => { const { sut } = await setup({ isAdmin: true }); expect(sut.addingToShelf.isAllowed).toBeTruthy(); }); }); }); ``` -------------------------------- ### React Application Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/application.md Illustrates a typical directory structure for a React application, highlighting the placement of the 'app' directory which contains routing and the main App component. ```plaintext ├── app/ | ├── Routing/ | └── App.tsx ├── screens/ ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### Example Usage: Mounting Alert Message Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/guides/renderComponentInStore.md A React component example demonstrating how to use the `UIStore` to mount a `ReactNode` (an alert message) for later display. It uses `useState` to get the store instance and `useEffect` to call the `mount` method. ```tsx const Alert = () => { const [{ mount }] = useState(createUIStore); const message = Hello; useEffect(() => { mount(message); }, []); ... ``` -------------------------------- ### Example Screens Structure Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Shows how screens are organized, with each screen potentially having its own store and entry point, and how they are collected in the screens directory. ```directory ├── app/ ├── screens/ | ├── Feedback/ | ├── NoAccess/ | ├── NotFound/ | ├── PopularGoods/ | ├── NewGoods/ | | ├── NewGoods.tsx | | ├── store/ | | └── index.ts | └── index.ts ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### Endpoint Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/api.md Provides an example of the directory structure for endpoints, showing organization by resource (e.g., docs, payments) and inclusion of DTOs and enums. ```tree ├── api/ | ├── endpoints/ | | ├── docs/ | | | |── docs.ts | | | |── enums.ts | | | |── dto.ts | | | └── index.ts | | |── payments/ | | | |── payments.ts | | | |── enums.ts | | | |── dto.ts | | | └── index.ts | | └── index.ts ``` -------------------------------- ### API Endpoints Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Details the directory structure within the 'api/endpoints' segment, demonstrating how it mirrors the server API structure, with examples for user-related endpoints. ```plaintext ├── api/ | ├── fetchers/ | ├── endpoints/ | | ├── users/ | | | ├── users.ts | | | ├── dto.ts | | | └── index.ts | | └── index.ts | └── index.ts ``` -------------------------------- ### Example Project Structure Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Illustrates the directory structure for the payment module, showcasing the organization of features, domain logic, and module entry points. ```directory ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── features/ | | | ├── PaymentSwitch/ | | | ├── CardPayment/ | | | ├── CashPayment/ | | | └── index.ts | | ├── domain/ | | └── index.ts ├── api/ └── shared/ ``` -------------------------------- ### DocStore Unit Test Setup Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/api.md Sets up a Vitest describe block for testing the DocStore. It imports necessary fakers and modules for the test suite. ```ts import { docsFaker } from '@example/api/_fakers'; import { describe } from 'vitest'; describe('DocStore', () => { ... }) ``` -------------------------------- ### CurrentOrgStore with Data Formatting and Aggregation Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/domain.md Illustrates how the Domain layer handles data formatting and aggregation. This example shows a CurrentOrgStore that combines data from organization details and a list of documents, transforming it into a unified structure. ```typescript import { docsFetcher, orgFetcher, type DocsFetcher, type OrgFetcher, type DocsDTO, type OrgDTO, } from '@example/api'; type Params = { orgID: string }; class CurrentOrgStore { constructor( private readonly _params: Params, private readonly _docsFetcher: DocsFetcher, private readonly _orgFetcher: OrgFetcher, ) { makeAutoObservable(this, {}, { autoBind: true }); } public get info() { const { data: org } = this.orgQuery; const { data: docList } = this.myDocListQuery; if (!org || !docList) { return null; } return { ...this.orgQuery.data, myDocList: docList, } } private get orgQuery() { return this._orgFetcher.queries.org.create(this._params.orgID); } private get myDocListQuery() { return this._docsFetcher.queries.myDocList.create(this._params.orgID); } } export const createDocStore = (params: Params) => new DocStore(params, docsFetcher) ``` -------------------------------- ### API Endpoints Overview Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/api.md Provides examples of API endpoint paths, illustrating the typical structure for RESTful API interactions. ```api GET /api/v1/docs/info POST /api/v1/payments/create ``` -------------------------------- ### API Endpoint Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md A simple example of an API endpoint definition, specifying the HTTP method and path. ```APIDOC GET api/users/personInfo ``` -------------------------------- ### PermissionsStore Usage Example Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/policies.md Demonstrates how to use the PermissionsStore within another store (UIStore) to access specific permissions, such as checking if book creation is allowed via the administration policy. ```ts export class UIStore { constructor(private readonly _permissions: PermissionsStore) { makeAutoObservable(this, {}, { autoBind: true }); } public get isAllowedBookCreation() { return this._permissions.administration.administrationActions.isAllowed; } } ``` -------------------------------- ### Next.js Application Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/application.md Demonstrates a common directory structure for a Next.js application, emphasizing that the 'pages' directory serves as the Application layer due to Next.js's file-based routing. ```plaintext ├── pages/ # pages является application слоем (Nextjs специфичность) | ├── index.tsx | ├── _app.tsx ├── screens/ ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### Shared Layer Structure Example (React) Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/shared.md Illustrates a typical directory structure for the Shared layer in a React-based project, showcasing common subdirectories for constants, types, utils, services, stores, and UI components. ```plaintext ├── app/ ├── screens/ ├── modules/ ├── api/ └── shared/ ├── constants/ ├── types/ ├── utils/ ├── services/ ├── stores/ ├── ui/ | ├── components/ | ├── hooks/ | ├── external.ts | └── index.ts └── index.ts ``` -------------------------------- ### Shared Layer Structure Example (React) Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Provides an example of the directory structure within the 'shared' layer for a React stack, highlighting common subdirectories like constants, types, utils, services, stores, and ui. ```plaintext ├── app/ ├── screens/ ├── modules/ ├── api/ └── shared/ | ├── constants/ | ├── types/ | ├── utils/ | ├── services/ | ├── stores/ | ├── ui/ | | ├── components/ | | ├── hooks/ | | ├── external.ts | | └── index.ts | └── index.ts ``` -------------------------------- ### Modules Layer Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Illustrates the structure of a module within the 'modules' layer, such as 'payment', showing its 'features' and 'domain' segments. ```plaintext ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── features/ | | ├── domain/ | | └── index.ts ├── api/ └── shared/ ``` -------------------------------- ### API Layer Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Shows the directory structure within the 'api' layer, including segments like _fakers, fetchers, endpoints, and services. ```plaintext ├── api/ | ├── _fakers/ | ├── fetchers/ | ├── endpoints/ | ├── services/ | └── index.ts ``` -------------------------------- ### useLogic Initialization with Validation Schema Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/features/useLogic.md Demonstrates initializing the useLogic hook with a form instance that includes the defined validation schema. ```typescript export const useLogic = (): Result => { const form = useForm({ validationSchema }); ... }; ``` -------------------------------- ### Types for Validated Data Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/arch/modules/validations.md Defines a separate 'types' file for validated data to prevent circular dependencies between useLogic, validation, and UIStore. This example shows the structure for 'BookFormValues'. ```typescript export type BookFormValues = { name: string; } ``` -------------------------------- ### Admin Route Guard Example Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/routes.md Demonstrates how to use the AdminRouteGuard to protect the CreateBookPage, ensuring only administrators can access it. It wraps the CreationBookScreen component with the guard. ```tsx import { CreationBookScreen } from '@example/screens'; import { AdminRouteGuard } from '@example/modules/permissions'; const CreateBookPage = () => { return ( ); }; export default CreateBookPage; ``` -------------------------------- ### Project Structure Example Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Illustrates a typical project directory structure for the Astral frontend application, showing the main directories like app, screens, modules, api, and shared. ```plaintext ├── app/ ├── screens/ ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### Valid Class Directory Naming (PascalCase) Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/classes.md Shows the correct convention for naming class directories, which should start with an uppercase letter (PascalCase) to clearly identify them as classes. ```bash ├── services/ | |── FileService/ | | |── FileService.ts | | └── index.ts | |── ErrorService/ | | |── ErrorService.ts | | └── index.ts | └── index.ts ``` -------------------------------- ### Invalid Shared Validations Export Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/validations.md An example of an incorrect way to export shared validations, where custom validations are mixed with external library exports in a way that doesn't consolidate them under a single `v` object. ```ts export * from './okpo'; export * as v from '@astral/validations'; ``` ```ts import { v, validateOkpo } from '@example/shared'; v.string(validateOkpo()) ``` -------------------------------- ### Invalid Utils Directory Structures Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/react/utils.md Illustrates common mistakes in organizing utility functions. These examples show incorrect placements or naming conventions that deviate from the project's standards. ```plaintext ├── UserInfo/ | ├── formatPriceToView.ts | ├── UserInfo.tsx | └── index.ts ``` ```plaintext ├── UserInfo/ | ├── utils.ts | ├── UserInfo.tsx | └── index.ts ``` ```plaintext ├── UserInfo/ | ├── utils/ | | |── utils.ts | | └── index.ts | ├── UserInfo.tsx | └── index.ts ``` -------------------------------- ### Project Structure: Utils, Services, Stores Location Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/arch/modules/features/UIStore.md Utils, services, and stores, which are part of a feature's business logic, should reside within the `UIStore` directory. This centralizes the feature's logic and clarifies the entry point for consuming it. ```project-structure ├── Cart/ | ├── UIStore/ | | |── stores/ | | |── utils/ | | |── services/ | | |── UIStore.ts | | └── index.ts | ├── Cart.tsx | └── index.ts ``` -------------------------------- ### Переиспользование логики инициализации в CartStore Source: https://github.com/astralfrontend/guides/blob/main/docs/unit-testing/guides/reusable.md Демонстрирует вынесение логики инициализации для тестирования различных сценариев с использованием CartStore. Функция `setupCartStore` создает моки для зависимостей и экземпляр CartStore, позволяя легко тестировать ошибки добавления и удаления товаров. ```tsx describe('CartStore', () => { const setupCartStore = ( cartFetcherMockImplementation: Partial, ) => { const booksFetcherMock = mockFetcher(cartFetcherMockImplementation); const cartFetcherMock = mockFetcher(cartFetcherMockImplementation); const notifyMock = mock(); const sut = new CartStore(cartFetcherMock, notifyMock); return { sut, notifyMock }; }; it('Ошибка добавления товара в корзину провоцирует появление уведомления', async () => { const { sut, notifyMock } = setupCartStore({ mutations: { addGoods: async () => { throw Error('Неизвестная ошибка'); }, } }); await vi.waitFor(() => { sut.addGoods([]); }); expect(notifyMock.error).toBeCalledWith('Неизвестная ошибка'); }); it('Ошибка удаления товара из корзины провоцирует появление уведомления', async () => { const { sut, notifyMock } = setupCartStore({ mutations: { removeGoods: async () => { throw Error('Неизвестная ошибка'); }, } }); await vi.waitFor(() => { sut.removeGoods([]); }); expect(notifyMock.error).toBeCalledWith('Неизвестная ошибка'); }); }); ``` -------------------------------- ### Reading Book Route Guard Example Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/routes.md Shows how to implement a ReadingBookRouteGuard for the ReadingBookPage. It includes fetching a book ID using useRouterParams and conditionally rendering the page or a not found screen. ```tsx import { NotFoundScreen, ReadingBookScreen } from '@example/screens'; import { useRouterParams } from '@example/shared'; import { ReadingBookRouteGuard } from '@example/modules/permissions'; const ReadingBookPage = () => { const { id } = useRouterParams(); if (!id) { return ; } return ( ); }; export default ReadingBookPage; ``` -------------------------------- ### Binding Logic to Component Lifecycle Methods Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/features/UILogic.md This example demonstrates how to bind logic, such as mount and unmount operations, to a component's lifecycle using `useEffect` and a UI store, ensuring proper setup and cleanup. ```tsx export const Card = () => { const [{ mount, unmount }] = useState(createUIStore); useEffect(() => { mount(); return unmount; }, []); return ...; }; ``` -------------------------------- ### BooksPolicyStore Example Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/preparingData.md Пример реализации `BooksPolicyStore` для управления доступом к добавлению книги на полку. Включает подготовку данных из `UserRepository` и `BillingRepository`. ```typescript // @astral/permissions в реальном коде должен реэкспортироваться через shared import { PolicyManagerStore, Policy } from '@astral/permissions'; export class BooksPolicyStore { private readonly policy: PermissionsPolicy; constructor( policyManager: PolicyManagerStore, private readonly _billingRepo: BillingRepository, private readonly _userRepo: UserRepository, ) { makeAutoObservable(this, {}, { autoBind: true }); this.policy = policyManager.createPolicy({ name: 'books', // prepareData будет вызван одновременно с другими policy посредством policyManager prepareData: async () => { await Promise.all([ this.userRepo.getRolesQuery().async(), this._userRepo.getPersonInfoQuery().async(), this._billingRepo.getBillingInfoQuery().async(), ]); }, }); } /** * Возможность добавить на полку книгу */ public get addingToShelf() { return this.policy.createPermission((allow, deny) => { if (this._userRepo.getRolesQuery().data?.isAdmin) { return allow(); } const billingInfo = this._billingRepo.getBillingInfoQuery()?.data; if (!billingInfo?.paid) { return deny(PermissionDenialReason.NoPayAccount); } if ( billingInfo.info.shelf.currentCount >= billingInfo.info.shelf.allowedCount ) { return deny(PermissionDenialReason.ExceedShelfCount); } allow(); }); } } ``` -------------------------------- ### UIStore Initialization with useLogic Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/features/useLogic.md Illustrates the initialization of UIStore within a component before passing it to the useLogic hook. This pattern ensures that the store is managed by the component's lifecycle. ```tsx const Card = (props: Props) => { const [uiStore] = useState(createUIStore); const { fullName, isShowDescription, description } = useLogic(uiStore); return ( {fullName} {isShowDescription && {description}} ); }; ``` -------------------------------- ### Method Definition: Arrow Functions (Invalid) Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/classes.md Highlights invalid TypeScript class method definitions. This example shows the incorrect use of traditional function declarations and function expressions where arrow functions are preferred. ```ts class CartService { public pay() { ... }; public send = function() { ... }; } ``` -------------------------------- ### Valid Internal Import Alias Configuration Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/imrort-alias.md Demonstrates a valid TypeScript configuration for internal import aliases using the '#' prefix. This setup allows importing modules from the '#shared' path, which maps to the './shared' directory. ```json { "compilerOptions": { "baseUrl": ".", "paths": { "#shared": ["./shared"] } } } ``` ```ts import { Button } from '#shared'; ``` -------------------------------- ### Скрипт startup.prod.sh Source: https://github.com/astralfrontend/guides/blob/main/docs/env/csr.md Bash скрипт для продакшен режима. Генерирует index.html, инжектирует env переменные, подменяет переменные для Nginx и запускает Nginx. ```bash # Пример содержимого startup.prod.sh # Достает PUBLIC_ env переменные из окружения # Генерирует index.html из index.template.html # Инжектирует env переменные в index.html # Подменяет переменные для nginx # Запускает nginx ``` -------------------------------- ### Docs Fetcher Implementation Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/api.md An example of a fetcher implementation using `@astral/mobx-query` for interacting with the documentation API. It demonstrates defining queries for fetching single documents, infinite queries for document lists, and mutations for editing documents. ```ts import { docsEndpoint } from '../endpoints'; import { cacheService } from '../services'; const docsFetcher = { queries: { doc: cacheService.createQuerySet((id: string) => ({ execute: () => docsEndpoint.getDoc(id).then(({ data }) => data), })), }, infiniteQueries: { docList: cacheService.createInfiniteQuerySet( (filters: Omit) => ({ execute: ({ offset, count }) => docsEndpoint .getDocList({ offset, count, ...filters }) .then(({ data }) => data.list), }), ), }, mutations: { editDoc: cacheService.createMutationSet( (params: DocsDTO.EditDocInput) => docsEndpoint.editDoc(params), ), }, }; export type DocsFetcher = typeof docsFetcher; ``` ```ts const docsFetcher = { queries: { doc: cacheService.createQuerySet((id: string) => ({ // деструктуризация в данном случае необходимо, чтобы в modules не попадал объект с данными о httpService execute: () => docsEndpoint.getDoc(id).then(({ data }) => data), })), }, }; ``` -------------------------------- ### Example Domain Structure Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Demonstrates the typical structure within a module's domain layer, including services, stores, utilities, types, and constants for business logic. ```directory ├── app/ ├── screens/ ├── modules/ | └── cart/ | | ├── features/ | | └── domain/ | | | ├── services/ | | | ├── stores/ | | | | ├── CartStore/ | | | | └── index.ts | | | ├── utils/ | | | ├── types/ | | | ├── constants/ | | | └── index.ts | | └── index.ts ├── api/ └── shared/ ``` -------------------------------- ### Valid CreateBook Test with Fake Data Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/tests/fakes.md Demonstrates a valid test case for the CreateBook functionality, utilizing fake book data and ensuring correct data is passed to the repository. This example highlights the use of `bookRepositoryFaker.makeBookByName()` to generate fake data. ```ts it('CreateBook отправляет измененные values формы в репозиторий', async () => { const fakeBook = bookRepositoryFaker.makeBookByName(); const fakeBookFormValues: BookFormValues = { name: fakeBook.name, genre: fakeBook.genre, pageCount: '22', author: fakeBook.author, isPresentCoAuthor: false, }; const cacheService = createCacheService(); const creationBookSpy = vi.fn().mockResolvedValue(undefined); const adminRepositoryMock = mock({ createBookMutation: () => cacheService.createMutation(creationBookSpy), }); const sut = new CreateBookScreenStore( adminRepositoryMock, createRouterMock(), ); await sut.createBook(fakeBookFormValues); expect(creationBookSpy).toBeCalledWith({ name: fakeBook.name, genreID: fakeBook.genre.id, pageCount: 22, author: fakeBook.author, }); }); ``` -------------------------------- ### Invalid Internal Import Alias Configuration Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/imrort-alias.md Illustrates an invalid TypeScript configuration for internal import aliases. Using a prefix like '@project/*' can lead to ambiguity and unwanted autocompletion suggestions in IDEs, as it might conflict with installed packages. ```json { "compilerOptions": { "baseUrl": ".", "paths": { "@project/*": ["./*"] } } } ``` ```ts import { Button } from '@project/shared'; ``` -------------------------------- ### Invalid CreateBook Test without Fake Data Prefix Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/tests/fakes.md Illustrates an invalid test case for CreateBook where fake data is not properly prefixed, potentially leading to confusion with real data. This example shows the incorrect usage by not using the `fake` prefix. ```ts it('CreateBook отправляет измененные values формы в репозиторий', async () => { const book = bookRepositoryFaker.makeBookByName(); const bookFormValues: BookFormValues = { name: book.name, genre: book.genre, pageCount: '22', author: book.author, isPresentCoAuthor: false, }; const cacheService = createCacheService(); const creationBookSpy = vi.fn().mockResolvedValue(undefined); const adminRepositoryMock = mock({ createBookMutation: () => cacheService.createMutation(creationBookSpy), }); const sut = new CreateBookScreenStore( adminRepositoryMock, createRouterMock(), ); await sut.createBook(bookFormValues); expect(creationBookSpy).toBeCalledWith({ name: book.name, genreID: book.genre.id, pageCount: 22, author: book.author, }); }); ``` -------------------------------- ### PermissionsStore Implementation Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/policies.md Defines the PermissionsStore class, a singleton that acts as the main entry point for accessing application permissions. It initializes and manages various policy stores (administration, books, payment) and provides methods for data preparation and status checking. It depends on billing and user repositories. ```ts import { makeAutoObservable } from 'mobx'; import { billingRepository, userRepository } from '@example/data'; import type { BillingRepository, UserRepository } from '@example/data'; // В реальном коде @astral/permissions необходимо реэкспортировать через shared import type { PolicyManagerStore } from '@astral/permissions'; import { createPolicyManagerStore } from '@astral/permissions'; import { createAdministrationPolicyStore, createBooksPolicyStore, createPaymentPolicyStore, } from './policies'; import type { AdministrationPolicyStore, BooksPolicyStore, PaymentPolicyStore, } from './policies'; /** * Содержит все доступы приложения */ export class PermissionsStore { private readonly policyManager: PolicyManagerStore; public readonly administration: AdministrationPolicyStore; public readonly books: BooksPolicyStore; constructor(billingRepo: BillingRepository, userRepo: UserRepository) { makeAutoObservable(this, {}, { autoBind: true }); // policyManager регистрирует все доступы и позволяет подготовить данные для формирования доступов this.policyManager = createPolicyManagerStore(); this.administration = createAdministrationPolicyStore( this.policyManager, userRepo, ); this.books = createBooksPolicyStore( this.policyManager, billingRepo, userRepo, ); } /** * Подготавливает данные для формирования доступов */ public prepareData = () => this.policyManager.prepareDataSync(); public get preparingDataStatus() { return this.policyManager.preparingDataStatus; } } // singleton export const permissionsStore = new PermissionsStore( billingRepository, userRepository, ); ``` -------------------------------- ### Factory Method with Combined Parameters (Invalid) Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/classes.md Highlights an invalid TypeScript pattern where a factory method passes parameters incorrectly to the constructor. This example demonstrates a violation of the principle of grouping parameters into an object for the constructor, leading to less maintainable code. ```ts type Params = { id: string; }; class CartService { constructor( private readonly _params: Params, private readonly _cartStore: CartStore, private readonly _paymentService: PaymentService, ) {} } const createCartService = (id: string, cartStore: CartStore) => new CartService({ id }, cartStore, paymentService) ``` ```ts type Params = { id: string; cartStore: CartStore }; class CartService { constructor( private readonly _params: Params, private readonly _paymentService: PaymentService, ) {} } const createCartService = (id: string, cartStore: CartStore) => new CartService({ id, cartStore }, paymentService) ``` -------------------------------- ### Next.js Routing Example with Dynamic Route Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/application.md Shows a Next.js page component (`pages/goods/[id].tsx`) that handles dynamic routing, accessing route parameters (like 'id') using `useRouter` and rendering different screens based on the presence of the ID. ```tsx import { NextPage } from 'next'; import { useRouter } from '@example/shared'; import { NotFoundScreen, GoodsInfoScreen } from '@example/screens'; export const GoodsInfoPage: NextPage = () => { const router = useRouter(); const { id } = router.query; if (!id) { return ; } return ; }; ``` -------------------------------- ### API Layer Structure Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/api.md Illustrates the directory structure of the API layer, highlighting key subdirectories like fetchers, endpoints, and services. ```tree ├── api/ | ├── _fakers/ | ├── fetchers/ | ├── endpoints/ | ├── services/ | └── index.ts ``` -------------------------------- ### Initialize Permissions and Auth Stores Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/preparingData.md Initializes the permissionsStore and authStore, adding protected HTTP clients. This is typically called in the application's root component. ```tsx import React, { useEffect } from 'react'; import { observer } from 'mobx-react-lite'; import { useRoutes } from 'react-router-dom'; import { ThemeProvider } from '@mui/material/styles'; import { NotificationContainer } from '@app/components/NotificationContainer'; import { ContentState } from '@app/components/ContentState'; import { MainLayout } from '@app/layouts/MainLayout'; import { routes } from './routes'; import { permissionsStore } from '@app/stores/PermissionsStore'; import { authStore } from '@app/stores/AuthStore'; import { apiHttpClient } from '@app/api/httpClient'; import { theme } from '@app/theme'; export const App = observer(() => { const renderRoutes = useRoutes(routes); const permissionsStatus = permissionsStore.preparingDataStatus; useEffect(() => { permissionsStore.prepareData(); authStore.addProtectedHttpClients([apiHttpClient]); }, []); return ( {renderRoutes} ); }); ``` -------------------------------- ### Abstracting Browser API in UIStore Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/modules/features/UIStore.md This demonstrates the practice of abstracting Browser API interactions within the UIStore. This approach simplifies testing by allowing the substitution of real browser APIs with mock dependencies. ```ts export class UIStore { constructor( private readonly _storage: LocalStorageService, ) { makeAutoObservable(this); } public setSearch = (search: string) => { this._storage.setItem('search', search) } } ``` ```ts export class UIStore { constructor( private readonly _intersectionObserver: IntersectionObserver, ) { makeAutoObservable(this); } ... public mount = (itemRef: Ref) => { this._intersectionObserver(this.showAction, { root: itemRef.current }) } } ``` -------------------------------- ### Unit Testing Integration Logic Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/screens.md Demonstrates unit testing for the integration logic within a screen's store, using mocks for dependencies like CardPaymentStore and Router. This approach ensures that the screen's integration logic functions correctly without needing to test the underlying features themselves. ```typescript import type { CardPaymentStore } from '@example/modules/payment'; import { APP_ROUTES, type Router, createFlagStore } from '@example/shared'; class CartScreenStore { private readonly modalStore = createFlagStore(); constructor( private readonly _cardPaymentStore: CardPaymentStore, private readonly _router: Router, ) {} public openModal = () => { this.pay(); this.modalStore.setTrue(); }; public pay = () => { this._cardPaymentStore.pay({ onSuccess: () => { this._router.push(APP_ROUTES.cart.getRedirectPath()); }, }); }; } ``` ```typescript describe('CartScreenStore', () => { it('Процесс оплаты запускается фоном при открытии модалки', () => { const cartPaymentStoreMock = mock(); const sut = new CartScreenStore(cartPaymentStoreMock, createRouterMock()); sut.openModal(); expect(cartPaymentStoreMock.pay).toBeCalled(); }); }); ``` -------------------------------- ### Valid Styled Wrapper Naming (Styled Prefix) Source: https://github.com/astralfrontend/guides/blob/main/docs/style-guide/rules/react/styled.md Demonstrates the correct usage of the 'Styled' prefix for styled-component wrappers when a semantic name is not feasible. It shows a valid example of wrapping a Button component from '@astral/ui' with a 'StyledButton' and an invalid example using 'ButtonStyled'. ```ts import { Button } from '@astral/ui'; export const StyledButton = styled(Button)` color: red; `; ``` ```tsx import { StyledButton } from './styles'; const Button = () => ; ``` ```ts import { Button } from '@astral/ui'; // Постфикс запрещен export const ButtonStyled = styled(Button)` color: red; `; ``` -------------------------------- ### React Application Structure Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/overview.md Illustrates the application layer structure for a React project, including routing configuration and the main application component. ```directory ├── app/ | ├── Routing/ | └── App.tsx ├── screens/ ├── modules/ ├── api/ └── shared/ ``` -------------------------------- ### Test Case Scenarios for `addingToShelf` Source: https://github.com/astralfrontend/guides/blob/main/docs/permissions/testing.md Illustrates the specific test cases required for the `addingToShelf` permission, mapping directly to the `allow` and `deny` conditions in the policy implementation. This includes scenarios for admin access, unpaid accounts, and exceeding shelf limits. ```ts describe('BooksPolicyStore', () => { describe('Добавление книги на полку', () => { it('Доступно администратору', async () => { const { sut } = await setup({ isAdmin: true }); expect(sut.addingToShelf.isAllowed).toBeTruthy(); }); it('Недоступно, если аккаунт не оплачен', async () => {}); it('Недоступно, если превышено количество добавлений', async () => {}); it('Недоступно, если достигнуто максимальное количество добавлений', async () => {}); it('Доступно, если аккаунт оплачен и не превышено максимальное количество книг на полке', async () => {}); }); }); ``` -------------------------------- ### Defining Endpoint Functions Source: https://github.com/astralfrontend/guides/blob/main/docs/arch/api.md Shows a TypeScript example of defining an endpoint function that interacts with an HTTP service to fetch data. ```typescript import { DocsDTO } from './dto'; export const docsEndpoints = { getDocInfo: (id: string) => httpService.get(`/api/v1/docs/info/${id}`), } ```