├── .github ├── pr-labeler.yml ├── release-drafter.yml └── workflows │ ├── pr-labeler.yml │ ├── release-drafter.yml │ ├── release.yml │ └── test-and-build.yml ├── .gitignore ├── LICENSE ├── README.md ├── apps └── test-app │ ├── .babelrc │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── blog │ │ ├── [slug].tsx │ │ └── index.tsx │ ├── index.tsx │ └── profile │ │ └── index.tsx │ ├── postcss.config.js │ ├── public │ ├── favicon.ico │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── computed │ │ └── widgets │ │ │ └── layouts │ │ │ ├── base │ │ │ ├── data-load.ts │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ │ └── index.ts │ ├── entities │ │ └── authenticated-user │ │ │ ├── index.ts │ │ │ └── model.ts │ ├── layouts │ │ └── factories.ts │ ├── pages │ │ ├── blog-post │ │ │ ├── index.ts │ │ │ ├── model.ts │ │ │ └── page.tsx │ │ ├── blog │ │ │ ├── index.ts │ │ │ ├── model.ts │ │ │ └── page.tsx │ │ ├── home │ │ │ ├── index.tsx │ │ │ ├── model.ts │ │ │ └── page.tsx │ │ └── my-profile │ │ │ ├── index.tsx │ │ │ ├── model.ts │ │ │ └── page.tsx │ ├── processes │ │ ├── auth.ts │ │ └── index.ts │ ├── shared │ │ ├── api │ │ │ ├── index.ts │ │ │ └── local │ │ │ │ ├── index.ts │ │ │ │ ├── request.ts │ │ │ │ ├── requests.ts │ │ │ │ └── types.ts │ │ ├── events │ │ │ └── index.ts │ │ ├── routing │ │ │ ├── index.ts │ │ │ └── paths.ts │ │ └── ui │ │ │ └── globals.css │ └── widgets │ │ └── header │ │ ├── index.tsx │ │ └── styles.module.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── use-local.mjs │ └── yarn.lock ├── package.json ├── src ├── constants.ts ├── context-env.ts ├── context-normalizers.ts ├── enhanced-events.test.ts ├── enhanced-events.ts ├── env.ts ├── factories │ ├── get-initial-props.ts │ ├── get-server-side-props.ts │ ├── get-static-props.ts │ └── index.ts ├── index.ts ├── shared.ts ├── state.ts ├── types.ts └── with-effector.ts ├── tsconfig.json ├── vite.config.ts ├── vitest.config.ts └── yarn.lock /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | feature: ['feature/*', 'feat/*'] 2 | refactor: ['refactor/*'] 3 | fix: ['fix/*', 'bugfix/*', 'hotfix/*'] 4 | style: ['style/*'] 5 | chore: ['chore/*'] 6 | dependencies: ['deps/*', 'renovate/*'] 7 | ci: ['ci/*'] 8 | documentation: ['docs/*'] 9 | tests: ['tests/*'] 10 | optimization: ['optimization/*'] -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: '⚠️ Breaking changes' 3 | label: 'BREAKING CHANGES' 4 | 5 | - title: '🚀 Features' 6 | label: 'feature' 7 | 8 | - title: '🛠 Refactor' 9 | label: 'refactor' 10 | 11 | - title: '🐛 Bug Fixes' 12 | label: 'fix' 13 | 14 | - title: '💅 Style' 15 | label: 'style' 16 | 17 | - title: '🧰 Maintenance' 18 | labels: 19 | - 'chore' 20 | - 'dependencies' 21 | - 'ci' 22 | 23 | - title: '📚 Documentation' 24 | label: 'documentation' 25 | 26 | - title: '🧪 Tests' 27 | label: 'tests' 28 | 29 | - title: '🏎 Optimizations' 30 | label: 'optimizations' 31 | 32 | version-resolver: 33 | major: 34 | labels: 35 | - 'BREAKING CHANGES' 36 | minor: 37 | labels: 38 | - 'feature' 39 | - 'enhancement' 40 | - 'dependencies' 41 | patch: 42 | labels: 43 | - 'fix' 44 | - 'bugfix' 45 | - 'bug' 46 | - 'optimizations' 47 | - 'refactor' 48 | - 'style' 49 | default: patch 50 | 51 | name-template: 'v$RESOLVED_VERSION' 52 | tag-template: 'v$RESOLVED_VERSION' 53 | 54 | change-template: '- $TITLE #$NUMBER (@$AUTHOR)' 55 | template: | 56 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | name: PR Labeler 2 | 3 | on: 4 | pull_request: 5 | types: [opened] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | pr-labeler: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | pull-requests: write 16 | steps: 17 | - uses: TimonVS/pr-labeler-action@v4 18 | with: 19 | repo-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | update-release-draft: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: release-drafter/release-drafter@v5 12 | with: 13 | disable-autolabeler: true 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | get-meta: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout to tag 12 | uses: actions/checkout@v3 13 | with: 14 | ref: ${{ github.event.release.tag_name }} 15 | fetch-depth: 0 16 | 17 | - name: Parse version from tag 18 | id: version 19 | uses: release-kit/semver@v1 20 | 21 | - name: Get branch name 22 | id: branch-name 23 | run: echo "::set-output name=value::${{ steps.version.outputs.prerelease != null && 'release/${{ steps.version.outputs.prerelease }}' || 'release/latest' }}" 24 | 25 | - name: Check branch existence 26 | id: branch-exists 27 | run: echo "::set-output name=flag::$(git branch -a | egrep "remotes/origin/${{ steps.branch-name.outputs.value }}$" | wc -l)" 28 | 29 | outputs: 30 | tag: ${{ github.event.release.tag_name }} 31 | prerelease: ${{ steps.version.outputs.prerelease }} 32 | full-version: ${{ steps.version.outputs.full }} 33 | branch: ${{ steps.branch-name.outputs.value }} 34 | branch-exists: ${{ steps.branch-exists.outputs.flag == '1' }} 35 | 36 | sync-and-release: 37 | runs-on: ubuntu-latest 38 | needs: get-meta 39 | if: needs.get-meta.outputs.branch == 'release/latest' 40 | steps: 41 | - name: Setup git 42 | run: | 43 | git config --global user.name "GitHub Actions Bot" 44 | git config --global user.email "<>" 45 | 46 | - name: Checkout 47 | uses: actions/checkout@v3 48 | with: 49 | fetch-depth: 0 50 | 51 | - name: Create branch 52 | if: ${{ needs.get-meta.outputs.branch-exists == 'false' }} 53 | run: | 54 | git checkout -b ${{ needs.get-meta.outputs.branch }} 55 | git push -u origin ${{ needs.get-meta.outputs.branch }} 56 | 57 | - name: Checkout branch 58 | if: ${{ needs.get-meta.outputs.branch-exists == 'true' }} 59 | run: git checkout ${{ needs.get-meta.outputs.branch }} 60 | 61 | - name: Override branch using tag HEAD 62 | run: git reset --hard ${{ needs.get-meta.outputs.tag }} 63 | 64 | - name: Push to protected branch 65 | uses: CasperWA/push-protected@v2 66 | env: 67 | TOKEN: ${{ secrets.FULL_ACCESS_GITHUB_TOKEN }} 68 | if: ${{ env.TOKEN != '' }} 69 | with: 70 | token: ${{ secrets.FULL_ACCESS_GITHUB_TOKEN }} 71 | branch: ${{ needs.get-meta.outputs.branch }} 72 | tags: true 73 | force: true 74 | unprotect_reviews: true 75 | 76 | - name: Push to unprotected branch 77 | env: 78 | TOKEN: ${{ secrets.FULL_ACCESS_GITHUB_TOKEN }} 79 | if: ${{ env.TOKEN == '' }} 80 | run: git push --tags --force 81 | 82 | release: 83 | runs-on: ubuntu-latest 84 | needs: get-meta 85 | steps: 86 | - name: Checkout to tag 87 | uses: actions/checkout@v3 88 | with: 89 | ref: ${{ github.event.release.tag_name }} 90 | 91 | - uses: actions/setup-node@v1 92 | with: 93 | node-version: 16 94 | registry-url: https://registry.npmjs.org/ 95 | 96 | - name: Build package 97 | run: | 98 | yarn install --frozen-lockfile 99 | yarn test 100 | yarn build 101 | 102 | - name: Set version in package.json 103 | uses: reedyuk/npm-version@1.0.1 104 | with: 105 | version: ${{ needs.get-meta.outputs.full-version }} 106 | 107 | - name: Create NPM config 108 | run: npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN 109 | env: 110 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 111 | 112 | - name: Publish latest 113 | if: ${{ needs.get-meta.outputs.prerelease == '' }} 114 | run: npm publish 115 | 116 | - name: Publish RC 117 | if: ${{ needs.get-meta.outputs.prerelease != '' }} 118 | run: npm publish --tag ${{ needs.get-meta.outputs.prerelease }} -------------------------------------------------------------------------------- /.github/workflows/test-and-build.yml: -------------------------------------------------------------------------------- 1 | name: Test and Build 2 | 3 | on: 4 | pull_request: 5 | branches: ['main', 'release/alpha', 'release/beta', 'release/next'] 6 | 7 | jobs: 8 | test-and-build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 16 16 | registry-url: https://registry.npmjs.org/ 17 | 18 | - name: Test and build 19 | if: steps.cache.outputs.cache-hit != 'true' 20 | run: | 21 | yarn install --frozen-lockfile 22 | yarn test 23 | yarn build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | .next 5 | .idea 6 | .rollup.cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Evgeny Zakharov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Navigation 2 | 3 | - [Installation](#installation) 4 | - [Usage](#usage) 5 | - [Initial setup](#initial-setup) 6 | - [Main Concepts](#main-concepts) 7 | - [Factories](#factories) 8 | - [`getInitialProps`](#getinitialprops-server-and-client-side) 9 | - [`getServerSideProps`](#getserversideprops-only-server-side) 10 | - [`getStaticProps`](#getstaticprops-only-server-side) 11 | - [Advanced Page Events Usage](#advanced-page-events-usage) 12 | - [`usePageEvent`](#usepageevent-useful-on-the-client-side) 13 | - [`enhancePageEvent`](#enhancepageevent-manual-flow-control) 14 | - [Recipes](#recipes) 15 | - [Cookies](#cookies) 16 | - [Common Questions](#common-questions) 17 | - [Contrubuting](#contributing) 18 | - [Maintenance](#maintenance) 19 | - [Regular flow](#regular-flow) 20 | - [Prerelease from](#prerelease-flow) 21 | - [Conventions](#conventions) 22 | 23 | ## Installation 24 | 25 | ```sh 26 | yarn add nextjs-effector @effector/next effector-react effector 27 | ``` 28 | 29 | ## Usage 30 | 31 | ### Examples 32 | 33 | - [Test App](/apps/test-app) - used for testing the `nextjs-effector` library. You can find some architecture notes in [README](/apps/test-app/README.md). 34 | 35 | ### Initial setup 36 | 37 | First, add `effector/babel-plugin` to your `.babelrc`: 38 | 39 | ```json 40 | { 41 | "presets": ["next/babel"], 42 | "plugins": ["effector/babel-plugin"] 43 | } 44 | ``` 45 | 46 | By doing that, all our Effector units will be created with `sid` constant, which is unique and stable between the server and the client, so we can safely serialize store values for sending to the client. 47 | 48 | You can also try out [the official `@effector/swc-plugin`](https://github.com/effector/swc-plugin), which is still in early beta as the whole SWC-plugins system itself. 49 | 50 | Finally, enhance your `App`: 51 | 52 | ```tsx 53 | /* pages/_app.tsx */ 54 | 55 | import App from "next/app"; 56 | import { withEffector } from "nextjs-effector"; 57 | 58 | // Passing effector-react exports is required to access the Scope 59 | export default withEffector(App); 60 | ``` 61 | 62 | After that, the `App` will be wrapped in Effector's Scope Provider. The `withEffector` function uses `@effector/next` under the hood, which handles all of Next.js caveats and special requirements for us. This way you can focus on writing a business logic without thinking about the problems of integrating Effector into your Next.js application. 63 | 64 | ### Main Concepts 65 | 66 | There are 2 types of data in any application: 67 | 68 | - **Shared** - used on almost every page (translations, logged user info) 69 | - **Specific** - used only in the relevant pages (post content on post page, group info on group page) 70 | 71 | Usually, we want these conditions to be met: 72 | 73 | - The **shared** data is loaded once in the application lifecycle (expect for manual update) 74 | - The **specific** data is loaded on each navigation between the pages 75 | - No **shared** events boilerplate on every page 76 | 77 | Assume we have the following Effector events: 78 | 79 | ```tsx 80 | /* Needed everywhere */ 81 | export const loadAuthenticatedUser = createEvent(); 82 | export const loadTranslations = createEvent(); 83 | 84 | /* Needed only on the Post page */ 85 | export const loadPostCategories = createEvent(); 86 | export const loadPostContent = createEvent(); 87 | ``` 88 | 89 | Let's group them into **shared** and **specific**: 90 | 91 | ```tsx 92 | export const appStarted = createEvent(); 93 | export const postPageStarted = createEvent(); 94 | 95 | sample({ 96 | clock: appStarted, 97 | target: [loadAuthenticatedUser, loadTranslations], 98 | }); 99 | 100 | sample({ 101 | clock: postPageStarted, 102 | target: [loadPostCategories, loadPostContent], 103 | }); 104 | ``` 105 | 106 | We want the `appStarted` to be called once in the application lifecycle and the `postPageStarted` to be called on requesting/navigating to the Post page. `nextjs-effector` library provides a 2-level GIP factory to cover this case: 107 | 108 | ```tsx 109 | export const createGIP = createGIPFactory({ 110 | // Will be called once 111 | sharedEvents: [appStarted], 112 | }); 113 | 114 | PostPage.getInitialProps = createGIP({ 115 | // Will be called on visiting PostPage 116 | pageEvent: postPageStarted, 117 | }); 118 | ``` 119 | 120 | Also, the library provides `createGSSPFactory` for `getServerSideProps` and `createGSPFactory` for `getStaticProps`. They have almost the same API, but both of them run `sharedEvents` on each request / static page generation. 121 | 122 | ### Factories 123 | 124 | #### `getInitialProps` (server and client-side) 125 | 126 | `getInitialProps` is easier to work with and doesn't require executing the shared logic on each request, including navigation between pages. 127 | 128 | ```tsx 129 | /* 130 | * 1. Create events 131 | * GIP accepts the events with "PageContext" or "void" payload types 132 | */ 133 | export const appStarted = createEvent(); 134 | export const appStarted = createEvent(); 135 | export const pageStarted = createEvent(); 136 | export const pageStarted = createEvent(); 137 | export const pageStarted = createEvent>(); 138 | 139 | /* 140 | * 2. Create GIP factory 141 | * The place depends on your architecture 142 | */ 143 | export const createGIP = createGIPFactory({ 144 | // Will be called once: 145 | // - Server side on initial load 146 | // - Client side on navigation (only if not called yet) 147 | sharedEvents: [appStarted], 148 | 149 | // Allows specifying shared events behavior 150 | // When "false", the shared events run like pageEvent 151 | runSharedOnce: true, 152 | 153 | // Allows customizing server-side Scope creation process 154 | // By default, the library just uses fork(), like below 155 | // But you can fill the stores in scope with your values (cookies, for example) 156 | createServerScope: () => fork(), 157 | 158 | // The second argument in https://effector.dev/docs/api/effector/serialize 159 | serializeOptions: { ... }, 160 | 161 | // You can define your custom logic using the "customize" function 162 | // It's run after all events are settled but before Scope serialization 163 | // So, here you can safely call allSettled 164 | async customize({ scope, context }) { 165 | // You can also return nothing (there will be no impact on props in this case) 166 | return { /* Props */ }; 167 | }, 168 | }); 169 | 170 | /* 171 | * 3. Create GIP 172 | * Usually, it's done inside the "pages" directory 173 | */ 174 | Page.getInitialProps = createGIP({ 175 | // Will be called on each page visit: 176 | // - Server side on initial load 177 | // - Client side on navigation (even if already called) 178 | pageEvent: pageStarted, 179 | 180 | // The same as on factory level 181 | async customize({ scope, context }) { ... }, 182 | }); 183 | ``` 184 | 185 | #### `getServerSideProps` (only server-side) 186 | 187 | For everyday cases, we recommend using `getInitialProps` instead. But `getServerSideProps` may be useful in some edge cases like executing logic with heavy computations, or accessing the data available only on the server side. 188 | 189 | ```tsx 190 | /* 191 | * 1. Create events 192 | * GSSP accepts the events with "PageContext" or "void" payload types 193 | */ 194 | export const appStarted = createEvent(); 195 | export const appStarted = createEvent(); 196 | export const pageStarted = createEvent(); 197 | export const pageStarted = createEvent(); 198 | export const pageStarted = createEvent>(); 199 | 200 | /* 201 | * 2. Create GSSP factory 202 | * The place depends on your architecture 203 | */ 204 | export const createGSSP = createGSSPFactory({ 205 | // Will be called on the first request and each page navigation (always on the server side) 206 | sharedEvents: [appStarted], 207 | 208 | // The second argument in https://effector.dev/docs/api/effector/serialize 209 | serializeOptions: { ... }, 210 | 211 | // You can define your custom logic using the "customize" function 212 | // It's run after all events are settled but before Scope serialization 213 | // So, here you can safely call allSettled 214 | customize({ scope, context }) { 215 | // You can omit the "props" field (there will be no impact on props in this case) 216 | return { /* GSSP Result */ }; 217 | }, 218 | }); 219 | 220 | /* 221 | * 3. Create GSSP 222 | * Usually, it's done inside the "pages" directory 223 | */ 224 | export const getServerSideProps = createGSSP({ 225 | // Will be called on each page navigation (always on the server side) 226 | // Always called after shared events 227 | pageEvent: pageStarted, 228 | 229 | // The same as on factory level 230 | customize({ scope, context }) { ... }, 231 | }); 232 | ``` 233 | 234 | #### `getStaticProps` (only server-side) 235 | 236 | Recommended for static pages. 237 | 238 | ```tsx 239 | /* 240 | * 1. Create events 241 | */ 242 | export const appStarted = createEvent(); 243 | export const appStarted = createEvent(); 244 | export const pageStarted = createEvent(); 245 | export const pageStarted = createEvent(); 246 | export const pageStarted = createEvent>(); 247 | 248 | /* 249 | * 2. Create GSP factory 250 | * The place depends on your architecture 251 | */ 252 | export const createGSP = createGSPFactory({ 253 | // Will be called on each page generation (always on the server side) 254 | sharedEvents: [appStarted], 255 | }); 256 | 257 | /* 258 | * 3. Create GSP 259 | * Usually, it's done inside the "pages" directory 260 | */ 261 | export const getStaticProps = createGSP({ 262 | // Will be called on each page generation (always on the server side) 263 | pageEvent: pageStarted, 264 | 265 | // The second argument in https://effector.dev/docs/api/effector/serialize 266 | serializeOptions: { ... }, 267 | 268 | // You can define your custom logic using the "customize" function 269 | // It's run after all events are settled but before Scope serialization 270 | // So, here you can safely call allSettled 271 | // Important: due to complex types, this method is not available on factory level like in GIP and GSSP 272 | customize({ scope, context }) { 273 | // You can omit the "props" field (there will be no impact on props in this case) 274 | return { /* GSP Result */ }; 275 | }, 276 | }); 277 | ``` 278 | 279 | ### Advanced Page Events Usage 280 | 281 | #### `usePageEvent` (useful on the client side) 282 | 283 | Executes the provided `Event | Event` on the client side. 284 | 285 | The hook may be useful for the `getStaticProps` cases - it allows to keep Next.js optimization and request some user-specific global data at the same time. 286 | 287 | The second parameter is [options to enhance](#enhancepageevent-manual-flow-control) the event using `enhancePageEvent`. 288 | 289 | Usage: 290 | 291 | ```tsx 292 | const Page: NextPage = () => { 293 | usePageEvent(appStarted, { runOnce: true }); 294 | return ; 295 | }; 296 | 297 | export const getStaticProps: GetStaticProps = async () => { 298 | /* ... */ 299 | }; 300 | 301 | export default Page; 302 | ``` 303 | 304 | #### `enhancePageEvent` (manual flow control) 305 | 306 | Wraps your event and adds some logic to it. 307 | 308 | The enhanced event can be safely used anywhere. 309 | 310 | It doesn't cause any changes to the original event - you may use it just as before. 311 | 312 | ```tsx 313 | const enhancedEvent = enhancePageEvent(appStarted, { 314 | // Works like the "runSharedOnce" option in GIP fabric, but for the single event 315 | // This option applies to both client and server environments: 316 | // If the enhanced event was called on the server side, it won't be called on the client side 317 | runOnce: true, 318 | }); 319 | ``` 320 | 321 | #### Utility functions 322 | 323 | ```tsx 324 | /* 325 | * PageContext has the "env" field with "client" or "server" value, 326 | * so you can determine the environment where the code is executed 327 | * 328 | * The library provides useful types and type-guards for these purposes 329 | */ 330 | import { 331 | isClientPageContext, 332 | isServerPageContext, 333 | ClientPageContext, 334 | ServerPageContext 335 | } from 'nextjs-effector' 336 | 337 | const pageStartedOnClient = createEvent() 338 | const pageStartedOnServer = createEvent() 339 | 340 | sample({ 341 | source: pageStarted, 342 | filter: isClientPageContext, 343 | target: pageStartedOnClient 344 | }) 345 | 346 | sample({ 347 | source: pageStarted, 348 | filter: isServerPageContext, 349 | target: pageStartedOnServer 350 | }) 351 | 352 | sample({ 353 | source: pageStartedOnServer, 354 | fn: (context) => { 355 | // You can access "req" and "res" on the server side 356 | const { req, res } = context 357 | return req.cookie 358 | } 359 | }) 360 | 361 | /* 362 | * GSP accepts the events with the "StaticPageContext | void" payload 363 | * Unlike PageContext, the StaticPageContext doesn't include query 364 | * and some other properties 365 | */ 366 | export const appStarted = createEvent() 367 | export const pageStarted = createEvent() 368 | export const pageStarted = createEvent>() 369 | 370 | /* 371 | * Also, the library exports some utility types 372 | */ 373 | type PageEvent<...> = Event> 374 | type StaticPageEvent<...> = Event> 375 | type EmptyOrPageEvent<...> = PageEvent<...> | Event 376 | type EmptyOrStaticPageEvent<...> = StaticPageEvent<...> | Event 377 | ``` 378 | 379 | ## Recipes 380 | 381 | ### Cookies 382 | 383 | You can use `createServerScope` to set cookies before executing any logic: 384 | 385 | ```ts 386 | export const createGIP = createGIPFactory({ 387 | sharedEvents: [appStarted], 388 | createServerScope: (context) => { 389 | return fork({ 390 | values: [[$cookies, context.req?.headers.cookie ?? ""]], 391 | }); 392 | }, 393 | }); 394 | ``` 395 | 396 | Also, you can access the `req` object in effector logic by using `isServerContext` 397 | 398 | ```ts 399 | import { isServerPageContext } from "nextjs-effector"; 400 | 401 | sample({ 402 | source: appStarted, 403 | filter: isServerPageContext, 404 | fn: (context) => context.req.cookie, 405 | target: $cookies, 406 | }); 407 | ``` 408 | 409 | ## Common Questions 410 | 411 | ### Where should I call createGIPFactory / createGSSPFactory / createGSPFactory? 412 | 413 | The place depends on your architecture. But one thing is certain - **creating factories on each page is a really bad idea**. They are designed to simplify and encapsulate the repeated logic parts. 414 | 415 | For example, with [`Feature Sliced Design`](https://feature-sliced.design) you might consider creating a `layouts` layer, which can be used to create reusable page layouts and factories. 416 | 417 | ### How are multiple customize work? 418 | 419 | GIP: 420 | 421 | 1. Execute factory-level `customize` and save result 422 | 2. Execute page-level `customize` and save result 423 | 3. Merge results (page-level priority is higher) 424 | 4. Merge `nextjs-effector` props inside and return the result from GIP 425 | 426 | GSSP: 427 | 428 | 1. Execute factory-level `customize` and save result 429 | 2. It has `redirect` field? Return the result from GSSP 430 | 3. It has `notFound` field? Return the result from GSSP 431 | 4. Execute page-level `customize` and save result 432 | 5. It has `redirect` field? Return the result from GSSP 433 | 6. It has `notFound` field? Return the result from GSSP 434 | 7. Deep merge results (page-level priority is higher) 435 | 8. Merge `nextjs-effector` props inside and return the result from GSSP 436 | 437 | ### Why in GSSP are the shared events called on each request? 438 | 439 | `getServerSideProps`, unlike the `getInitialProps`, is run only on the server side. The problem is that the `pageEvent` logic may depend on globally shared data. So, we need either to run shared events on each request (as we do now), or get this globally shared data in some other way, for example by sending it back from the client in a serialized form (sounds risky and hard). 440 | 441 | Also, to check if shared events need to be executed, we should either ask it from the client or persist this data on a server. Both ways sound hard to implement. 442 | 443 | That's why `getInitialProps` is the more recommended way to bind your Effector models to the Page lifecycle. When navigating between pages, it runs on the client side, so we can easily omit the app event execution. 444 | 445 | ### I need to run sharedEvents and pageEvent in parallel. How can I do that? 446 | 447 | You can create GIP / GSSP fabric without `sharedEvents`, and define the flow manually: 448 | 449 | ```tsx 450 | const createGIP = createGIPFactory(); 451 | 452 | Page.getInitialProps = createGIP({ 453 | pageEvent: pageStarted, 454 | }); 455 | 456 | sample({ 457 | source: pageStarted, 458 | target: appStarted, 459 | }); 460 | ``` 461 | 462 | Also, you can use `enhancePageEvent` to run specific events only once in the application lifecycle. 463 | 464 | ### What do I do if there are bundling issues? 465 | 466 | Since Next.js 12 ESM imports are prioritized over CommonJS imports. While CJS-only dependencies are still supported, it is not recommended to use them, as those can lead to library doubles in the bundle and really weird bugs. 467 | 468 | You can read about it [here](https://github.com/effector/next#esm-dependencies-and-library-duplicates-in-the-bundle) 469 | 470 | In case when there is a CommonJS-only dependency, which uses Effector and you absolutely need it and the author can't fix it - you can copy the library to your project as the *last resort measure*. 471 | 472 | #### How to 473 | 474 | 1. [Download repository](https://github.com/risenforces/nextjs-effector/archive/refs/heads/release/latest.zip) 475 | 2. Copy the `src` folder contents into your project, for example into `src/nextjs-effector` 476 | 3. Create the alias using tsconfig.json: 477 | 478 | ```json 479 | { 480 | "compilerOptions": { 481 | "baseUrl": "./", 482 | "paths": { 483 | "nextjs-effector": ["./src/nextjs-effector"], 484 | "nextjs-effector/*": ["./src/nextjs-effector/*"] 485 | } 486 | } 487 | } 488 | ``` 489 | 490 | ## Contributing 491 | 492 | 1. Fork this repo 493 | 2. Use the [Regular flow](#regular-flow) 494 | 495 | Please follow [Conventions](#conventions) 496 | 497 | ## Maintenance 498 | 499 | The dev branch is `main` - any developer changes are merged in there. 500 | 501 | Also, there is a `release/latest` branch. It always contains the actual source code for release published with the `latest` tag. 502 | 503 | All changes are made using Pull Requests - the push is forbidden. PR can be merged only after successful `test-and-build` workflow checks. 504 | 505 | When PR is merged, the `release-drafter` workflow creates/updates a draft release. The changelog is built from the merged branch scope (`feat`, `fix`, etc) and PR title. When the release is ready - we publish the draft. 506 | 507 | Then, the `release` workflow handles everything: 508 | 509 | - It runs tests, builds a package, and publishes it 510 | - It synchronizes the released tag with the `release/latest` branch 511 | 512 | ### Regular flow 513 | 514 | 1. Create [feature branch](#conventions) 515 | 2. Make changes in your feature branch and [commit](#conventions) 516 | 3. Create a Pull Request from your feature branch to `main` 517 | The PR is needed to test the code before pushing it to the `release` branch 518 | 4. If your PR contains breaking changes, don't forget to put a `BREAKING CHANGES` label 519 | 5. Merge the PR in `main` 520 | 6. All done! Now you have a drafted release - just publish it when ready 521 | 522 | ### Prerelease flow 523 | 524 | 1. Assume your prerelease tag is `beta` 525 | 2. Create `release/beta` branch 526 | 3. Create [feature branch](#conventions) 527 | 4. Make changes in your feature branch and [commit](#conventions) 528 | 5. Create a Pull Request from your feature branch to `release/beta` 529 | The PR is needed to test the code before pushing to `release` branch 530 | 6. Create a GitHub release with a tag like `v1.0.0-beta`, pointing to `release/beta` branch 531 | For the next `beta` versions use semver build syntax: `v1.0.0-beta+1` 532 | 7. After that, the `release` workflow will publish your package with the `beta` tag 533 | 8. When the `beta` version is ready to become `latest` - create a Pull Request from `release/beta` to the `main` branch 534 | 9. Continue from the [Regular flow's](#regular-flow) #5 step 535 | 536 | ### Conventions 537 | 538 | **Feature branches**: 539 | 540 | - Should start with `feat/`, `fix/`, `docs/`, `refactor/`, etc., depending on the changes you want to propose (see [pr-labeler.yml](./.github/pr-labeler.yml) for a full list of scopes) 541 | 542 | **Commits**: 543 | 544 | - Should follow the [Conventional Commits specification](https://www.conventionalcommits.org) 545 | 546 | **Pull requests**: 547 | 548 | - Should have a human-readable name, for example: "Add a TODO list feature" 549 | - Should describe changes 550 | - Should have correct labels 551 | -------------------------------------------------------------------------------- /apps/test-app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": ["effector/babel-plugin"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/test-app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const { configure, presets } = require('eslint-kit') 2 | 3 | module.exports = configure({ 4 | allowDebug: process.env.NODE_ENV !== 'production', 5 | 6 | presets: [ 7 | presets.imports(), 8 | presets.node(), 9 | presets.prettier(), 10 | presets.react(), 11 | presets.nextJs(), 12 | presets.effector(), 13 | presets.typescript(), 14 | ], 15 | 16 | extend: { 17 | rules: { 18 | 'effector/no-watch': 'off', 19 | }, 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /apps/test-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/test-app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "quoteProps": "consistent" 6 | } -------------------------------------------------------------------------------- /apps/test-app/README.md: -------------------------------------------------------------------------------- 1 | ## Architecture 2 | 3 | - `src/shared/events` - exports `appStarted` for global usage 4 | 5 | - `src/layouts/factories` - exports GIP/GSP/GSSP factories. Uses `appStarted` from `src/shared/events` 6 | 7 | - `src/processes/*` - initiates application global logic. Uses `appStarted` from `src/shared/events` 8 | 9 | - `src/pages/*` - declares pages views and models. Exports `pageStarted` for using with GIP/GSP/GSSP factories 10 | 11 | - `pages/*` - declares Next.js pages. Uses GIP/GSP/GSSP factories from `src/layouts/factories` to bind `appStarted` and `pageStarted` to page 12 | -------------------------------------------------------------------------------- /apps/test-app/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /apps/test-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "yarn use-local && next dev", 7 | "build": "yarn use-local && next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "use-local": "node use-local.mjs" 11 | }, 12 | "dependencies": { 13 | "@effector/next": "^0.5.0", 14 | "@fontsource/acme": "^4.5.10", 15 | "@fontsource/fira-mono": "^4.5.10", 16 | "@types/node": "18.15.13", 17 | "@types/react": "18.0.37", 18 | "@types/react-dom": "18.0.11", 19 | "autoprefixer": "10.4.14", 20 | "axios": "^1.3.6", 21 | "clsx": "^1.2.1", 22 | "effector": "^22.8.1", 23 | "effector-react": "^22.5.1", 24 | "eslint": "^8.39.0", 25 | "eslint-config-next": "13.3.0", 26 | "fs-extra": "^11.1.1", 27 | "next": "13.3.0", 28 | "nextjs-effector": "^0.50.0-beta.0", 29 | "patronum": "^1.17.0", 30 | "postcss": "8.4.23", 31 | "react": "18.2.0", 32 | "react-dom": "18.2.0", 33 | "tailwindcss": "3.3.1", 34 | "typescript": "5.0.4", 35 | "zx": "^7.2.1" 36 | }, 37 | "resolutions": { 38 | "@effector/next": "^0.5.0", 39 | "effector": "^22.8.1", 40 | "effector-react": "^22.5.1" 41 | }, 42 | "devDependencies": { 43 | "eslint-kit": "^7.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /apps/test-app/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '@fontsource/acme' 2 | import '@fontsource/fira-mono' 3 | import '@app/shared/ui/globals.css' 4 | import '@app/processes' 5 | import App from 'next/app' 6 | import { withEffector } from 'nextjs-effector' 7 | 8 | export default withEffector(App) 9 | -------------------------------------------------------------------------------- /apps/test-app/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Head, Html, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /apps/test-app/pages/blog/[slug].tsx: -------------------------------------------------------------------------------- 1 | import { GetStaticPaths, NextPage } from 'next' 2 | import { usePageEvent } from 'nextjs-effector' 3 | import { createGSP } from '@app/layouts/factories' 4 | import { BlogPostPage, pageStarted } from '@app/pages/blog-post' 5 | import { localApi } from '@app/shared/api' 6 | import { appStarted } from '@app/shared/events' 7 | 8 | const Page: NextPage = () => { 9 | console.info('[Render] BlogPostPage') 10 | 11 | // eslint-disable-next-line effector/mandatory-scope-binding 12 | usePageEvent(appStarted, { runOnce: true }) 13 | 14 | return 15 | } 16 | 17 | export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => { 18 | const posts = await localApi.getAllPostsFx() 19 | 20 | return { 21 | paths: posts.map((post) => ({ 22 | params: { slug: post.slug }, 23 | })), 24 | fallback: false, 25 | } 26 | } 27 | 28 | export const getStaticProps = createGSP< 29 | Record, 30 | { slug: string } 31 | >({ 32 | pageEvent: pageStarted, 33 | customize: () => ({ revalidate: 60 }), 34 | }) 35 | 36 | export default Page 37 | -------------------------------------------------------------------------------- /apps/test-app/pages/blog/index.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from 'next' 2 | import { usePageEvent } from 'nextjs-effector' 3 | import { createGSP } from '@app/layouts/factories' 4 | import { BlogPage, pageStarted } from '@app/pages/blog' 5 | import { appStarted } from '@app/shared/events' 6 | 7 | const Page: NextPage = () => { 8 | console.info('[Render] BlogPage') 9 | 10 | // eslint-disable-next-line effector/mandatory-scope-binding 11 | usePageEvent(appStarted, { runOnce: true }) 12 | 13 | return 14 | } 15 | 16 | export const getStaticProps = createGSP({ 17 | pageEvent: pageStarted, 18 | }) 19 | 20 | export default Page 21 | -------------------------------------------------------------------------------- /apps/test-app/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from 'next' 2 | import { createGIP } from '@app/layouts/factories' 3 | import { HomePage, pageStarted } from '@app/pages/home' 4 | 5 | const Page: NextPage = () => { 6 | console.info('[Render] HomePage') 7 | return 8 | } 9 | 10 | Page.getInitialProps = createGIP({ 11 | pageEvent: pageStarted, 12 | }) 13 | 14 | export default Page 15 | -------------------------------------------------------------------------------- /apps/test-app/pages/profile/index.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from 'next' 2 | import NextErrorPage from 'next/error' 3 | import { createGIP } from '@app/layouts/factories' 4 | import { MyProfilePage, pageStarted } from '@app/pages/my-profile' 5 | import { $bio } from '@app/pages/my-profile/model' 6 | 7 | interface Props { 8 | notFound?: boolean 9 | } 10 | 11 | const Page: NextPage = ({ notFound }) => { 12 | console.info('[Render] ProfilePage') 13 | 14 | if (notFound) { 15 | return 16 | } 17 | 18 | return 19 | } 20 | 21 | Page.getInitialProps = createGIP({ 22 | pageEvent: pageStarted, 23 | customize({ scope, context }) { 24 | const { res } = context 25 | const notFound = scope.getState($bio) === null 26 | if (notFound && res) res.statusCode = 404 27 | return { notFound } 28 | }, 29 | }) 30 | 31 | export default Page 32 | -------------------------------------------------------------------------------- /apps/test-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/test-app/public/favicon.ico: -------------------------------------------------------------------------------- 1 |  (F  (n00 (-� ��F(  $]��]$ �������� 8����������8��������������������������#���OOO�������������������������ggg����#Y����������������������������555����Y�����kkk������������������������������� �����������������������Y�����JJJ���������kkk������Y#�������������� ������#������111�DDD�������������������8����������8 �������� $]��]$( @ ,U����U,*������������*����������������Q������������������Qr��������������������rr����������������������rO������������������������O������������������������������������������������������(����������������������������'�������888���������������������������������������������������������___������������������������������������������������������������������������SSS��������+��������hhh�������������������������������������������������������������+T���������������������������������������������������������,,,���������T����������GGG��������������������������������������������������������������������������������������������������������������������������������+++���������������������������������jjj��������������������������������������������������������������������T������������������������������������III������������T+������������hhh���������������������������������+�����������������������������,,,��������������������������GGG��������������������������'����������������������������������(�������������333�___����������������������������������������O������������������������Or����������������������rr��������������������rQ������������������Q����������������*������������*,U����U,(0` - (Lj����jK( V��������������U%��������������������&������������������������Q��������������������������R��������������������������������������������������������������������������������������������������������������������������������������������������������������������������P��������������������������������������O����������������������������������������������������������������������������������#������������������������������������������#������������������������������������������������������$$$�hhh�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�eee�PPP�����������U���������������������������������������������������������������������������������������������������sss�����������U������������eee������������������������������������������������������������������������������������������������� ���������������������������������������������������������������������������������������������HHH������������� (�������������EEE������������������������������������������������������������������������������������������(K��������������������������������������������������������������������������������������,,,��������������Lj��������������)))�����������������������������������������������������������������������������������j�������������������������������������������������������������������������������������������������������������������������������������������������������������������������iii����������������������������������eee����������������������������������������������������������������������������������������������������������������������������������������HHH������������������j�����������������EEE��������������������������������������������������������������jL����������������������������������������������������������,,,������������������K(������������������)))�������������������������������������������������������( ���������������������������������������������������������������������� ��������������������������������������������iii��������������������U�������������������eee����������������������������������������U������������������������������������HHH����������������������������������������EEE���������������������������������#����������������������������,,,��������������������#��������������������222�}}}�������������������������������������������������������������O��������������������������������������P��������������������������������������������������������������������������������������������������������������������������������������������������������������������������R��������������������������Q������������������������&��������������������%U��������������V (Kj����jL( �PNG 2 |  3 | IHDR\r�fsRGB���8eXIfMM*�i��D"8sIDATx�] �ՙn�]<QVA���h$ �N��13*�q��d�č�I���D�L2��(�(Ԙ2�ę�G ��q_@屈���xț�Џ��{o�������U�{}�O��;������9�‘d���(Dg��8 ��N�]��@ �hx�?v �N�3�=`;�6�.�&� �u�� ��6� P��н��@�àR�P�iZq�^DN���wp� � ��X�hИH g@�� 4 | :��|�5` p"@�'�ɲ�s{ �p�*�2����� dү���|(0� 5 | 0��>K� 6 | �xX�6IJ ��C|?$KE N�}ϓ|������h $ 2 � �|/�.Nz�# ���W�e� 7 | �5�� ����ܶ�� �;�y�� �g�s�h^I� �DL(�;�8�� Hjg�cH|x�1��R"�a���Ӂ�G��@��9`/`%0� H�@j �~,���K 8 | �,t).��I���D�T�O�)~��V�u$b 誛 �U%�7������_�$b 8A������J�3` 510wQ�?��vr���:�2�K�@ ��v*{%#��A�Z�咁^(��=�g\��W�����!:��,`�6��643�:@�c.Fٟ����u?�<��'������_܏vp: �8Q�� 9 | I�Ł� p{3���kHȢ�G�����c�Ѽ <�62&� 10 | ��2uC�����敭��T�3� 11 | �� ���;���d�/~m��.��X�@{�w.��d]G��{lK��Eb���(P�RuM�T�C���� �d��])��_Lm�=��=@b���K��GUk�^�U�������)1����g�T�Š��m`9�\����Q��@����Ⱆ6�:ڞ�^�w�����E�D ��� �5����F�,�� 12 | �X"�d�m�<�nB~�� @����t�t�x�� �;�f�>����I8����8��C1۪$B���e���+��jl��EZ��& ��S:�:�6�m����\G1��`���!�nl�l�Ɗ�^�Q`��@Oc�S��@e�ͷ���qb�p���S��@u p���F�D@�Г������2@#����L3�A��$H2�_h��FH #rq(��O�D�򤬈���runGOWa�b�&�SgD�3�ED�to�*Ǥ����9k��~)���,$�x�R�1�v�K ��9�D 䍁U(�w�&LE��ꩻ� S)��3�Y8x8$.i�(��K�ŀY ����a�]��� �4��ǀ c����@3�f����4�Ƣ�� �/*b������$!I�~� �7�B*-1` o �� �$��ǡD�����L�������J"���OQ��)��2@#�x4�"$e���I�8��Oi��8�"��G��8[x�t<�.��7&�m&؎R�^��tq�ؕ�.���Y�-2��d���*_��&d|j\�W�b � �G����*g����釁�F4�"I�؃�/b1q�N����Y�D ��p ���9���p�}w\��Ԥ���1 j`��O���xK=��H���A��1 �#� 13 | D:U8j���t���$b b�A||�U�Q��26%��)1 ��_ �ꢳ!~D�����+b >A��:]�E$��50��GDhR�t����ݻwR�)�� P���n$� 3���@bS�Nu�,Y�j�ʲ��:����;�����@�`�|�-[)�'OV��Ն�sFxڮ��ۥ�n}͛7�����~��ƺ�:���Q��J_��UKj8�q0x���;v4̞=[�hW=� �� �&�!e5�8hѢE��w�]�����6���_�iW}�SZ�? �/`�;vl�}��2<�h�"� ���A�܁�X,�m۶�+V�(��<�w���#F�^���;���aH�c� ��)S�*�{a���p ��c89(�^����4�&E��oÆ ��W�/��u�=�^���*?{k^�_E�����z���g��UI-���{WU* 14 | �:p�9 .tڷo(/ݺus>��3�'�^�Rg���ڞG��I_D���� ���~~���{ ���?N0�7�S��.ƍ׸�~?}/y]nA;�أ���2]�FOB2C?�_I����[�:�:�=#�OzK�-� ��ϣ�%����?j��I���P�ۯ��{N�-hU��t�:�������,���G�K�-hU���c�hP7 �� �˜�@�n?�\�-�k�.���2�:�� �`��F��=�-�V�_�G��܂V���}�0WI����F��ʭ���sM�r Z�8pJ�Q�*@OK8��� 15 | r Z� �ݖa,��w��S�W^y����.��5�at7��ݏ���Tv#�~7n ��A"�����+��W��pM��/�hK8����g��F/^������M{e��R�|�)q��7�t��?8'���K��P~���瞰�\��r ��>�ǷUk�eP��|�^x���� 16 | �/V/��v������ ���*�p�v������ʟ]J��}��k8(������ĉ�ѣGǗ�O�mڴq,X�o ���e. �^ �Qx���p�t����4^_�N�{�����y�2�s������-عsg�s���i�v��Z 8 17 | !~PJ?�c�������|�]�ܽ{��z�긓R��1pn���z�����tlp�9�f�r�v�jT殿�z�4*O�L�~����ԕ3��4�~~�r �;�m�xY�+��� ������3r �;�m�x�4���:7]Ձq L�4)U��!r �1��u�6���$� �7����8�w��̙3Ǹ|5�>?�\z��O� ��͆���,�E����3�����2���[����2Wu:E�����^p. H1cJ�t�]}��B�u��SOu�����I c�O�����%� �AZ������k����D?�5�@Q�� ���3�w�+��"��T��S��Uޥ�13��?� �5M'݋��>p��Z�j�~fj� ׈�סԐ�n�����>���i5D�[bf ��~a�'�`Xc���-�1�k����āI�������k��Q�ů|�k�M��(92�@�t�����݂X-�Lדa��N4��qܞ'$f0@� @V�nA�ܘY�L9:�|/^s��� ��)0`�j��T\w�uZ-����¨\� @�:��c�t���{�-��Rb��1%��I,Y%T���~ ��r �1����C��,�$��*ˀ���f<��0z����h�F��������| ���8Z-�CR����Tg��HRf��glY����s��-��p��'+����m�_ؒg������C�{� ����Ȫ�ϏΙ3g�-�GR|׹7`G��񥡘�0�U��_ٵZЏ�د�D�)���\>����ʗ������zN���@��~~��-��P��{rs���@�<����|.]�Ը|��m|g����_��y�W�KD1�b�M���%�s\����r �1��n�\ �ƒ�"-��` .4��~%3��I}[0A��$��=-�>BH"G�ۏ�^r��<�EBG�i�%���9�@^�~~ @�����1�� ��@�t�-[����{%@C�$�mAg���Κ5kʆх����/双O��l��ӿ��B�@.X���u�p�O��6��x�9MPn�`߷o_���^n�`t� 18 | ��(�����\r��s�A�y���ۂ�T��@h 19 | �E0l�0��;�tڵӘkƸN����Y�jU�� 20 | S#�|^㽺- |��p� N�.���ޥ`�^{�zL�6��4�ě�b��e�]&"�d�sΜ9Uޥ�U0�! ��*nP�*`���o֨v����i8G�����hh��m������ɓ�s�=�{J�U0�Ղ���wZ������������8bEz���,Y�D��![C�>}��7:k׮ �no��f�>jvR?#b��X �(��F�AT�F��i��[�{��zv��>��C���a+�[0B2�D��=��G~�( 21 | �ĺ������LO�\s�܂>"8|�`[) 22 | &Lp8�'��������4oGe�#�ۏ�lْ_\�D̀܂�2Z�l��i�9�� t�ȑ9f ޢ�-����=���Y�y��n?uQ�}Xͬ �sA�i>=��1�=R��+� + �܂��.2� �K������CƢۃ20h� �˫%53�5@�MA�%���̣������j[��9�;��_(�����0��~r���\�{�m�P����x#TT9��n?����N#��ץ&� }���) 23 | �T�VL�!���j���` �p �8@Rr�UAV�A����=��-����pLH�`@n�*Ȋ1�܂U���?}w ]�H2@�ߴi��V���[�˯%�������5�8�)Э 24 | T`��|rZbZ-�.�!da+@����ߞ�Z�gf�[0p������ I��gr �$��o%P�_rCy �V�|߽����"m�Y���-�[ l��kxA���ۯ9]�[pҤI�Ȩ�pP���k��Feِ���gHE�d�nAm"Z�$��5} ���z�8����2r�X�|� ��Sܻw��r�J�s�J�~�T�f�z{�ͫ��x�j?j��Q�E�n��js���|G�xз���󕾤�rzr�� ��`���V{���u��4448�V��ra��p���QRZ�<{�dK.F9��#~T���s.����N%*� ���Ýu�8G&����/W:*x%�{�}@� ��l���Nc#�AI�������i����*?�د�0}�g���C"Ā pۯ������4薒ҏ(b�8�_Q�Y� ���r7'��� `��� �j�6�� *��3�W�g��"��l� �ˆ1�:�Sg}%� � ��P?����1`�����Y���"��D�0b@ �������9������[t��F1���p`k�\U�`��R��A#W81 e`)R�ZM�����[ u��F0� rq.�����#^�=C"Ā9 P'�R~f��� 27 | pn�zdC"�e���?�\K����@&$b }jz�3۵�x/{ ��1 Ra�#�|��ƟUK�=&�^��TM�n�2�9�5)?s���{O'�D ��D���o[kM�oK0�x���Td�_@]b r� �G�����;����D ��D���1�gaR �`��'`0�  �> \��/���f��������ŀ����!fn�Z�|b����U�.t���ट���r�9�+�������� �b rnE�Dk�=��8�����!b R�Cl�P�E�`�܌�K�'~�@���}*�!`�@��6L� �;�� $b@ D��?#��g�F� 28 | �� V��1�v��;�Es��Q����=ɮ�4���b@ T��n��!��3q�0^�V��c��1�ܶ��[����M�=8I����1@�څ@Cu��`N�o��WJĀ�W����e��I��n��N�mீ��ܴ�_ d��(�4`E܅I�� ��"̵�1 *3�+\�E� �\M���)g r��� 29 | ���8�>��p�?vI��0�ǀ~�!b������$'�%"I����R��i�1 �0� �?S~&���r��� ��{ n�_ �����L�?��T�e��Ǝ�7�C"r��OQ~"qI���O 8�?$b � ܋r�#@�_�v�J̙��/��3�'d�/����W[����o'N� �l� �-2����@j�O~��0���2`H�@�؄��+����p OB��uO��(l�S�ԕ���9����~�c�:x/�X d�.���Ɣ�d��V�y@F$H2�����+M*�i��l8O@F$H2����2�4&r� PO��֢��€��7N�YS ����Y�1`��;�JS3n� g[�'��@W@"la`32�n?'�HB2p 30 | �hām�mu �����j@F@��V����Z!��xI���H�y�ѱ) ��>��Z!6���a�`�����dDV$9f��� pM�6�I�!LG:\LdrwPy�~�P�%��L3��7�TK��Am�mo|�6�� 3��-�hJ3��?�67 �yr���"�� ��g��4.$�1���_�[*��&���S/�dq������� C��h�3��>�6Ŷ%������\�#�RZq� � =lK|ŔX��X�WS�ej5/����$���:��v@������8�� �d��1(�z2~F�)���3��͋���l��C�������#����=�.\Lt? %�N$9b�%�:���2��u �1|-� ld�����t$b��@?���@� �F�c��ρ^�D �d�[9�ࠐz�����: 31 | H�@ ��P2v)~���@����z5��|����R�ֵ���|`#�W39؂��<�"-�0��\<�d ��u�oGLz1��Gp����e�倯d�.�j H�@j �F�3��@ c{s<��J& �@�����b���w�� �� ��n���v��< �����,M;��*p>p!0hH��{=�����x�]I� �DLh����<'��h8�@V �#��J���f�I� ��Hn����W�} �N�t[u�$��������� @� 2 �]&)� �#�3���, =%�T���k�&� I�����I��ӳ��[8 � �L�]�]t�T�g���6�-@b2U�OV��: A?�� } .i�| �xC���rv�w;��#�>�i8_b82�WP�������{'n���8�z;�Ƥy��s���@���P��o|�S�ih$3��@߹j��IEND�B`� -------------------------------------------------------------------------------- /apps/test-app/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/test-app/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/test-app/src/computed/widgets/layouts/base/data-load.ts: -------------------------------------------------------------------------------- 1 | import { createGIPFactory } from 'nextjs-effector' 2 | 3 | export const createBaseGetInitialProps = createGIPFactory() 4 | -------------------------------------------------------------------------------- /apps/test-app/src/computed/widgets/layouts/base/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | import { Header } from '@app/widgets/header' 3 | import styles from './styles.module.css' 4 | 5 | export interface Props { 6 | header?: ReactNode 7 | title: string 8 | content: ReactNode 9 | } 10 | 11 | export function BaseLayout({ header =
, title, content }: Props) { 12 | return ( 13 | <> 14 | {header} 15 |

{title}

16 |
{content}
17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /apps/test-app/src/computed/widgets/layouts/base/styles.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | @apply p-4; 3 | @apply text-3xl; 4 | } 5 | 6 | .content { 7 | @apply p-4; 8 | } -------------------------------------------------------------------------------- /apps/test-app/src/computed/widgets/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export { BaseLayout as BaseTemplate } from './base' 2 | -------------------------------------------------------------------------------- /apps/test-app/src/entities/authenticated-user/index.ts: -------------------------------------------------------------------------------- 1 | export * from './model' 2 | -------------------------------------------------------------------------------- /apps/test-app/src/entities/authenticated-user/model.ts: -------------------------------------------------------------------------------- 1 | import { attach, createEvent, restore, sample } from 'effector' 2 | import { localApi } from '@app/shared/api' 3 | 4 | const loadMeFx = attach({ effect: localApi.getMeFx }) 5 | 6 | export const loadAuthenticatedUser = createEvent() 7 | 8 | export const $authenticatedUser = restore(loadMeFx, null) 9 | 10 | sample({ 11 | clock: loadAuthenticatedUser, 12 | target: loadMeFx, 13 | }) 14 | -------------------------------------------------------------------------------- /apps/test-app/src/layouts/factories.ts: -------------------------------------------------------------------------------- 1 | import { createGIPFactory, createGSPFactory } from 'nextjs-effector' 2 | import { appStarted } from '../shared/events' 3 | 4 | export const createGIP = createGIPFactory({ 5 | sharedEvents: [appStarted], 6 | }) 7 | 8 | export const createGSP = createGSPFactory() 9 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/blog-post/index.ts: -------------------------------------------------------------------------------- 1 | export { pageStarted } from './model' 2 | export { BlogPostPage } from './page' 3 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/blog-post/model.ts: -------------------------------------------------------------------------------- 1 | import { attach, createEvent, restore, sample } from 'effector' 2 | import { StaticPageContext } from 'nextjs-effector' 3 | import { localApi } from '@app/shared/api' 4 | 5 | export const pageStarted = createEvent>() 6 | 7 | const loadPostFx = attach({ effect: localApi.getPostBySlugFx }) 8 | const loadCategoriesFx = attach({ effect: localApi.getCategoriesByIdFx }) 9 | export const $post = restore(loadPostFx, null) 10 | export const $categories = restore(loadCategoriesFx, []) 11 | 12 | sample({ 13 | source: pageStarted, 14 | fn: ({ params }) => params!.slug, 15 | target: loadPostFx, 16 | }) 17 | 18 | sample({ 19 | clock: loadPostFx.done, 20 | source: $post, 21 | filter: Boolean, 22 | fn: (post) => post.categoryIds, 23 | target: loadCategoriesFx, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/blog-post/page.tsx: -------------------------------------------------------------------------------- 1 | import { useUnit } from 'effector-react' 2 | import { BaseTemplate } from '@app/computed/widgets/layouts' 3 | import { $categories, $post } from './model' 4 | 5 | export function BlogPostPage() { 6 | const post = useUnit($post) 7 | const categories = useUnit($categories) 8 | 9 | if (!post) { 10 | return null 11 | } 12 | 13 | return ( 14 | 18 |
19 | Categories: 20 |
    21 | {categories.map((category, index) => { 22 | return ( 23 |
  • 24 | {category.name} 25 | {index + 1 !== categories.length && ','} 26 |
  • 27 | ) 28 | })} 29 |
30 |
31 |
{post.content}
32 | 33 | } 34 | /> 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/blog/index.ts: -------------------------------------------------------------------------------- 1 | export { pageStarted } from './model' 2 | export { BlogPage } from './page' 3 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/blog/model.ts: -------------------------------------------------------------------------------- 1 | import { attach, createEvent, restore, sample } from 'effector' 2 | import { localApi } from '@app/shared/api' 3 | 4 | export const pageStarted = createEvent() 5 | 6 | const loadAllPostsFx = attach({ effect: localApi.getAllPostsFx }) 7 | export const $posts = restore(loadAllPostsFx, []) 8 | 9 | sample({ 10 | clock: pageStarted, 11 | target: loadAllPostsFx, 12 | }) 13 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/blog/page.tsx: -------------------------------------------------------------------------------- 1 | import { useUnit } from 'effector-react' 2 | import Link from 'next/link' 3 | import { BaseTemplate } from '@app/computed/widgets/layouts' 4 | import { paths } from '@app/shared/routing' 5 | import { $posts } from './model' 6 | 7 | export function BlogPage() { 8 | const posts = useUnit($posts) 9 | 10 | return ( 11 | { 14 | return ( 15 | 21 | 22 |

{post.title}

23 |
24 | 25 | ) 26 | })} 27 | /> 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/home/index.tsx: -------------------------------------------------------------------------------- 1 | export { pageStarted } from './model' 2 | export * from './page' 3 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/home/model.ts: -------------------------------------------------------------------------------- 1 | import { createEvent } from 'effector' 2 | 3 | export const pageStarted = createEvent() 4 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/home/page.tsx: -------------------------------------------------------------------------------- 1 | import { useUnit } from 'effector-react' 2 | import { BaseTemplate } from '@app/computed/widgets/layouts' 3 | import { $authenticatedUser } from '@app/entities/authenticated-user' 4 | 5 | export function HomePage() { 6 | const user = useUnit($authenticatedUser) 7 | 8 | return ( 9 | User: {JSON.stringify(user, null, 2)}} 12 | /> 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/my-profile/index.tsx: -------------------------------------------------------------------------------- 1 | export { pageStarted } from './model' 2 | export * from './page' 3 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/my-profile/model.ts: -------------------------------------------------------------------------------- 1 | import { attach, createEvent, restore, sample } from 'effector' 2 | import { localApi } from '@app/shared/api' 3 | 4 | export const pageStarted = createEvent() 5 | 6 | const loadBioFx = attach({ effect: localApi.getBioFx }) 7 | 8 | export const $bio = restore(loadBioFx, null) 9 | 10 | sample({ 11 | clock: pageStarted, 12 | target: loadBioFx, 13 | }) 14 | -------------------------------------------------------------------------------- /apps/test-app/src/pages/my-profile/page.tsx: -------------------------------------------------------------------------------- 1 | import { useUnit } from 'effector-react' 2 | import { BaseTemplate } from '@app/computed/widgets/layouts' 3 | import { $authenticatedUser } from '@app/entities/authenticated-user' 4 | import { $bio } from './model' 5 | 6 | export function MyProfilePage() { 7 | const user = useUnit($authenticatedUser) 8 | const bio = useUnit($bio) 9 | 10 | return ( 11 | 15 |
User: {JSON.stringify(user, null, 2)}
16 |
Bio: {JSON.stringify(bio, null, 2)}
17 | 18 | } 19 | /> 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /apps/test-app/src/processes/auth.ts: -------------------------------------------------------------------------------- 1 | import { sample } from 'effector' 2 | import { loadAuthenticatedUser } from '../entities/authenticated-user' 3 | import { appStarted } from '../shared/events' 4 | 5 | sample({ 6 | clock: appStarted, 7 | target: loadAuthenticatedUser, 8 | }) 9 | -------------------------------------------------------------------------------- /apps/test-app/src/processes/index.ts: -------------------------------------------------------------------------------- 1 | import './auth' 2 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './local' 2 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/api/local/index.ts: -------------------------------------------------------------------------------- 1 | export * as localApi from './requests' 2 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/api/local/request.ts: -------------------------------------------------------------------------------- 1 | import { createEffect } from 'effector' 2 | 3 | export function createRequest

(fn: (params: P) => R) { 4 | return createEffect(fn) 5 | } 6 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/api/local/requests.ts: -------------------------------------------------------------------------------- 1 | import { createRequest } from './request' 2 | import { Bio, Category, Post, User } from './types' 3 | 4 | export const getMeFx = createRequest(() => ({ 5 | id: 1, 6 | username: 'risen', 7 | firstName: 'Evgeny', 8 | lastName: 'Rampage', 9 | })) 10 | 11 | export const getBioFx = createRequest(() => ({ 12 | id: 1, 13 | birthDate: new Date().toISOString(), 14 | occupation: 'No occupation, just visiting', 15 | })) 16 | 17 | const categories: Category[] = [ 18 | { 19 | id: 1, 20 | name: 'IT', 21 | postIds: [1], 22 | }, 23 | { 24 | id: 2, 25 | name: 'Society', 26 | postIds: [1, 2], 27 | }, 28 | ] 29 | 30 | const posts: Post[] = [ 31 | { 32 | id: 1, 33 | slug: 'effector-is-the-best-stm', 34 | title: 'Effector is the best State Manager library', 35 | content: 'Prove me wrong', 36 | categoryIds: [1, 2], 37 | }, 38 | { 39 | id: 2, 40 | slug: 'there-are-only-three-genders', 41 | title: 'There are only three genders', 42 | content: 'Male, female, and Apache Helicopter', 43 | categoryIds: [2], 44 | }, 45 | ] 46 | 47 | export const getAllCategoriesFx = createRequest( 48 | () => categories 49 | ) 50 | 51 | export const getCategoriesByIdFx = createRequest((ids) => 52 | categories.filter((category) => ids.includes(category.id)) 53 | ) 54 | 55 | export const getCategoryFx = createRequest( 56 | (id) => categories.find((category) => category.id === id) ?? null 57 | ) 58 | 59 | export const getAllPostsFx = createRequest(() => posts) 60 | 61 | export const getPostBySlugFx = createRequest( 62 | (slug) => posts.find((post) => post.slug === slug) ?? null 63 | ) 64 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/api/local/types.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number 3 | username: string 4 | firstName: string 5 | lastName: string 6 | } 7 | 8 | export interface Bio { 9 | id: number 10 | birthDate: string 11 | occupation: string 12 | } 13 | 14 | export interface Category { 15 | id: number 16 | name: string 17 | 18 | postIds: number[] 19 | } 20 | 21 | export interface Post { 22 | id: number 23 | slug: string 24 | title: string 25 | content: string 26 | 27 | categoryIds: number[] 28 | } 29 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/events/index.ts: -------------------------------------------------------------------------------- 1 | import { createEvent } from 'effector' 2 | 3 | export const appStarted = createEvent() 4 | 5 | appStarted.watch(() => console.info('[Event] appStarted')) 6 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/routing/index.ts: -------------------------------------------------------------------------------- 1 | export * from './paths' 2 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/routing/paths.ts: -------------------------------------------------------------------------------- 1 | export const paths = { 2 | home: () => `/`, 3 | me: () => `/profile`, 4 | profile: (id: `:id`) => `/profile/${id}`, 5 | about: () => `/about`, 6 | blog: () => `/blog`, 7 | blogPost: (slug = ':slug') => `/blog/${slug}`, 8 | } 9 | -------------------------------------------------------------------------------- /apps/test-app/src/shared/ui/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html, 6 | body, 7 | #root { 8 | height: 100%; 9 | } 10 | 11 | body { 12 | font-family: 'Acme', sans-serif; 13 | } 14 | 15 | pre { 16 | font-family: 'Fira Mono', monospace; 17 | } -------------------------------------------------------------------------------- /apps/test-app/src/widgets/header/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import { useUnit } from 'effector-react' 3 | import Link from 'next/link' 4 | import { $authenticatedUser } from '@app/entities/authenticated-user' 5 | import { paths } from '@app/shared/routing' 6 | import styles from './styles.module.css' 7 | 8 | interface Route { 9 | title: string 10 | path: string 11 | } 12 | 13 | const routes: Route[] = [ 14 | { 15 | title: 'Home', 16 | path: paths.home(), 17 | }, 18 | { 19 | title: 'My Profile', 20 | path: paths.me(), 21 | }, 22 | { 23 | title: 'Blog', 24 | path: paths.blog(), 25 | }, 26 | ] 27 | 28 | export function Header() { 29 | const user = useUnit($authenticatedUser) 30 | 31 | return ( 32 |

33 | 34 | 35 | Effector + Next.js 36 | 37 | 38 | 47 | {user && ( 48 | 49 | 50 | Welcome, {user.firstName}! 51 | 52 | 53 | )} 54 |
55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /apps/test-app/src/widgets/header/styles.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | @apply flex items-center; 3 | @apply bg-black text-white; 4 | } 5 | 6 | .logo { 7 | @apply text-xl; 8 | } 9 | 10 | .navbar { 11 | @apply m-auto; 12 | } 13 | 14 | .navlink { 15 | @apply inline-block p-4; 16 | @apply hover:text-fuchsia-400; 17 | } -------------------------------------------------------------------------------- /apps/test-app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './src/**/*.{js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | } -------------------------------------------------------------------------------- /apps/test-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "paths": { 18 | "@app/*": ["./src/*"], 19 | } 20 | }, 21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /apps/test-app/use-local.mjs: -------------------------------------------------------------------------------- 1 | import { $, within, cd } from "zx/core"; 2 | import { copy } from "fs-extra"; 3 | 4 | // build lib 5 | await within(async () => { 6 | cd("../../"); 7 | await $`yarn build`; 8 | }); 9 | 10 | // copy to node_modules 11 | await copy("../../dist", "node_modules/nextjs-effector/dist"); 12 | await copy("../../package.json", "node_modules/nextjs-effector/package.json"); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-effector", 3 | "version": "0.0.0-set-by-ci", 4 | "description": "High-level effector.js bindings for Next.js framework", 5 | "repository": "git@github.com:risenforces/nextjs-effector.git", 6 | "author": "Evgeny Zakharov ", 7 | "license": "MIT", 8 | "private": false, 9 | "type": "module", 10 | "main": "index.umd.cjs", 11 | "module": "index.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.umd.cjs" 16 | } 17 | }, 18 | "types": "./dist/index.d.ts", 19 | "files": [ 20 | "README.md", 21 | "dist", 22 | "LICENSE" 23 | ], 24 | "scripts": { 25 | "test": "vitest run", 26 | "build:code": "vite build", 27 | "build:types": "tsc", 28 | "build": "yarn build:code && yarn build:types" 29 | }, 30 | "devDependencies": { 31 | "@effector/next": "^0.3.0", 32 | "@types/react": "^18.0.37", 33 | "@vitejs/plugin-react": "^4.0.0", 34 | "effector": "^22.8.1", 35 | "effector-react": "^22.5.1", 36 | "next": "^13.3.0", 37 | "react": "^18.2.0", 38 | "react-dom": "^18.2.0", 39 | "typescript": "^5.0.4", 40 | "vite": "^4.3.1", 41 | "vitest": "^0.30.1" 42 | }, 43 | "peerDependencies": { 44 | "@effector/next": "^0.5.0", 45 | "effector": "^22.8.1", 46 | "effector-react": "^22.5.1", 47 | "next": ">=12.0.0", 48 | "react": ">=17.0.0", 49 | "react-dom": ">=17.0.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const INITIAL_STATE_KEY = '__EFFECTOR_NEXTJS_INITIAL_STATE__' 2 | -------------------------------------------------------------------------------- /src/context-env.ts: -------------------------------------------------------------------------------- 1 | import { ParsedUrlQuery } from 'querystring' 2 | import { ClientPageContext, PageContext, ServerPageContext } from './types' 3 | 4 | export function isClientPageContext< 5 | Q extends ParsedUrlQuery = ParsedUrlQuery, 6 | P extends ParsedUrlQuery = ParsedUrlQuery 7 | >(value: PageContext): value is ClientPageContext { 8 | return value.env === 'client' 9 | } 10 | 11 | export function isServerPageContext< 12 | Q extends ParsedUrlQuery = ParsedUrlQuery, 13 | P extends ParsedUrlQuery = ParsedUrlQuery 14 | >(value: PageContext): value is ServerPageContext { 15 | return value.env === 'server' 16 | } 17 | -------------------------------------------------------------------------------- /src/context-normalizers.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GetServerSidePropsContext, 3 | GetStaticPropsContext, 4 | NextPageContext, 5 | } from 'next' 6 | import { NextRouter } from 'next/router' 7 | import { ParsedUrlQuery } from 'querystring' 8 | import { env } from './env' 9 | import { PageContext, PageContextBase, StaticPageContext } from './types' 10 | 11 | function normalizeQuery(query: ParsedUrlQuery, route: string) { 12 | const onlyQuery: ParsedUrlQuery = {} 13 | const onlyParams: ParsedUrlQuery = {} 14 | 15 | for (const [name, value] of Object.entries(query)) { 16 | if (!value) continue 17 | 18 | // handle catch and optional catch 19 | if (Array.isArray(value) && route.includes(`[...${name}]`)) { 20 | onlyParams[name] = value 21 | continue 22 | } 23 | 24 | if (route.includes(`[${name}]`)) { 25 | onlyParams[name] = value 26 | continue 27 | } 28 | 29 | onlyQuery[name] = value 30 | } 31 | 32 | return { 33 | params: onlyParams, 34 | query: onlyQuery, 35 | } 36 | } 37 | 38 | function removeParamsFromQuery(query: ParsedUrlQuery, params: ParsedUrlQuery) { 39 | const filteredEntries = Object.entries(query).filter(([key]) => { 40 | const hasProperty = Object.prototype.hasOwnProperty.call(params, key) 41 | return !hasProperty 42 | }) 43 | 44 | return Object.fromEntries(filteredEntries) 45 | } 46 | 47 | function buildPathname({ req, resolvedUrl }: GetServerSidePropsContext) { 48 | const domain = req.headers.host 49 | const protocol = req.headers.referer?.split('://')?.[0] ?? 'https' 50 | return `${protocol}://` + domain + resolvedUrl 51 | } 52 | 53 | function withoutExplicitUndefined>(object: T): T { 54 | const entries = Object.entries(object).filter( 55 | ([, value]) => value !== undefined 56 | ) 57 | 58 | return Object.fromEntries(entries) as T 59 | } 60 | 61 | export const ContextNormalizers = { 62 | router: (router: NextRouter): PageContext => 63 | withoutExplicitUndefined({ 64 | env: 'client', 65 | pathname: router.pathname, 66 | asPath: router.asPath, 67 | defaultLocale: router.defaultLocale, 68 | locale: router.locale, 69 | locales: router.locales, 70 | route: router.route, 71 | ...normalizeQuery(router.query, router.route), 72 | }), 73 | getInitialProps: (context: NextPageContext): PageContext => { 74 | const base: PageContextBase = withoutExplicitUndefined({ 75 | pathname: context.pathname, 76 | asPath: context.asPath, 77 | defaultLocale: context.defaultLocale, 78 | locale: context.locale, 79 | locales: context.locales, 80 | route: context.pathname, 81 | ...normalizeQuery(context.query, context.pathname), 82 | }) 83 | 84 | if (env.isClient) { 85 | return { ...base, env: 'client' } 86 | } 87 | 88 | return Object.defineProperties(base, { 89 | env: { 90 | value: 'server', 91 | enumerable: true, 92 | }, 93 | req: { 94 | value: context.req, 95 | enumerable: false, 96 | }, 97 | res: { 98 | value: context.res, 99 | enumerable: false, 100 | }, 101 | }) as PageContext 102 | }, 103 | getServerSideProps: (context: GetServerSidePropsContext): PageContext => { 104 | const base: PageContextBase = withoutExplicitUndefined({ 105 | defaultLocale: context.defaultLocale, 106 | locale: context.locale, 107 | locales: context.locales, 108 | params: context.params ?? {}, 109 | query: removeParamsFromQuery(context.query, context.params ?? {}), 110 | pathname: buildPathname(context), 111 | asPath: context.resolvedUrl, 112 | }) 113 | 114 | return Object.defineProperties(base, { 115 | env: { 116 | value: 'server', 117 | enumerable: true, 118 | }, 119 | req: { 120 | value: context.req, 121 | enumerable: false, 122 | }, 123 | res: { 124 | value: context.res, 125 | enumerable: false, 126 | }, 127 | }) as PageContext 128 | }, 129 | getStaticProps: (context: GetStaticPropsContext): StaticPageContext => 130 | withoutExplicitUndefined({ 131 | defaultLocale: context.defaultLocale, 132 | locale: context.locale, 133 | locales: context.locales, 134 | params: context.params, 135 | preview: context.preview, 136 | previewData: context.previewData, 137 | }), 138 | } 139 | -------------------------------------------------------------------------------- /src/enhanced-events.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | import { allSettled, createEvent, createStore, fork } from "effector"; 3 | 4 | import { enhancePageEvent } from "./enhanced-events"; 5 | 6 | describe("enhancePageEvent", () => { 7 | describe("options: runOnce", () => { 8 | it('when "true", should run the event only once', async () => { 9 | const event = createEvent(); 10 | 11 | const $times = createStore(0).on(event, (count) => count + 1); 12 | 13 | const scope = fork(); 14 | 15 | const enhanced = enhancePageEvent(event, { runOnce: true }); 16 | 17 | await allSettled(enhanced, { scope }); 18 | await allSettled(enhanced, { scope }); 19 | 20 | expect(scope.getState($times)).toBe(1); 21 | }); 22 | 23 | it('when "false", should run the event the same amount of times', async () => { 24 | const event = createEvent(); 25 | 26 | const $times = createStore(0).on(event, (count) => count + 1); 27 | 28 | const scope = fork(); 29 | 30 | const enhanced = enhancePageEvent(event); 31 | 32 | await allSettled(enhanced, { scope }); 33 | await allSettled(enhanced, { scope }); 34 | 35 | expect(scope.getState($times)).toBe(2); 36 | }); 37 | }); 38 | 39 | describe("caching", () => { 40 | it("should return the same enhanced event on multiple calls", async () => { 41 | const event = createEvent(); 42 | 43 | const enhanced1 = enhancePageEvent(event); 44 | const enhanced2 = enhancePageEvent(event); 45 | expect(enhanced1).toBe(enhanced2); 46 | 47 | const enhanced3 = enhancePageEvent(event, { runOnce: true }); 48 | const enhanced4 = enhancePageEvent(event, { runOnce: true }); 49 | expect(enhanced3).not.toBe(enhanced1); 50 | expect(enhanced3).toBe(enhanced4); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/enhanced-events.ts: -------------------------------------------------------------------------------- 1 | import { createEvent, createStore, Event, sample } from "effector"; 2 | import { useUnit } from "effector-react"; 3 | import * as NextRouter from "next/router.js"; 4 | import { useEffect } from "react"; 5 | import { ContextNormalizers } from "./context-normalizers"; 6 | import { assertStrict } from "./shared"; 7 | import { EmptyOrPageEvent, PageContext, StaticPageContext } from "./types"; 8 | 9 | export interface EnhancedEventOptions { 10 | runOnce?: boolean; 11 | } 12 | 13 | const enhancedEventsCache = new Map>(); 14 | 15 | type AnyPayload = PageContext | StaticPageContext | void; 16 | 17 | export function enhancePageEvent

( 18 | event: Event

, 19 | options: EnhancedEventOptions = {} 20 | ): Event

{ 21 | const key = `${event.sid}-runOnce:${options.runOnce ?? false}`; 22 | 23 | const cached = enhancedEventsCache.get(key); 24 | if (cached) return cached; 25 | 26 | const { runOnce = false } = options; 27 | 28 | const enhancedEvent = createEvent

(); 29 | const $called = createStore(false, { sid: `${key}/called` }); 30 | $called.on(event, () => true); 31 | 32 | sample({ 33 | clock: enhancedEvent, 34 | source: $called, 35 | filter: (called) => { 36 | if (runOnce) return !called; 37 | return true; 38 | }, 39 | fn: (_, payload) => payload, 40 | target: event, 41 | }); 42 | 43 | enhancedEventsCache.set(key, enhancedEvent); 44 | return enhancedEvent; 45 | } 46 | 47 | export function usePageEvent( 48 | event: EmptyOrPageEvent, 49 | options: EnhancedEventOptions = {} 50 | ) { 51 | assertStrict(event); 52 | 53 | const useRouter = readUseRouterHook(); 54 | const router = useRouter(); 55 | 56 | // the function has a cache inside, so we can safely call it on every render 57 | const enhancedEvent = enhancePageEvent(event, options); 58 | const boundEvent = useUnit(enhancedEvent); 59 | 60 | useEffect(() => { 61 | const context = ContextNormalizers.router(router); 62 | boundEvent(context); 63 | }, [router, boundEvent]); 64 | } 65 | 66 | function readUseRouterHook(): typeof NextRouter.useRouter { 67 | /** 68 | * CJS-to-ESM interop is cringe :( 69 | * 70 | * Since the release of next@12.0.0 ES modules a prioritized over CommonJS modules, 71 | * so it is most possible that users will have ESM version of nextjs-effector in their bundle 72 | * 73 | * But there is no ESM version of next/router yet, so here we are, trying to resolve the cjs/esm interop issues. 74 | */ 75 | 76 | if (NextRouter.useRouter) { 77 | /** 78 | * Base case, usually works 79 | */ 80 | return NextRouter.useRouter; 81 | } 82 | 83 | // @ts-expect-error 84 | if (NextRouter.default?.useRouter) { 85 | /** 86 | * Weird case, but can happen sometimes with no clear reason. 87 | */ 88 | // @ts-expect-error 89 | return NextRouter.default!.useRouter as typeof NextRouter.useRouter; 90 | } 91 | 92 | throw Error( 93 | `Something went wrong with resolution of CJS next/router module: Got this '${NextRouter.useRouter}' instead of useRouter hook. 94 | Please, try to make an reproduce and provide it to 'nextjs-effector' issues 95 | ` 96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /src/env.ts: -------------------------------------------------------------------------------- 1 | interface Env { 2 | isClient: boolean 3 | isServer: boolean 4 | } 5 | 6 | export const env: Env = { 7 | isClient: typeof window !== 'undefined', 8 | isServer: typeof window === 'undefined', 9 | } 10 | -------------------------------------------------------------------------------- /src/factories/get-initial-props.ts: -------------------------------------------------------------------------------- 1 | import { allSettled, fork, Scope, serialize, Store } from 'effector' 2 | import { NextPageContext } from 'next' 3 | import { INITIAL_STATE_KEY } from '../constants' 4 | import { ContextNormalizers } from '../context-normalizers' 5 | import { enhancePageEvent } from '../enhanced-events' 6 | import { env } from '../env' 7 | import { assertStrict, isPageEvent } from '../shared' 8 | import { state } from '../state' 9 | import { AnyProps, EmptyOrPageEvent, GetInitialProps } from '../types' 10 | 11 | export interface CreateAppGIPConfig { 12 | sharedEvents?: EmptyOrPageEvent[] 13 | runSharedOnce?: boolean 14 | createServerScope?: (context: NextPageContext) => Scope | Promise 15 | customize?: CustomizeGIP 16 | serializeOptions?: Parameters[1] 17 | } 18 | 19 | export interface CustomizeGIPParams { 20 | scope: Scope 21 | context: NextPageContext 22 | } 23 | 24 | export type CustomizeGIP

= ( 25 | params: CustomizeGIPParams 26 | ) => void | Promise | P | Promise

27 | 28 | export interface CreateGIPConfig

{ 29 | pageEvent?: EmptyOrPageEvent 30 | customize?: CustomizeGIP

31 | } 32 | 33 | export function createGIPFactory({ 34 | sharedEvents = [], 35 | runSharedOnce = true, 36 | createServerScope = () => fork(), 37 | customize: factoryCustomize, 38 | serializeOptions 39 | }: CreateAppGIPConfig = {}) { 40 | /* 41 | * When "runSharedOnce" is equals to "true", 42 | * create enhanced shared events with "runOnce" 43 | */ 44 | const wrappedSharedEvents = sharedEvents.map((event) => { 45 | assertStrict(event) 46 | return enhancePageEvent(event, { runOnce: runSharedOnce }) 47 | }) 48 | 49 | return function createGIP

({ 50 | pageEvent, 51 | customize: pageCustomize, 52 | }: CreateGIPConfig

= {}): GetInitialProps

{ 53 | return async function getInitialProps(context) { 54 | /* 55 | * Determine the Effector events to run 56 | * 57 | * On server-side, use both shared and page events 58 | * 59 | * On client-side, use only page event, 60 | * as we don't want to run shared events again 61 | */ 62 | const events = [...wrappedSharedEvents, pageEvent].filter(isPageEvent) 63 | 64 | const normalizedContext = ContextNormalizers.getInitialProps(context) 65 | 66 | const scope = state.clientScope ?? await createServerScope(context) 67 | 68 | for (const event of events) { 69 | await allSettled(event, { scope, params: normalizedContext }) 70 | } 71 | 72 | /* 73 | * On client-side, save the newly created Scope inside scopeMap 74 | * We need it to access on user navigation (see code above) 75 | */ 76 | if (env.isClient) { 77 | // eslint-disable-next-line require-atomic-updates 78 | state.clientScope = scope 79 | } 80 | 81 | const factoryGipResult = await factoryCustomize?.({ scope, context }) ?? {} as P 82 | const pageGipResult = await pageCustomize?.({ scope, context }) ?? {} as P 83 | const initialProps: P = { ...factoryGipResult, ...pageGipResult } 84 | 85 | /* 86 | * Serialize after customize to include user operations 87 | */ 88 | const effectorProps = { 89 | [INITIAL_STATE_KEY]: serialize(scope, serializeOptions), 90 | } 91 | 92 | return Object.assign(initialProps, effectorProps) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/factories/get-server-side-props.ts: -------------------------------------------------------------------------------- 1 | import { allSettled, fork, Scope, serialize } from 'effector' 2 | import { 3 | GetServerSideProps, 4 | GetServerSidePropsContext, 5 | GetServerSidePropsResult, 6 | PreviewData, 7 | } from 'next' 8 | import { ParsedUrlQuery } from 'querystring' 9 | import { INITIAL_STATE_KEY } from '../constants' 10 | import { ContextNormalizers } from '../context-normalizers' 11 | import { isPageEvent } from '../shared' 12 | import { AnyProps, EmptyOrPageEvent } from '../types' 13 | 14 | export interface CreateAppGSSPConfig { 15 | sharedEvents?: EmptyOrPageEvent[] 16 | createServerScope?: (context: GetServerSidePropsContext) => Scope | Promise 17 | customize?: CustomizeGSSP 18 | serializeOptions?: Parameters[1] 19 | } 20 | 21 | export interface CustomizeGSSPParams< 22 | Q extends ParsedUrlQuery = ParsedUrlQuery, 23 | D extends PreviewData = PreviewData 24 | > { 25 | scope: Scope 26 | context: GetServerSidePropsContext 27 | } 28 | 29 | export type CustomizeGSSP< 30 | P extends AnyProps = AnyProps, 31 | Q extends ParsedUrlQuery = ParsedUrlQuery, 32 | D extends PreviewData = PreviewData 33 | > = ( 34 | params: CustomizeGSSPParams 35 | ) => void | Promise | GetServerSidePropsResult

| Promise> 36 | 37 | export interface CreateGSSPConfig< 38 | P extends AnyProps, 39 | Q extends ParsedUrlQuery, 40 | D extends PreviewData 41 | > { 42 | pageEvent?: EmptyOrPageEvent 43 | customize?: CustomizeGSSP 44 | } 45 | 46 | export function createGSSPFactory({ 47 | sharedEvents = [], 48 | createServerScope = () => fork(), 49 | customize: factoryCustomize, 50 | serializeOptions 51 | }: CreateAppGSSPConfig = {}) { 52 | return function createGSSP< 53 | P extends AnyProps = AnyProps, 54 | Q extends ParsedUrlQuery = ParsedUrlQuery, 55 | D extends PreviewData = PreviewData 56 | >({ 57 | pageEvent, 58 | customize: pageCustomize, 59 | }: CreateGSSPConfig = {}): GetServerSideProps { 60 | return async function getServerSideProps(context) { 61 | /* 62 | * In GSSP, always run both "sharedEvents" and "pageEvent" 63 | */ 64 | const events = [...sharedEvents, pageEvent].filter(isPageEvent) 65 | 66 | const normalizedContext = ContextNormalizers.getServerSideProps(context) 67 | 68 | const scope = await createServerScope(context) 69 | 70 | for (const event of events) { 71 | await allSettled(event, { scope, params: normalizedContext }) 72 | } 73 | 74 | const factoryGsspResult = await factoryCustomize?.({ scope, context }) ?? { props: {} as P } 75 | 76 | /* 77 | * Pass 404 and redirects as they are 78 | */ 79 | if ('redirect' in factoryGsspResult || 'notFound' in factoryGsspResult) { 80 | return factoryGsspResult 81 | } 82 | 83 | const pageGsspResult = await pageCustomize?.({ scope, context }) ?? { props: {} as P } 84 | 85 | /* 86 | * Pass 404 and redirects as they are 87 | */ 88 | if ('redirect' in pageGsspResult || 'notFound' in pageGsspResult) { 89 | return pageGsspResult 90 | } 91 | 92 | const gsspResult: GetServerSidePropsResult

= { 93 | props: { 94 | ...factoryGsspResult.props, 95 | ...pageGsspResult.props 96 | } 97 | } 98 | 99 | /* 100 | * Serialize after customize to include user operations 101 | */ 102 | const effectorProps = { 103 | [INITIAL_STATE_KEY]: serialize(scope, serializeOptions), 104 | } 105 | 106 | /* 107 | * Mix serialized Effector Scope values into the user props 108 | */ 109 | gsspResult.props = await gsspResult.props 110 | Object.assign(gsspResult.props, effectorProps) 111 | 112 | return gsspResult 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/factories/get-static-props.ts: -------------------------------------------------------------------------------- 1 | import { allSettled, fork, Scope, serialize } from 'effector' 2 | import { 3 | GetStaticProps, 4 | GetStaticPropsContext, 5 | GetStaticPropsResult, 6 | PreviewData 7 | } from 'next' 8 | import { ParsedUrlQuery } from 'querystring' 9 | import { INITIAL_STATE_KEY } from '../constants' 10 | import { ContextNormalizers } from '../context-normalizers' 11 | import { isStaticPageEvent } from '../shared' 12 | import { AnyProps, EmptyOrStaticPageEvent } from '../types' 13 | 14 | export interface CreateAppGSPConfig { 15 | sharedEvents?: EmptyOrStaticPageEvent[] 16 | createServerScope?: (context: GetStaticPropsContext) => Scope | Promise 17 | serializeOptions?: Parameters[1] 18 | } 19 | 20 | export interface CustomizeGSPParams< 21 | Q extends ParsedUrlQuery = ParsedUrlQuery, 22 | D extends PreviewData = PreviewData 23 | > { 24 | scope: Scope 25 | context: GetStaticPropsContext 26 | } 27 | 28 | export type LightweightGetStaticPropsResult = Pick, 'revalidate'> 29 | 30 | export type CustomizeGSP< 31 | P extends AnyProps = AnyProps, 32 | Q extends ParsedUrlQuery = ParsedUrlQuery, 33 | D extends PreviewData = PreviewData 34 | > = ( 35 | params: CustomizeGSPParams 36 | ) => LightweightGetStaticPropsResult | Promise | GetStaticPropsResult

| Promise> 37 | 38 | export interface CreateGSPConfig< 39 | P extends AnyProps, 40 | Q extends ParsedUrlQuery, 41 | D extends PreviewData 42 | > { 43 | pageEvent?: EmptyOrStaticPageEvent 44 | customize?: CustomizeGSP 45 | } 46 | 47 | export function createGSPFactory({ 48 | sharedEvents = [], 49 | createServerScope = () => fork(), 50 | serializeOptions 51 | }: CreateAppGSPConfig = {}) { 52 | return function createGSP< 53 | P extends AnyProps = AnyProps, 54 | Q extends ParsedUrlQuery = ParsedUrlQuery, 55 | D extends PreviewData = PreviewData 56 | >({ pageEvent, customize }: CreateGSPConfig = {}): GetStaticProps< 57 | P, 58 | Q, 59 | D 60 | > { 61 | return async function getStaticProps(context) { 62 | /* 63 | * In GSP, always run both "sharedEvents" and "pageEvent" 64 | */ 65 | const events = [...sharedEvents, pageEvent].filter(isStaticPageEvent) 66 | 67 | const normalizedContext = ContextNormalizers.getStaticProps(context) 68 | 69 | const scope = await createServerScope(context) 70 | 71 | for (const event of events) { 72 | await allSettled(event, { scope, params: normalizedContext }) 73 | } 74 | 75 | let gspResult: GetStaticPropsResult

= { props: {} as P } 76 | 77 | /* 78 | * Override with user's GSP result when "customize" defined 79 | */ 80 | if (customize) { 81 | const customGspResult = await customize({ scope, context }) 82 | if ('notFound' in customGspResult) gspResult = customGspResult 83 | else if ('redirect' in customGspResult) gspResult = customGspResult 84 | else gspResult = { props: {} as P, ...customGspResult } 85 | } 86 | 87 | /* 88 | * Pass 404 and redirects as they are 89 | */ 90 | if ('notFound' in gspResult || 'redirect' in gspResult) { 91 | return gspResult 92 | } 93 | 94 | /* 95 | * Serialize after customize to include user operations 96 | */ 97 | const effectorProps = { 98 | [INITIAL_STATE_KEY]: serialize(scope, serializeOptions), 99 | } 100 | 101 | /* 102 | * Mix serialized Effector Scope values into the user props 103 | */ 104 | Object.assign(gspResult.props, effectorProps) 105 | 106 | return gspResult 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/factories/index.ts: -------------------------------------------------------------------------------- 1 | export * from './get-initial-props' 2 | export * from './get-server-side-props' 3 | export * from './get-static-props' 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { ContextNormalizers } from './context-normalizers' 2 | export { isClientPageContext, isServerPageContext } from './context-env' 3 | export { enhancePageEvent, usePageEvent } from './enhanced-events' 4 | export type { 5 | CustomizeGIP, 6 | CustomizeGIPParams, 7 | CustomizeGSP, 8 | CustomizeGSPParams, 9 | CustomizeGSSP, 10 | CustomizeGSSPParams, 11 | } from './factories' 12 | export { 13 | createGIPFactory, 14 | createGSPFactory, 15 | createGSSPFactory, 16 | } from './factories' 17 | 18 | export * from './types' 19 | export { withEffector } from './with-effector' 20 | -------------------------------------------------------------------------------- /src/shared.ts: -------------------------------------------------------------------------------- 1 | import { Event } from 'effector' 2 | import { PageEvent, StaticPageEvent } from './types' 3 | 4 | export function isPageEvent(value: unknown): value is PageEvent { 5 | return Boolean(value) 6 | } 7 | 8 | export function isStaticPageEvent(value: unknown): value is StaticPageEvent { 9 | return Boolean(value) 10 | } 11 | 12 | /** 13 | * Casts an event union into a single event by removing empty event 14 | * (it's very hard to use event unions in Effector flow) 15 | */ 16 | export function assertStrict( 17 | event: Event | Event 18 | ): asserts event is Event {} 19 | -------------------------------------------------------------------------------- /src/state.ts: -------------------------------------------------------------------------------- 1 | import { Scope } from 'effector' 2 | 3 | interface State { 4 | clientScope: Scope | null 5 | } 6 | 7 | export const state: State = { 8 | clientScope: null, 9 | } 10 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage, ServerResponse } from 'http' 2 | import { Event } from 'effector' 3 | import { GetStaticPropsContext, NextPageContext, PreviewData } from 'next' 4 | import { ParsedUrlQuery } from 'querystring' 5 | 6 | export interface PageContextBase< 7 | Q extends ParsedUrlQuery = ParsedUrlQuery, 8 | P extends ParsedUrlQuery = ParsedUrlQuery 9 | > { 10 | route?: string 11 | pathname: string 12 | query: Q 13 | params: P 14 | asPath?: string 15 | locale?: string 16 | locales?: string[] 17 | defaultLocale?: string 18 | } 19 | 20 | export interface PageContextClientEnv { 21 | env: 'client' 22 | } 23 | 24 | export interface PageContextServerEnv { 25 | env: 'server' 26 | req: IncomingMessage & { 27 | cookies: { 28 | [key: string]: string 29 | } 30 | } 31 | res: ServerResponse 32 | } 33 | 34 | export type ClientPageContext< 35 | Q extends ParsedUrlQuery = ParsedUrlQuery, 36 | P extends ParsedUrlQuery = ParsedUrlQuery 37 | > = PageContextBase & PageContextClientEnv 38 | 39 | export type ServerPageContext< 40 | Q extends ParsedUrlQuery = ParsedUrlQuery, 41 | P extends ParsedUrlQuery = ParsedUrlQuery 42 | > = PageContextBase & PageContextServerEnv 43 | 44 | export type PageContext< 45 | Q extends ParsedUrlQuery = ParsedUrlQuery, 46 | P extends ParsedUrlQuery = ParsedUrlQuery 47 | > = ClientPageContext | ServerPageContext 48 | 49 | export type PageEvent< 50 | Q extends ParsedUrlQuery = ParsedUrlQuery, 51 | P extends ParsedUrlQuery = ParsedUrlQuery 52 | > = Event> 53 | 54 | export type EmptyOrPageEvent< 55 | Q extends ParsedUrlQuery = ParsedUrlQuery, 56 | P extends ParsedUrlQuery = ParsedUrlQuery 57 | > = PageEvent | Event 58 | 59 | export type StaticPageContext< 60 | P extends ParsedUrlQuery = ParsedUrlQuery, 61 | D extends PreviewData = PreviewData 62 | > = GetStaticPropsContext 63 | 64 | export type StaticPageEvent< 65 | P extends ParsedUrlQuery = ParsedUrlQuery, 66 | D extends PreviewData = PreviewData 67 | > = Event> 68 | 69 | export type EmptyOrStaticPageEvent< 70 | P extends ParsedUrlQuery = ParsedUrlQuery, 71 | D extends PreviewData = PreviewData 72 | > = StaticPageEvent | Event 73 | 74 | export interface AnyProps { 75 | [key: string]: any 76 | } 77 | 78 | export type GetInitialProps

= (context: NextPageContext) => Promise

79 | -------------------------------------------------------------------------------- /src/with-effector.ts: -------------------------------------------------------------------------------- 1 | import { EffectorNext } from "@effector/next"; 2 | import type { NextComponentType } from "next"; 3 | import type { AppContext, AppProps } from "next/app"; 4 | import React from "react"; 5 | import { INITIAL_STATE_KEY } from "./constants"; 6 | 7 | export function withEffector(App: NextComponentType) { 8 | return function EnhancedApp(props: AppProps): React.ReactNode { 9 | const { [INITIAL_STATE_KEY]: initialState, ...pageProps } = props.pageProps; 10 | 11 | return React.createElement( 12 | EffectorNext, 13 | // @ts-expect-error 14 | { values: initialState }, 15 | React.createElement( 16 | App, 17 | Object.assign({}, props, { pageProps: pageProps }) 18 | ) 19 | ); 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "esModuleInterop": true, 10 | "declaration": true, 11 | "emitDeclarationOnly": true, 12 | "outDir": "dist", 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "src" 19 | }, 20 | "include": ["src/**/*"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve, dirname } from "node:path"; 2 | import { fileURLToPath } from "node:url"; 3 | import { defineConfig } from "vite"; 4 | 5 | import pkgJson from "./package.json" assert { type: "json" }; 6 | 7 | const __dirname = dirname(fileURLToPath(import.meta.url)); 8 | 9 | const external = [...Object.keys(pkgJson.peerDependencies), 'next/router.js']; 10 | 11 | export default defineConfig({ 12 | build: { 13 | lib: { 14 | // Could also be a dictionary or array of multiple entry points 15 | entry: resolve(__dirname, "src/index.ts"), 16 | name: "NextjsEffector", 17 | // the proper extensions will be added 18 | fileName: "index", 19 | }, 20 | rollupOptions: { 21 | external, 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | import react from "@vitejs/plugin-react"; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | react({ 8 | babel: { 9 | plugins: ["effector/babel-plugin"], 10 | }, 11 | }), 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@ampproject/remapping@^2.2.0": 6 | version "2.2.1" 7 | resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" 8 | integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== 9 | dependencies: 10 | "@jridgewell/gen-mapping" "^0.3.0" 11 | "@jridgewell/trace-mapping" "^0.3.9" 12 | 13 | "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": 14 | version "7.21.4" 15 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" 16 | integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== 17 | dependencies: 18 | "@babel/highlight" "^7.18.6" 19 | 20 | "@babel/compat-data@^7.21.4": 21 | version "7.21.4" 22 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" 23 | integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== 24 | 25 | "@babel/core@^7.21.4": 26 | version "7.21.4" 27 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" 28 | integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== 29 | dependencies: 30 | "@ampproject/remapping" "^2.2.0" 31 | "@babel/code-frame" "^7.21.4" 32 | "@babel/generator" "^7.21.4" 33 | "@babel/helper-compilation-targets" "^7.21.4" 34 | "@babel/helper-module-transforms" "^7.21.2" 35 | "@babel/helpers" "^7.21.0" 36 | "@babel/parser" "^7.21.4" 37 | "@babel/template" "^7.20.7" 38 | "@babel/traverse" "^7.21.4" 39 | "@babel/types" "^7.21.4" 40 | convert-source-map "^1.7.0" 41 | debug "^4.1.0" 42 | gensync "^1.0.0-beta.2" 43 | json5 "^2.2.2" 44 | semver "^6.3.0" 45 | 46 | "@babel/generator@^7.21.4": 47 | version "7.21.4" 48 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" 49 | integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== 50 | dependencies: 51 | "@babel/types" "^7.21.4" 52 | "@jridgewell/gen-mapping" "^0.3.2" 53 | "@jridgewell/trace-mapping" "^0.3.17" 54 | jsesc "^2.5.1" 55 | 56 | "@babel/helper-compilation-targets@^7.21.4": 57 | version "7.21.4" 58 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" 59 | integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== 60 | dependencies: 61 | "@babel/compat-data" "^7.21.4" 62 | "@babel/helper-validator-option" "^7.21.0" 63 | browserslist "^4.21.3" 64 | lru-cache "^5.1.1" 65 | semver "^6.3.0" 66 | 67 | "@babel/helper-environment-visitor@^7.18.9": 68 | version "7.18.9" 69 | resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" 70 | integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== 71 | 72 | "@babel/helper-function-name@^7.21.0": 73 | version "7.21.0" 74 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" 75 | integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== 76 | dependencies: 77 | "@babel/template" "^7.20.7" 78 | "@babel/types" "^7.21.0" 79 | 80 | "@babel/helper-hoist-variables@^7.18.6": 81 | version "7.18.6" 82 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" 83 | integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== 84 | dependencies: 85 | "@babel/types" "^7.18.6" 86 | 87 | "@babel/helper-module-imports@^7.18.6": 88 | version "7.21.4" 89 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" 90 | integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== 91 | dependencies: 92 | "@babel/types" "^7.21.4" 93 | 94 | "@babel/helper-module-transforms@^7.21.2": 95 | version "7.21.2" 96 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" 97 | integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== 98 | dependencies: 99 | "@babel/helper-environment-visitor" "^7.18.9" 100 | "@babel/helper-module-imports" "^7.18.6" 101 | "@babel/helper-simple-access" "^7.20.2" 102 | "@babel/helper-split-export-declaration" "^7.18.6" 103 | "@babel/helper-validator-identifier" "^7.19.1" 104 | "@babel/template" "^7.20.7" 105 | "@babel/traverse" "^7.21.2" 106 | "@babel/types" "^7.21.2" 107 | 108 | "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2": 109 | version "7.20.2" 110 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" 111 | integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== 112 | 113 | "@babel/helper-simple-access@^7.20.2": 114 | version "7.20.2" 115 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" 116 | integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== 117 | dependencies: 118 | "@babel/types" "^7.20.2" 119 | 120 | "@babel/helper-split-export-declaration@^7.18.6": 121 | version "7.18.6" 122 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" 123 | integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== 124 | dependencies: 125 | "@babel/types" "^7.18.6" 126 | 127 | "@babel/helper-string-parser@^7.19.4": 128 | version "7.19.4" 129 | resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" 130 | integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== 131 | 132 | "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": 133 | version "7.19.1" 134 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" 135 | integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== 136 | 137 | "@babel/helper-validator-option@^7.21.0": 138 | version "7.21.0" 139 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" 140 | integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== 141 | 142 | "@babel/helpers@^7.21.0": 143 | version "7.21.0" 144 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" 145 | integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== 146 | dependencies: 147 | "@babel/template" "^7.20.7" 148 | "@babel/traverse" "^7.21.0" 149 | "@babel/types" "^7.21.0" 150 | 151 | "@babel/highlight@^7.18.6": 152 | version "7.18.6" 153 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" 154 | integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== 155 | dependencies: 156 | "@babel/helper-validator-identifier" "^7.18.6" 157 | chalk "^2.0.0" 158 | js-tokens "^4.0.0" 159 | 160 | "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": 161 | version "7.21.4" 162 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" 163 | integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== 164 | 165 | "@babel/plugin-transform-react-jsx-self@^7.21.0": 166 | version "7.21.0" 167 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" 168 | integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== 169 | dependencies: 170 | "@babel/helper-plugin-utils" "^7.20.2" 171 | 172 | "@babel/plugin-transform-react-jsx-source@^7.19.6": 173 | version "7.19.6" 174 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" 175 | integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== 176 | dependencies: 177 | "@babel/helper-plugin-utils" "^7.19.0" 178 | 179 | "@babel/template@^7.20.7": 180 | version "7.20.7" 181 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" 182 | integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== 183 | dependencies: 184 | "@babel/code-frame" "^7.18.6" 185 | "@babel/parser" "^7.20.7" 186 | "@babel/types" "^7.20.7" 187 | 188 | "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": 189 | version "7.21.4" 190 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" 191 | integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== 192 | dependencies: 193 | "@babel/code-frame" "^7.21.4" 194 | "@babel/generator" "^7.21.4" 195 | "@babel/helper-environment-visitor" "^7.18.9" 196 | "@babel/helper-function-name" "^7.21.0" 197 | "@babel/helper-hoist-variables" "^7.18.6" 198 | "@babel/helper-split-export-declaration" "^7.18.6" 199 | "@babel/parser" "^7.21.4" 200 | "@babel/types" "^7.21.4" 201 | debug "^4.1.0" 202 | globals "^11.1.0" 203 | 204 | "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4": 205 | version "7.21.4" 206 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" 207 | integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== 208 | dependencies: 209 | "@babel/helper-string-parser" "^7.19.4" 210 | "@babel/helper-validator-identifier" "^7.19.1" 211 | to-fast-properties "^2.0.0" 212 | 213 | "@effector/next@^0.3.0": 214 | version "0.3.0" 215 | resolved "https://registry.yarnpkg.com/@effector/next/-/next-0.3.0.tgz#e26434969c6d5826fd8aa55f74de990646df71e8" 216 | integrity sha512-e3WuX96SvH9AoGhN1PDraXiaFXnow/ZiNlTtOiLzMcY3TXhhBYXgDZOnS5n2BtUtz4GvC3/mTaYrmtxXUUtnFQ== 217 | 218 | "@esbuild/android-arm64@0.17.17": 219 | version "0.17.17" 220 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz#164b054d58551f8856285f386e1a8f45d9ba3a31" 221 | integrity sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg== 222 | 223 | "@esbuild/android-arm@0.17.17": 224 | version "0.17.17" 225 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.17.tgz#1b3b5a702a69b88deef342a7a80df4c894e4f065" 226 | integrity sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg== 227 | 228 | "@esbuild/android-x64@0.17.17": 229 | version "0.17.17" 230 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.17.tgz#6781527e3c4ea4de532b149d18a2167f06783e7f" 231 | integrity sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA== 232 | 233 | "@esbuild/darwin-arm64@0.17.17": 234 | version "0.17.17" 235 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.17.tgz#c5961ef4d3c1cc80dafe905cc145b5a71d2ac196" 236 | integrity sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ== 237 | 238 | "@esbuild/darwin-x64@0.17.17": 239 | version "0.17.17" 240 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.17.tgz#b81f3259cc349691f67ae30f7b333a53899b3c20" 241 | integrity sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg== 242 | 243 | "@esbuild/freebsd-arm64@0.17.17": 244 | version "0.17.17" 245 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.17.tgz#db846ad16cf916fd3acdda79b85ea867cb100e87" 246 | integrity sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA== 247 | 248 | "@esbuild/freebsd-x64@0.17.17": 249 | version "0.17.17" 250 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.17.tgz#4dd99acbaaba00949d509e7c144b1b6ef9e1815b" 251 | integrity sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw== 252 | 253 | "@esbuild/linux-arm64@0.17.17": 254 | version "0.17.17" 255 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.17.tgz#7f9274140b2bb9f4230dbbfdf5dc2761215e30f6" 256 | integrity sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw== 257 | 258 | "@esbuild/linux-arm@0.17.17": 259 | version "0.17.17" 260 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.17.tgz#5c8e44c2af056bb2147cf9ad13840220bcb8948b" 261 | integrity sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg== 262 | 263 | "@esbuild/linux-ia32@0.17.17": 264 | version "0.17.17" 265 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.17.tgz#18a6b3798658be7f46e9873fa0c8d4bec54c9212" 266 | integrity sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q== 267 | 268 | "@esbuild/linux-loong64@0.17.17": 269 | version "0.17.17" 270 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.17.tgz#a8d93514a47f7b4232716c9f02aeb630bae24c40" 271 | integrity sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw== 272 | 273 | "@esbuild/linux-mips64el@0.17.17": 274 | version "0.17.17" 275 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.17.tgz#4784efb1c3f0eac8133695fa89253d558149ee1b" 276 | integrity sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A== 277 | 278 | "@esbuild/linux-ppc64@0.17.17": 279 | version "0.17.17" 280 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.17.tgz#ef6558ec5e5dd9dc16886343e0ccdb0699d70d3c" 281 | integrity sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ== 282 | 283 | "@esbuild/linux-riscv64@0.17.17": 284 | version "0.17.17" 285 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.17.tgz#13a87fdbcb462c46809c9d16bcf79817ecf9ce6f" 286 | integrity sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA== 287 | 288 | "@esbuild/linux-s390x@0.17.17": 289 | version "0.17.17" 290 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.17.tgz#83cb16d1d3ac0dca803b3f031ba3dc13f1ec7ade" 291 | integrity sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ== 292 | 293 | "@esbuild/linux-x64@0.17.17": 294 | version "0.17.17" 295 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.17.tgz#7bc400568690b688e20a0c94b2faabdd89ae1a79" 296 | integrity sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg== 297 | 298 | "@esbuild/netbsd-x64@0.17.17": 299 | version "0.17.17" 300 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.17.tgz#1b5dcfbc4bfba80e67a11e9148de836af5b58b6c" 301 | integrity sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA== 302 | 303 | "@esbuild/openbsd-x64@0.17.17": 304 | version "0.17.17" 305 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.17.tgz#e275098902291149a5dcd012c9ea0796d6b7adff" 306 | integrity sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA== 307 | 308 | "@esbuild/sunos-x64@0.17.17": 309 | version "0.17.17" 310 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.17.tgz#10603474866f64986c0370a2d4fe5a2bb7fee4f5" 311 | integrity sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q== 312 | 313 | "@esbuild/win32-arm64@0.17.17": 314 | version "0.17.17" 315 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.17.tgz#521a6d97ee0f96b7c435930353cc4e93078f0b54" 316 | integrity sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q== 317 | 318 | "@esbuild/win32-ia32@0.17.17": 319 | version "0.17.17" 320 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.17.tgz#56f88462ebe82dad829dc2303175c0e0ccd8e38e" 321 | integrity sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ== 322 | 323 | "@esbuild/win32-x64@0.17.17": 324 | version "0.17.17" 325 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.17.tgz#2b577b976e6844106715bbe0cdc57cd1528063f9" 326 | integrity sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg== 327 | 328 | "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": 329 | version "0.3.3" 330 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" 331 | integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== 332 | dependencies: 333 | "@jridgewell/set-array" "^1.0.1" 334 | "@jridgewell/sourcemap-codec" "^1.4.10" 335 | "@jridgewell/trace-mapping" "^0.3.9" 336 | 337 | "@jridgewell/resolve-uri@3.1.0": 338 | version "3.1.0" 339 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" 340 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 341 | 342 | "@jridgewell/set-array@^1.0.1": 343 | version "1.1.2" 344 | resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" 345 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== 346 | 347 | "@jridgewell/sourcemap-codec@1.4.14": 348 | version "1.4.14" 349 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" 350 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 351 | 352 | "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13": 353 | version "1.4.15" 354 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" 355 | integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== 356 | 357 | "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": 358 | version "0.3.18" 359 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" 360 | integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== 361 | dependencies: 362 | "@jridgewell/resolve-uri" "3.1.0" 363 | "@jridgewell/sourcemap-codec" "1.4.14" 364 | 365 | "@next/env@13.3.0": 366 | version "13.3.0" 367 | resolved "https://registry.yarnpkg.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba" 368 | integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ== 369 | 370 | "@next/swc-darwin-arm64@13.3.0": 371 | version "13.3.0" 372 | resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2" 373 | integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w== 374 | 375 | "@next/swc-darwin-x64@13.3.0": 376 | version "13.3.0" 377 | resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb" 378 | integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg== 379 | 380 | "@next/swc-linux-arm64-gnu@13.3.0": 381 | version "13.3.0" 382 | resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5" 383 | integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw== 384 | 385 | "@next/swc-linux-arm64-musl@13.3.0": 386 | version "13.3.0" 387 | resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2" 388 | integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ== 389 | 390 | "@next/swc-linux-x64-gnu@13.3.0": 391 | version "13.3.0" 392 | resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086" 393 | integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA== 394 | 395 | "@next/swc-linux-x64-musl@13.3.0": 396 | version "13.3.0" 397 | resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b" 398 | integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw== 399 | 400 | "@next/swc-win32-arm64-msvc@13.3.0": 401 | version "13.3.0" 402 | resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093" 403 | integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA== 404 | 405 | "@next/swc-win32-ia32-msvc@13.3.0": 406 | version "13.3.0" 407 | resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd" 408 | integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w== 409 | 410 | "@next/swc-win32-x64-msvc@13.3.0": 411 | version "13.3.0" 412 | resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770" 413 | integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ== 414 | 415 | "@swc/helpers@0.4.14": 416 | version "0.4.14" 417 | resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" 418 | integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== 419 | dependencies: 420 | tslib "^2.4.0" 421 | 422 | "@types/chai-subset@^1.3.3": 423 | version "1.3.3" 424 | resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94" 425 | integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw== 426 | dependencies: 427 | "@types/chai" "*" 428 | 429 | "@types/chai@*", "@types/chai@^4.3.4": 430 | version "4.3.4" 431 | resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" 432 | integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== 433 | 434 | "@types/node@*": 435 | version "18.15.13" 436 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" 437 | integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== 438 | 439 | "@types/prop-types@*": 440 | version "15.7.5" 441 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" 442 | integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== 443 | 444 | "@types/react@^18.0.37": 445 | version "18.0.37" 446 | resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.37.tgz#7a784e2a8b8f83abb04dc6b9ed9c9b4c0aee9be7" 447 | integrity sha512-4yaZZtkRN3ZIQD3KSEwkfcik8s0SWV+82dlJot1AbGYHCzJkWP3ENBY6wYeDRmKZ6HkrgoGAmR2HqdwYGp6OEw== 448 | dependencies: 449 | "@types/prop-types" "*" 450 | "@types/scheduler" "*" 451 | csstype "^3.0.2" 452 | 453 | "@types/scheduler@*": 454 | version "0.16.3" 455 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" 456 | integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== 457 | 458 | "@vitejs/plugin-react@^4.0.0": 459 | version "4.0.0" 460 | resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz#46d1c37c507447d10467be1c111595174555ef28" 461 | integrity sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ== 462 | dependencies: 463 | "@babel/core" "^7.21.4" 464 | "@babel/plugin-transform-react-jsx-self" "^7.21.0" 465 | "@babel/plugin-transform-react-jsx-source" "^7.19.6" 466 | react-refresh "^0.14.0" 467 | 468 | "@vitest/expect@0.30.1": 469 | version "0.30.1" 470 | resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.30.1.tgz#3c92a3fc23a198315ce8cd16689dc2d5aeac40b8" 471 | integrity sha512-c3kbEtN8XXJSeN81iDGq29bUzSjQhjES2WR3aColsS4lPGbivwLtas4DNUe0jD9gg/FYGIteqOenfU95EFituw== 472 | dependencies: 473 | "@vitest/spy" "0.30.1" 474 | "@vitest/utils" "0.30.1" 475 | chai "^4.3.7" 476 | 477 | "@vitest/runner@0.30.1": 478 | version "0.30.1" 479 | resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.30.1.tgz#534db590091e5d40682f47b9478f64b776073c50" 480 | integrity sha512-W62kT/8i0TF1UBCNMRtRMOBWJKRnNyv9RrjIgdUryEe0wNpGZvvwPDLuzYdxvgSckzjp54DSpv1xUbv4BQ0qVA== 481 | dependencies: 482 | "@vitest/utils" "0.30.1" 483 | concordance "^5.0.4" 484 | p-limit "^4.0.0" 485 | pathe "^1.1.0" 486 | 487 | "@vitest/snapshot@0.30.1": 488 | version "0.30.1" 489 | resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.30.1.tgz#25e912557b357ecb89d5ee35e8d7c4c7a5ecfe32" 490 | integrity sha512-fJZqKrE99zo27uoZA/azgWyWbFvM1rw2APS05yB0JaLwUIg9aUtvvnBf4q7JWhEcAHmSwbrxKFgyBUga6tq9Tw== 491 | dependencies: 492 | magic-string "^0.30.0" 493 | pathe "^1.1.0" 494 | pretty-format "^27.5.1" 495 | 496 | "@vitest/spy@0.30.1": 497 | version "0.30.1" 498 | resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.30.1.tgz#e3344d4513407afd922963737fb9733a7787a2bf" 499 | integrity sha512-YfJeIf37GvTZe04ZKxzJfnNNuNSmTEGnla2OdL60C8od16f3zOfv9q9K0nNii0NfjDJRt/CVN/POuY5/zTS+BA== 500 | dependencies: 501 | tinyspy "^2.1.0" 502 | 503 | "@vitest/utils@0.30.1": 504 | version "0.30.1" 505 | resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.30.1.tgz#0e5bf8c1b81a6dfa2b70120c2aa092a651440cda" 506 | integrity sha512-/c8Xv2zUVc+rnNt84QF0Y0zkfxnaGhp87K2dYJMLtLOIckPzuxLVzAtFCicGFdB4NeBHNzTRr1tNn7rCtQcWFA== 507 | dependencies: 508 | concordance "^5.0.4" 509 | loupe "^2.3.6" 510 | pretty-format "^27.5.1" 511 | 512 | acorn-walk@^8.2.0: 513 | version "8.2.0" 514 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" 515 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== 516 | 517 | acorn@^8.8.2: 518 | version "8.8.2" 519 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" 520 | integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== 521 | 522 | ansi-regex@^5.0.1: 523 | version "5.0.1" 524 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 525 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 526 | 527 | ansi-styles@^3.2.1: 528 | version "3.2.1" 529 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 530 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 531 | dependencies: 532 | color-convert "^1.9.0" 533 | 534 | ansi-styles@^5.0.0: 535 | version "5.2.0" 536 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" 537 | integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== 538 | 539 | assertion-error@^1.1.0: 540 | version "1.1.0" 541 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 542 | integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== 543 | 544 | blueimp-md5@^2.10.0: 545 | version "2.19.0" 546 | resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" 547 | integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== 548 | 549 | browserslist@^4.21.3: 550 | version "4.21.5" 551 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" 552 | integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== 553 | dependencies: 554 | caniuse-lite "^1.0.30001449" 555 | electron-to-chromium "^1.4.284" 556 | node-releases "^2.0.8" 557 | update-browserslist-db "^1.0.10" 558 | 559 | busboy@1.6.0: 560 | version "1.6.0" 561 | resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" 562 | integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== 563 | dependencies: 564 | streamsearch "^1.1.0" 565 | 566 | cac@^6.7.14: 567 | version "6.7.14" 568 | resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" 569 | integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== 570 | 571 | caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449: 572 | version "1.0.30001481" 573 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz#f58a717afe92f9e69d0e35ff64df596bfad93912" 574 | integrity sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ== 575 | 576 | chai@^4.3.7: 577 | version "4.3.7" 578 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" 579 | integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== 580 | dependencies: 581 | assertion-error "^1.1.0" 582 | check-error "^1.0.2" 583 | deep-eql "^4.1.2" 584 | get-func-name "^2.0.0" 585 | loupe "^2.3.1" 586 | pathval "^1.1.1" 587 | type-detect "^4.0.5" 588 | 589 | chalk@^2.0.0: 590 | version "2.4.2" 591 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 592 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 593 | dependencies: 594 | ansi-styles "^3.2.1" 595 | escape-string-regexp "^1.0.5" 596 | supports-color "^5.3.0" 597 | 598 | check-error@^1.0.2: 599 | version "1.0.2" 600 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 601 | integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== 602 | 603 | client-only@0.0.1: 604 | version "0.0.1" 605 | resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" 606 | integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== 607 | 608 | color-convert@^1.9.0: 609 | version "1.9.3" 610 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 611 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 612 | dependencies: 613 | color-name "1.1.3" 614 | 615 | color-name@1.1.3: 616 | version "1.1.3" 617 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 618 | integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== 619 | 620 | concordance@^5.0.4: 621 | version "5.0.4" 622 | resolved "https://registry.yarnpkg.com/concordance/-/concordance-5.0.4.tgz#9896073261adced72f88d60e4d56f8efc4bbbbd2" 623 | integrity sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw== 624 | dependencies: 625 | date-time "^3.1.0" 626 | esutils "^2.0.3" 627 | fast-diff "^1.2.0" 628 | js-string-escape "^1.0.1" 629 | lodash "^4.17.15" 630 | md5-hex "^3.0.1" 631 | semver "^7.3.2" 632 | well-known-symbols "^2.0.0" 633 | 634 | convert-source-map@^1.7.0: 635 | version "1.9.0" 636 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" 637 | integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== 638 | 639 | csstype@^3.0.2: 640 | version "3.1.2" 641 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" 642 | integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== 643 | 644 | date-time@^3.1.0: 645 | version "3.1.0" 646 | resolved "https://registry.yarnpkg.com/date-time/-/date-time-3.1.0.tgz#0d1e934d170579f481ed8df1e2b8ff70ee845e1e" 647 | integrity sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg== 648 | dependencies: 649 | time-zone "^1.0.0" 650 | 651 | debug@^4.1.0, debug@^4.3.4: 652 | version "4.3.4" 653 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 654 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 655 | dependencies: 656 | ms "2.1.2" 657 | 658 | deep-eql@^4.1.2: 659 | version "4.1.3" 660 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" 661 | integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== 662 | dependencies: 663 | type-detect "^4.0.0" 664 | 665 | effector-react@^22.5.1: 666 | version "22.5.1" 667 | resolved "https://registry.yarnpkg.com/effector-react/-/effector-react-22.5.1.tgz#09e7ef9a9328edf1ff4f4fcd50b0f4ff98980a52" 668 | integrity sha512-Sy5b/sUEZoCuyoCr+SiHcgd0L+PQqnIGx+SuPCZ7MhECYxezkkeecsk0EVj9sykLL2qlEeg+rdiWvIATJgOTeA== 669 | dependencies: 670 | use-sync-external-store "^1.0.0" 671 | 672 | effector@^22.8.1: 673 | version "22.8.1" 674 | resolved "https://registry.yarnpkg.com/effector/-/effector-22.8.1.tgz#a1bac860262e8d4301d9462d5000973810319def" 675 | integrity sha512-368ysZBxxqr0ThVYElhmo8p8rT2wTZ0hF/BIg1FxNPSIdr/wvw4Hbnioy22CbgtU5d53CnadkJnSlKqfOICOwg== 676 | 677 | electron-to-chromium@^1.4.284: 678 | version "1.4.368" 679 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" 680 | integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== 681 | 682 | esbuild@^0.17.5: 683 | version "0.17.17" 684 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.17.tgz#fa906ab11b11d2ed4700f494f4f764229b25c916" 685 | integrity sha512-/jUywtAymR8jR4qsa2RujlAF7Krpt5VWi72Q2yuLD4e/hvtNcFQ0I1j8m/bxq238pf3/0KO5yuXNpuLx8BE1KA== 686 | optionalDependencies: 687 | "@esbuild/android-arm" "0.17.17" 688 | "@esbuild/android-arm64" "0.17.17" 689 | "@esbuild/android-x64" "0.17.17" 690 | "@esbuild/darwin-arm64" "0.17.17" 691 | "@esbuild/darwin-x64" "0.17.17" 692 | "@esbuild/freebsd-arm64" "0.17.17" 693 | "@esbuild/freebsd-x64" "0.17.17" 694 | "@esbuild/linux-arm" "0.17.17" 695 | "@esbuild/linux-arm64" "0.17.17" 696 | "@esbuild/linux-ia32" "0.17.17" 697 | "@esbuild/linux-loong64" "0.17.17" 698 | "@esbuild/linux-mips64el" "0.17.17" 699 | "@esbuild/linux-ppc64" "0.17.17" 700 | "@esbuild/linux-riscv64" "0.17.17" 701 | "@esbuild/linux-s390x" "0.17.17" 702 | "@esbuild/linux-x64" "0.17.17" 703 | "@esbuild/netbsd-x64" "0.17.17" 704 | "@esbuild/openbsd-x64" "0.17.17" 705 | "@esbuild/sunos-x64" "0.17.17" 706 | "@esbuild/win32-arm64" "0.17.17" 707 | "@esbuild/win32-ia32" "0.17.17" 708 | "@esbuild/win32-x64" "0.17.17" 709 | 710 | escalade@^3.1.1: 711 | version "3.1.1" 712 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 713 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 714 | 715 | escape-string-regexp@^1.0.5: 716 | version "1.0.5" 717 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 718 | integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== 719 | 720 | esutils@^2.0.3: 721 | version "2.0.3" 722 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 723 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 724 | 725 | fast-diff@^1.2.0: 726 | version "1.2.0" 727 | resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" 728 | integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== 729 | 730 | fsevents@~2.3.2: 731 | version "2.3.2" 732 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 733 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 734 | 735 | gensync@^1.0.0-beta.2: 736 | version "1.0.0-beta.2" 737 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" 738 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== 739 | 740 | get-func-name@^2.0.0: 741 | version "2.0.0" 742 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 743 | integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== 744 | 745 | globals@^11.1.0: 746 | version "11.12.0" 747 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 748 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 749 | 750 | has-flag@^3.0.0: 751 | version "3.0.0" 752 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 753 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== 754 | 755 | js-string-escape@^1.0.1: 756 | version "1.0.1" 757 | resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" 758 | integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== 759 | 760 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: 761 | version "4.0.0" 762 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 763 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 764 | 765 | jsesc@^2.5.1: 766 | version "2.5.2" 767 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" 768 | integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== 769 | 770 | json5@^2.2.2: 771 | version "2.2.3" 772 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" 773 | integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== 774 | 775 | jsonc-parser@^3.2.0: 776 | version "3.2.0" 777 | resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" 778 | integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== 779 | 780 | local-pkg@^0.4.3: 781 | version "0.4.3" 782 | resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" 783 | integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== 784 | 785 | lodash@^4.17.15: 786 | version "4.17.21" 787 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 788 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 789 | 790 | loose-envify@^1.1.0: 791 | version "1.4.0" 792 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 793 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 794 | dependencies: 795 | js-tokens "^3.0.0 || ^4.0.0" 796 | 797 | loupe@^2.3.1, loupe@^2.3.6: 798 | version "2.3.6" 799 | resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" 800 | integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== 801 | dependencies: 802 | get-func-name "^2.0.0" 803 | 804 | lru-cache@^5.1.1: 805 | version "5.1.1" 806 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" 807 | integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== 808 | dependencies: 809 | yallist "^3.0.2" 810 | 811 | lru-cache@^6.0.0: 812 | version "6.0.0" 813 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 814 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 815 | dependencies: 816 | yallist "^4.0.0" 817 | 818 | magic-string@^0.30.0: 819 | version "0.30.0" 820 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.0.tgz#fd58a4748c5c4547338a424e90fa5dd17f4de529" 821 | integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ== 822 | dependencies: 823 | "@jridgewell/sourcemap-codec" "^1.4.13" 824 | 825 | md5-hex@^3.0.1: 826 | version "3.0.1" 827 | resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c" 828 | integrity sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== 829 | dependencies: 830 | blueimp-md5 "^2.10.0" 831 | 832 | mlly@^1.1.1, mlly@^1.2.0: 833 | version "1.2.0" 834 | resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.0.tgz#f0f6c2fc8d2d12ea6907cd869066689b5031b613" 835 | integrity sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww== 836 | dependencies: 837 | acorn "^8.8.2" 838 | pathe "^1.1.0" 839 | pkg-types "^1.0.2" 840 | ufo "^1.1.1" 841 | 842 | ms@2.1.2: 843 | version "2.1.2" 844 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 845 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 846 | 847 | nanoid@^3.3.4, nanoid@^3.3.6: 848 | version "3.3.6" 849 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" 850 | integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== 851 | 852 | next@^13.3.0: 853 | version "13.3.0" 854 | resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" 855 | integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA== 856 | dependencies: 857 | "@next/env" "13.3.0" 858 | "@swc/helpers" "0.4.14" 859 | busboy "1.6.0" 860 | caniuse-lite "^1.0.30001406" 861 | postcss "8.4.14" 862 | styled-jsx "5.1.1" 863 | optionalDependencies: 864 | "@next/swc-darwin-arm64" "13.3.0" 865 | "@next/swc-darwin-x64" "13.3.0" 866 | "@next/swc-linux-arm64-gnu" "13.3.0" 867 | "@next/swc-linux-arm64-musl" "13.3.0" 868 | "@next/swc-linux-x64-gnu" "13.3.0" 869 | "@next/swc-linux-x64-musl" "13.3.0" 870 | "@next/swc-win32-arm64-msvc" "13.3.0" 871 | "@next/swc-win32-ia32-msvc" "13.3.0" 872 | "@next/swc-win32-x64-msvc" "13.3.0" 873 | 874 | node-releases@^2.0.8: 875 | version "2.0.10" 876 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" 877 | integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== 878 | 879 | p-limit@^4.0.0: 880 | version "4.0.0" 881 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" 882 | integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== 883 | dependencies: 884 | yocto-queue "^1.0.0" 885 | 886 | pathe@^1.1.0: 887 | version "1.1.0" 888 | resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03" 889 | integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w== 890 | 891 | pathval@^1.1.1: 892 | version "1.1.1" 893 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" 894 | integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== 895 | 896 | picocolors@^1.0.0: 897 | version "1.0.0" 898 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 899 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 900 | 901 | pkg-types@^1.0.2: 902 | version "1.0.2" 903 | resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.2.tgz#c233efc5210a781e160e0cafd60c0d0510a4b12e" 904 | integrity sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ== 905 | dependencies: 906 | jsonc-parser "^3.2.0" 907 | mlly "^1.1.1" 908 | pathe "^1.1.0" 909 | 910 | postcss@8.4.14: 911 | version "8.4.14" 912 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" 913 | integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== 914 | dependencies: 915 | nanoid "^3.3.4" 916 | picocolors "^1.0.0" 917 | source-map-js "^1.0.2" 918 | 919 | postcss@^8.4.21: 920 | version "8.4.23" 921 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" 922 | integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== 923 | dependencies: 924 | nanoid "^3.3.6" 925 | picocolors "^1.0.0" 926 | source-map-js "^1.0.2" 927 | 928 | pretty-format@^27.5.1: 929 | version "27.5.1" 930 | resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" 931 | integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== 932 | dependencies: 933 | ansi-regex "^5.0.1" 934 | ansi-styles "^5.0.0" 935 | react-is "^17.0.1" 936 | 937 | react-dom@^18.2.0: 938 | version "18.2.0" 939 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" 940 | integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== 941 | dependencies: 942 | loose-envify "^1.1.0" 943 | scheduler "^0.23.0" 944 | 945 | react-is@^17.0.1: 946 | version "17.0.2" 947 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" 948 | integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== 949 | 950 | react-refresh@^0.14.0: 951 | version "0.14.0" 952 | resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" 953 | integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== 954 | 955 | react@^18.2.0: 956 | version "18.2.0" 957 | resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" 958 | integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== 959 | dependencies: 960 | loose-envify "^1.1.0" 961 | 962 | rollup@^3.20.2: 963 | version "3.20.7" 964 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.20.7.tgz#4f045dfb388abe08dd159f8cd286dcaca1e80b28" 965 | integrity sha512-P7E2zezKSLhWnTz46XxjSmInrbOCiul1yf+kJccMxT56vxjHwCbDfoLbiqFgu+WQoo9ij2PkraYaBstgB2prBA== 966 | optionalDependencies: 967 | fsevents "~2.3.2" 968 | 969 | scheduler@^0.23.0: 970 | version "0.23.0" 971 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" 972 | integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== 973 | dependencies: 974 | loose-envify "^1.1.0" 975 | 976 | semver@^6.3.0: 977 | version "6.3.0" 978 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 979 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 980 | 981 | semver@^7.3.2: 982 | version "7.5.0" 983 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" 984 | integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== 985 | dependencies: 986 | lru-cache "^6.0.0" 987 | 988 | siginfo@^2.0.0: 989 | version "2.0.0" 990 | resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" 991 | integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== 992 | 993 | source-map-js@^1.0.2: 994 | version "1.0.2" 995 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 996 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 997 | 998 | source-map@^0.6.1: 999 | version "0.6.1" 1000 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 1001 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1002 | 1003 | stackback@0.0.2: 1004 | version "0.0.2" 1005 | resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" 1006 | integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== 1007 | 1008 | std-env@^3.3.2: 1009 | version "3.3.2" 1010 | resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.2.tgz#af27343b001616015534292178327b202b9ee955" 1011 | integrity sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA== 1012 | 1013 | streamsearch@^1.1.0: 1014 | version "1.1.0" 1015 | resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" 1016 | integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== 1017 | 1018 | strip-literal@^1.0.1: 1019 | version "1.0.1" 1020 | resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.0.1.tgz#0115a332710c849b4e46497891fb8d585e404bd2" 1021 | integrity sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q== 1022 | dependencies: 1023 | acorn "^8.8.2" 1024 | 1025 | styled-jsx@5.1.1: 1026 | version "5.1.1" 1027 | resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" 1028 | integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== 1029 | dependencies: 1030 | client-only "0.0.1" 1031 | 1032 | supports-color@^5.3.0: 1033 | version "5.5.0" 1034 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1035 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1036 | dependencies: 1037 | has-flag "^3.0.0" 1038 | 1039 | time-zone@^1.0.0: 1040 | version "1.0.0" 1041 | resolved "https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d" 1042 | integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== 1043 | 1044 | tinybench@^2.4.0: 1045 | version "2.4.0" 1046 | resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.4.0.tgz#83f60d9e5545353610fe7993bd783120bc20c7a7" 1047 | integrity sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg== 1048 | 1049 | tinypool@^0.4.0: 1050 | version "0.4.0" 1051 | resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.4.0.tgz#3cf3ebd066717f9f837e8d7d31af3c127fdb5446" 1052 | integrity sha512-2ksntHOKf893wSAH4z/+JbPpi92esw8Gn9N2deXX+B0EO92hexAVI9GIZZPx7P5aYo5KULfeOSt3kMOmSOy6uA== 1053 | 1054 | tinyspy@^2.1.0: 1055 | version "2.1.0" 1056 | resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.1.0.tgz#bd6875098f988728e6456cfd5ab8cc06498ecdeb" 1057 | integrity sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ== 1058 | 1059 | to-fast-properties@^2.0.0: 1060 | version "2.0.0" 1061 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" 1062 | integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== 1063 | 1064 | tslib@^2.4.0: 1065 | version "2.5.0" 1066 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" 1067 | integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== 1068 | 1069 | type-detect@^4.0.0, type-detect@^4.0.5: 1070 | version "4.0.8" 1071 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 1072 | integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== 1073 | 1074 | typescript@^5.0.4: 1075 | version "5.0.4" 1076 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" 1077 | integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== 1078 | 1079 | ufo@^1.1.1: 1080 | version "1.1.1" 1081 | resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.1.tgz#e70265e7152f3aba425bd013d150b2cdf4056d7c" 1082 | integrity sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg== 1083 | 1084 | update-browserslist-db@^1.0.10: 1085 | version "1.0.11" 1086 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" 1087 | integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== 1088 | dependencies: 1089 | escalade "^3.1.1" 1090 | picocolors "^1.0.0" 1091 | 1092 | use-sync-external-store@^1.0.0: 1093 | version "1.2.0" 1094 | resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" 1095 | integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== 1096 | 1097 | vite-node@0.30.1: 1098 | version "0.30.1" 1099 | resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.30.1.tgz#ab0ed1553019c7d81ac95529c57ab8ac9e82347d" 1100 | integrity sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg== 1101 | dependencies: 1102 | cac "^6.7.14" 1103 | debug "^4.3.4" 1104 | mlly "^1.2.0" 1105 | pathe "^1.1.0" 1106 | picocolors "^1.0.0" 1107 | vite "^3.0.0 || ^4.0.0" 1108 | 1109 | "vite@^3.0.0 || ^4.0.0", vite@^4.3.1: 1110 | version "4.3.1" 1111 | resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.1.tgz#9badb1377f995632cdcf05f32103414db6fbb95a" 1112 | integrity sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg== 1113 | dependencies: 1114 | esbuild "^0.17.5" 1115 | postcss "^8.4.21" 1116 | rollup "^3.20.2" 1117 | optionalDependencies: 1118 | fsevents "~2.3.2" 1119 | 1120 | vitest@^0.30.1: 1121 | version "0.30.1" 1122 | resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.30.1.tgz#351d4a2f27aa8cc0245e3583e3ed45e30efc71d6" 1123 | integrity sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA== 1124 | dependencies: 1125 | "@types/chai" "^4.3.4" 1126 | "@types/chai-subset" "^1.3.3" 1127 | "@types/node" "*" 1128 | "@vitest/expect" "0.30.1" 1129 | "@vitest/runner" "0.30.1" 1130 | "@vitest/snapshot" "0.30.1" 1131 | "@vitest/spy" "0.30.1" 1132 | "@vitest/utils" "0.30.1" 1133 | acorn "^8.8.2" 1134 | acorn-walk "^8.2.0" 1135 | cac "^6.7.14" 1136 | chai "^4.3.7" 1137 | concordance "^5.0.4" 1138 | debug "^4.3.4" 1139 | local-pkg "^0.4.3" 1140 | magic-string "^0.30.0" 1141 | pathe "^1.1.0" 1142 | picocolors "^1.0.0" 1143 | source-map "^0.6.1" 1144 | std-env "^3.3.2" 1145 | strip-literal "^1.0.1" 1146 | tinybench "^2.4.0" 1147 | tinypool "^0.4.0" 1148 | vite "^3.0.0 || ^4.0.0" 1149 | vite-node "0.30.1" 1150 | why-is-node-running "^2.2.2" 1151 | 1152 | well-known-symbols@^2.0.0: 1153 | version "2.0.0" 1154 | resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" 1155 | integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== 1156 | 1157 | why-is-node-running@^2.2.2: 1158 | version "2.2.2" 1159 | resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" 1160 | integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== 1161 | dependencies: 1162 | siginfo "^2.0.0" 1163 | stackback "0.0.2" 1164 | 1165 | yallist@^3.0.2: 1166 | version "3.1.1" 1167 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 1168 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== 1169 | 1170 | yallist@^4.0.0: 1171 | version "4.0.0" 1172 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1173 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1174 | 1175 | yocto-queue@^1.0.0: 1176 | version "1.0.0" 1177 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" 1178 | integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== 1179 | --------------------------------------------------------------------------------